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, $title = false,
$mustRedirect = false, $mustRedirect = false,
$body = false, $body = false,
$head = false,
$timing = {}, $timing = {},
$isPreloading = false, $isPreloading = false,
$isWaitingForCompletion = false, $isWaitingForCompletion = false,
@ -125,12 +126,15 @@ var InstantClick = function(document, location) {
return returnValue return returnValue
} }
function changePage(title, body, newUrl, scrollY) { function changePage(title, body, headCache, newUrl, scrollY) {
document.documentElement.replaceChild(body, document.body) document.documentElement.replaceChild(body, document.body)
/* We cannot just use `document.body = doc.body`, it causes Safari (tested /* 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. 5.1, 6.0 and Mobile 7.0) to execute script tags directly.
*/ */
// updateHead
updateHead(headCache)
if (newUrl) { if (newUrl) {
history.pushState(null, null, newUrl) history.pushState(null, null, newUrl)
@ -190,6 +194,51 @@ var InstantClick = function(document, location) {
return html.replace(/<noscript[\s\S]+<\/noscript>/gi, '') 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 ////////// ////////// EVENT HANDLERS //////////
@ -292,6 +341,7 @@ var InstantClick = function(document, location) {
doc.documentElement.innerHTML = removeNoscriptTags($xhr.responseText) doc.documentElement.innerHTML = removeNoscriptTags($xhr.responseText)
$title = doc.title $title = doc.title
$body = doc.body $body = doc.body
$head = makeHeadCache(doc.head)
var alteredOnReceive = triggerPageEvent('receive', $url, $body, $title) var alteredOnReceive = triggerPageEvent('receive', $url, $body, $title)
if (alteredOnReceive) { if (alteredOnReceive) {
@ -304,9 +354,11 @@ var InstantClick = function(document, location) {
} }
var urlWithoutHash = removeHash($url) var urlWithoutHash = removeHash($url)
// console.log('set history 2', urlWithoutHash, $head)
$history[urlWithoutHash] = { $history[urlWithoutHash] = {
body: $body, body: $body,
title: $title, title: $title,
head: $head,
scrollY: urlWithoutHash in $history ? $history[urlWithoutHash].scrollY : 0 scrollY: urlWithoutHash in $history ? $history[urlWithoutHash].scrollY : 0
} }
@ -493,7 +545,7 @@ var InstantClick = function(document, location) {
} }
$history[$currentLocationWithoutHash].scrollY = pageYOffset $history[$currentLocationWithoutHash].scrollY = pageYOffset
setPreloadingAsHalted() setPreloadingAsHalted()
changePage($title, $body, $url) changePage($title, $body, $head, $url)
} }
@ -695,8 +747,10 @@ var InstantClick = function(document, location) {
$history[$currentLocationWithoutHash] = { $history[$currentLocationWithoutHash] = {
body: document.body, body: document.body,
title: document.title, title: document.title,
head: makeHeadCache(document.head),
scrollY: pageYOffset scrollY: pageYOffset
} }
// console.log('set history 1', $currentLocationWithoutHash)
var elems = document.head.children, var elems = document.head.children,
elem, elem,
@ -736,7 +790,7 @@ var InstantClick = function(document, location) {
$history[$currentLocationWithoutHash].scrollY = pageYOffset $history[$currentLocationWithoutHash].scrollY = pageYOffset
$currentLocationWithoutHash = loc $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 */}} {{- /* InstantClick.js */}}
{{- if (.Param "EnableInstantClick") }} {{- if (.Param "EnableInstantClick") }}
{{- $instantclick := resources.Get "js/instantclick.js" }} {{- $instantclick := resources.Get "js/instantclick.js" }}
{{- if hugo.IsProduction }}
{{- $instantclick = minify $instantclick }}
{{- end }}
<script src="{{ $instantclick.RelPermalink }}" data-no-instant <script src="{{ $instantclick.RelPermalink }}" data-no-instant
{{- if site.Params.assets.disableFingerprinting }}integrity="{{ $instantclick.Data.Integrity }}"{{- end }} {{- if site.Params.assets.disableFingerprinting }}integrity="{{ $instantclick.Data.Integrity }}"{{- end }}
></script> ></script>

View File

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