instantclick: support updating head tags

only for meta and link (not stylesheet) tags
This commit is contained in:
Reorx 2022-05-28 02:10:33 +08:00
parent 0d1dec9d55
commit c60eaeb535
3 changed files with 62 additions and 4 deletions

View File

@ -17,6 +17,7 @@ var InstantClick = function(document, location) {
$title = false,
$mustRedirect = false,
$body = false,
$head = false,
$timing = {},
$isPreloading = false,
$isWaitingForCompletion = false,
@ -125,12 +126,15 @@ var InstantClick = function(document, location) {
return returnValue
}
function changePage(title, body, newUrl, scrollY) {
function changePage(title, body, headCache, newUrl, scrollY) {
document.documentElement.replaceChild(body, document.body)
/* We cannot just use `document.body = doc.body`, it causes Safari (tested
5.1, 6.0 and Mobile 7.0) to execute script tags directly.
*/
// updateHead
updateHead(headCache)
if (newUrl) {
history.pushState(null, null, newUrl)
@ -190,6 +194,51 @@ var InstantClick = function(document, location) {
return html.replace(/<noscript[\s\S]+<\/noscript>/gi, '')
}
const stylesheetRegex = /stylesheet/
function loopHeadTags(head, callback) {
for (const i of head.children) {
let v
switch (i.tagName) {
case 'LINK':
const rel = i.getAttribute('rel') || ''
if (rel && !stylesheetRegex.test(rel)) {
v = rel
}
break;
case 'META':
const name = i.getAttribute('name') || ''
const property = i.getAttribute('property') || ''
if (name || property) {
v = `${name}-${property}`
}
break;
}
if (v) {
callback(i, `${i.tagName}-${v}`)
}
}
}
function makeHeadCache(head) {
const cache = {}
loopHeadTags(head, (i, key) => {
cache[key] = i.outerHTML
})
return cache
}
function updateHead(headCache) {
// console.log('update head', headCache)
loopHeadTags(document.head, (i, key) => {
const headTag = headCache[key]
if (headTag) {
i.outerHTML = headTag
}
})
}
////////// EVENT HANDLERS //////////
@ -292,6 +341,7 @@ var InstantClick = function(document, location) {
doc.documentElement.innerHTML = removeNoscriptTags($xhr.responseText)
$title = doc.title
$body = doc.body
$head = makeHeadCache(doc.head)
var alteredOnReceive = triggerPageEvent('receive', $url, $body, $title)
if (alteredOnReceive) {
@ -304,9 +354,11 @@ var InstantClick = function(document, location) {
}
var urlWithoutHash = removeHash($url)
// console.log('set history 2', urlWithoutHash, $head)
$history[urlWithoutHash] = {
body: $body,
title: $title,
head: $head,
scrollY: urlWithoutHash in $history ? $history[urlWithoutHash].scrollY : 0
}
@ -493,7 +545,7 @@ var InstantClick = function(document, location) {
}
$history[$currentLocationWithoutHash].scrollY = pageYOffset
setPreloadingAsHalted()
changePage($title, $body, $url)
changePage($title, $body, $head, $url)
}
@ -695,8 +747,10 @@ var InstantClick = function(document, location) {
$history[$currentLocationWithoutHash] = {
body: document.body,
title: document.title,
head: makeHeadCache(document.head),
scrollY: pageYOffset
}
// console.log('set history 1', $currentLocationWithoutHash)
var elems = document.head.children,
elem,
@ -736,7 +790,7 @@ var InstantClick = function(document, location) {
$history[$currentLocationWithoutHash].scrollY = pageYOffset
$currentLocationWithoutHash = loc
changePage($history[loc].title, $history[loc].body, false, $history[loc].scrollY)
changePage($history[loc].title, $history[loc].body, $history[loc].head, false, $history[loc].scrollY)
})
}

View File

@ -147,6 +147,9 @@
{{- /* InstantClick.js */}}
{{- if (.Param "EnableInstantClick") }}
{{- $instantclick := resources.Get "js/instantclick.js" }}
{{- if hugo.IsProduction }}
{{- $instantclick = minify $instantclick }}
{{- end }}
<script src="{{ $instantclick.RelPermalink }}" data-no-instant
{{- if site.Params.assets.disableFingerprinting }}integrity="{{ $instantclick.Data.Integrity }}"{{- end }}
></script>

View File

@ -30,7 +30,7 @@ function setPrefTheme(theme) {
const toggleThemeCallbacks = []
toggleThemeCallbacks.push((isDark) => {
// console.log('window toggle-theme')
// console.log('window toggle-theme 1')
if (isDark) {
setPrefTheme('light');
} else {
@ -42,6 +42,7 @@ toggleThemeCallbacks.push((isDark) => {
// because window is never changed by InstantClick,
// we add the listener to window to ensure the event is always received
window.addEventListener('toggle-theme', function() {
// console.log('window toggle-theme')
const isDark = isDarkTheme()
toggleThemeCallbacks.forEach(callback => callback(isDark))
});