Slightly improved diff performance

This commit is contained in:
Eduard Urbach 2017-11-10 08:41:45 +01:00
parent d8367b6172
commit 44369cb916
5 changed files with 47 additions and 35 deletions

View File

@ -1,5 +1,4 @@
.anime-list-container
vertical
width 100%
max-width 800px
margin 0 auto

View File

@ -9,8 +9,11 @@ export function load(arn: AnimeNotifier, element: HTMLElement) {
// Diff
export function diff(arn: AnimeNotifier, element: HTMLElement) {
let url = element.dataset.url || (element as HTMLAnchorElement).getAttribute("href")
arn.diff(url)
.then(() => arn.scrollTo(element))
.then(() => {
// Avoid instant layout thrashing
arn.requestIdleCallback(() => arn.scrollTo(element))
})
.catch(console.error)
}

View File

@ -294,6 +294,10 @@ export class AnimeNotifier {
}
countUp() {
if(!this.app.currentPath.includes("/paypal/success")) {
return
}
for(let element of findAll("count-up")) {
let final = parseInt(element.innerText)
let duration = 2000.0
@ -384,9 +388,7 @@ export class AnimeNotifier {
return Promise.resolve(response)
})
.then(response => response.text())
.then(html => {
Diff.root(document.documentElement, html)
})
.then(html => Diff.root(document.documentElement, html))
.then(() => this.app.emit("DOMContentLoaded"))
.then(() => this.loading(false)) // Because our loading element gets reset due to full page diff
}
@ -605,9 +607,7 @@ export class AnimeNotifier {
let request = fetch(path, {
credentials: "same-origin"
})
.then(response => {
return response.text()
})
.then(response => response.text())
history.pushState(url, null, url)
this.app.currentPath = url
@ -617,7 +617,7 @@ export class AnimeNotifier {
// Delay by transition-speed
return delay(200).then(() => request)
.then(html => this.app.setContent(html, true))
.then(html => Diff.innerHTML(this.app.content, html))
.then(() => this.app.emit("DOMContentLoaded"))
.then(() => this.loading(false))
.catch(console.error)
@ -664,9 +664,11 @@ export class AnimeNotifier {
const contentPadding = 24
let scrollHandle: number
let oldScroll = this.app.content.parentElement.scrollTop
let newScroll = 0
let finalScroll = Math.max(target.offsetTop - contentPadding, 0)
// Calculating scrollTop will force a layout - careful!
let oldScroll = this.app.content.parentElement.scrollTop
let scrollDistance = finalScroll - oldScroll
if(scrollDistance > 0 && scrollDistance < 4) {

View File

@ -39,7 +39,7 @@ export class Application {
this.lastRequest.abort()
this.lastRequest = null
}
return new Promise((resolve, reject) => {
let request = new XMLHttpRequest()
@ -71,7 +71,7 @@ export class Application {
if(options.addToHistory === undefined) {
options.addToHistory = true
}
// Set current path
this.currentPath = url
@ -85,14 +85,14 @@ export class Application {
if(e.target !== this.content) {
return
}
// Remove listener after we finally got the correct event.
this.content.removeEventListener("transitionend", onTransitionEnd)
// Wait for the network request to end.
request.then(html => {
// Set content
this.setContent(html, false)
this.setContent(html)
this.scrollToTop()
// Fade animations
@ -113,19 +113,15 @@ export class Application {
return request
}
setContent(html: string, diff: boolean) {
if(diff) {
Diff.innerHTML(this.content, html)
} else {
this.content.innerHTML = html
}
setContent(html: string) {
this.content.innerHTML = html
}
markActiveLinks(element?: HTMLElement) {
if(element === undefined)
element = document.body
let links = element.querySelectorAll("a")
let links = element.getElementsByTagName("a")
for(let i = 0; i < links.length; i++) {
let link = links[i]
@ -165,7 +161,7 @@ export class Application {
if(!url || url === self.currentPath)
return
// Load requested page
self.load(url)
}

View File

@ -7,23 +7,35 @@ export class Diff {
static rootContainer: HTMLElement
// innerHTML will diff the element with the given HTML string and apply DOM mutations.
static innerHTML(aRoot: HTMLElement, html: string) {
static innerHTML(aRoot: HTMLElement, html: string): Promise<void> {
if(!Diff.container) {
Diff.container = document.createElement("main")
}
Diff.container.innerHTML = html
Diff.childNodes(aRoot, Diff.container)
return new Promise((resolve, reject) => {
window.requestAnimationFrame(() => {
Diff.childNodes(aRoot, Diff.container)
resolve()
})
})
}
// root will diff the document root element with the given HTML string and apply DOM mutations.
static root(aRoot: HTMLElement, html: string) {
if(!Diff.rootContainer) {
Diff.rootContainer = document.createElement("html")
}
Diff.rootContainer.innerHTML = html.replace("<!DOCTYPE html>", "")
Diff.childNodes(aRoot.getElementsByTagName("body")[0], Diff.rootContainer.getElementsByTagName("body")[0])
return new Promise((resolve, reject) => {
if(!Diff.rootContainer) {
Diff.rootContainer = document.createElement("html")
}
Diff.rootContainer.innerHTML = html.replace("<!DOCTYPE html>", "")
window.requestAnimationFrame(() => {
Diff.childNodes(aRoot.getElementsByTagName("body")[0], Diff.rootContainer.getElementsByTagName("body")[0])
resolve()
})
})
}
// childNodes diffs the child nodes of 2 given elements and applies DOM mutations.
@ -31,7 +43,7 @@ export class Diff {
let aChild = [...aRoot.childNodes]
let bChild = [...bRoot.childNodes]
let numNodes = Math.max(aChild.length, bChild.length)
for(let i = 0; i < numNodes; i++) {
let a = aChild[i]
@ -69,7 +81,7 @@ export class Diff {
let elemB = b as HTMLElement
let removeAttributes: Attr[] = []
for(let x = 0; x < elemA.attributes.length; x++) {
let attrib = elemA.attributes[x]
@ -119,7 +131,7 @@ export class Diff {
continue
}
elemA.setAttribute(attrib.name, attrib.value)
}