Move History storage to sessionStorage (#3305)

* Move History storage to sessionStorage and history path to window

* Fix type warnings

* Revert currentPathForHistory to move it to its own PR

* fix test
This commit is contained in:
MichaelWest22 2025-06-17 11:08:34 +12:00 committed by GitHub
parent 508e332544
commit 7388d0c057
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 72 additions and 72 deletions

View File

@ -82,7 +82,7 @@ var htmx = (function() {
*/
historyEnabled: true,
/**
* The number of pages to keep in **localStorage** for history support.
* The number of pages to keep in **sessionStorage** for history support.
* @type number
* @default 10
*/
@ -819,10 +819,10 @@ var htmx = (function() {
* @returns {boolean}
*/
function canAccessLocalStorage() {
const test = 'htmx:localStorageTest'
const test = 'htmx:sessionStorageTest'
try {
localStorage.setItem(test, test)
localStorage.removeItem(test)
sessionStorage.setItem(test, test)
sessionStorage.removeItem(test)
return true
} catch (e) {
return false
@ -3137,13 +3137,13 @@ var htmx = (function() {
if (htmx.config.historyCacheSize <= 0) {
// make sure that an eventually already existing cache is purged
localStorage.removeItem('htmx-history-cache')
sessionStorage.removeItem('htmx-history-cache')
return
}
url = normalizePath(url)
const historyCache = parseJSON(localStorage.getItem('htmx-history-cache')) || []
const historyCache = parseJSON(sessionStorage.getItem('htmx-history-cache')) || []
for (let i = 0; i < historyCache.length; i++) {
if (historyCache[i].url === url) {
historyCache.splice(i, 1)
@ -3164,7 +3164,7 @@ var htmx = (function() {
// keep trying to save the cache until it succeeds or is empty
while (historyCache.length > 0) {
try {
localStorage.setItem('htmx-history-cache', JSON.stringify(historyCache))
sessionStorage.setItem('htmx-history-cache', JSON.stringify(historyCache))
break
} catch (e) {
triggerErrorEvent(getDocument().body, 'htmx:historyCacheError', { cause: e, cache: historyCache })
@ -3192,7 +3192,7 @@ var htmx = (function() {
url = normalizePath(url)
const historyCache = parseJSON(localStorage.getItem('htmx-history-cache')) || []
const historyCache = parseJSON(sessionStorage.getItem('htmx-history-cache')) || []
for (let i = 0; i < historyCache.length; i++) {
if (historyCache[i].url === url) {
return historyCache[i]
@ -3226,7 +3226,7 @@ var htmx = (function() {
// is present *anywhere* in the current document we're about to save,
// so we can prevent privileged data entering the cache.
// The page will still be reachable as a history entry, but htmx will fetch it
// live from the server onpopstate rather than look in the localStorage cache
// live from the server onpopstate rather than look in the sessionStorage cache
const disableHistoryCache = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]')
if (!disableHistoryCache) {
triggerEvent(getDocument().body, 'htmx:beforeHistorySave', { path, historyElt: elt })

View File

@ -4,12 +4,12 @@ describe('hx-history attribute', function() {
beforeEach(function() {
this.server = makeServer()
clearWorkArea()
localStorage.removeItem(HTMX_HISTORY_CACHE_NAME)
sessionStorage.removeItem(HTMX_HISTORY_CACHE_NAME)
})
afterEach(function() {
this.server.restore()
clearWorkArea()
localStorage.removeItem(HTMX_HISTORY_CACHE_NAME)
sessionStorage.removeItem(HTMX_HISTORY_CACHE_NAME)
})
it('history cache should not contain embargoed content', function() {
@ -32,8 +32,8 @@ describe('hx-history attribute', function() {
this.server.respond()
workArea.textContent.should.equal('test3')
// embargoed content should NOT be in the localStorage cache
var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
// embargoed content should NOT be in the sessionStorage cache
var cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
cache.length.should.equal(2)
// on history navigation, embargoed content is retrieved from server

View File

@ -5,12 +5,12 @@ describe('hx-push-url attribute', function() {
beforeEach(function() {
this.server = makeServer()
clearWorkArea()
localStorage.removeItem(HTMX_HISTORY_CACHE_NAME)
sessionStorage.removeItem(HTMX_HISTORY_CACHE_NAME)
})
afterEach(function() {
this.server.restore()
clearWorkArea()
localStorage.removeItem(HTMX_HISTORY_CACHE_NAME)
sessionStorage.removeItem(HTMX_HISTORY_CACHE_NAME)
})
it('navigation should push an element into the cache when true', function() {
@ -22,7 +22,7 @@ describe('hx-push-url attribute', function() {
div.click()
this.server.respond()
getWorkArea().textContent.should.equal('second')
var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
var cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
cache[cache.length - 1].url.should.equal('/test')
})
@ -35,7 +35,7 @@ describe('hx-push-url attribute', function() {
div.click()
this.server.respond()
getWorkArea().textContent.should.equal('second')
var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
var cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
should.equal(cache, null)
})
@ -48,7 +48,7 @@ describe('hx-push-url attribute', function() {
div.click()
this.server.respond()
getWorkArea().textContent.should.equal('second')
var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
var cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
cache.length.should.equal(2)
cache[1].url.should.equal('/abc123')
})
@ -68,7 +68,7 @@ describe('hx-push-url attribute', function() {
this.server.respond()
workArea.textContent.should.equal('test2')
var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
var cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
cache.length.should.equal(2)
htmx._('restoreHistory')('/test1')
@ -106,7 +106,7 @@ describe('hx-push-url attribute', function() {
byId('d1').click()
this.server.respond()
}
var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
var cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
cache.length.should.equal(10) // should only be 10 elements
})
@ -125,10 +125,10 @@ describe('hx-push-url attribute', function() {
this.server.respond()
workArea.textContent.should.equal('test2')
var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
var cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
cache.length.should.equal(2)
localStorage.removeItem(HTMX_HISTORY_CACHE_NAME) // clear cache
sessionStorage.removeItem(HTMX_HISTORY_CACHE_NAME) // clear cache
htmx._('restoreHistory')('/test1')
this.server.respond()
getWorkArea().textContent.should.equal('test1')
@ -138,7 +138,7 @@ describe('hx-push-url attribute', function() {
htmx.config.refreshOnHistoryMiss = true
var refresh = false
htmx.location = { reload: function() { refresh = true } }
localStorage.removeItem(HTMX_HISTORY_CACHE_NAME) // clear cache
sessionStorage.removeItem(HTMX_HISTORY_CACHE_NAME) // clear cache
htmx._('restoreHistory')('/test3')
refresh.should.equal(true)
htmx.location = window.location
@ -152,20 +152,20 @@ describe('hx-push-url attribute', function() {
div.click()
this.server.respond()
getWorkArea().textContent.should.equal('second')
var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
var cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
cache.length.should.equal(1)
})
it('deals with malformed JSON in history cache when getting', function() {
localStorage.setItem(HTMX_HISTORY_CACHE_NAME, 'Invalid JSON')
sessionStorage.setItem(HTMX_HISTORY_CACHE_NAME, 'Invalid JSON')
var history = htmx._('getCachedHistory')('url')
should.equal(history, null)
})
it('deals with malformed JSON in history cache when saving', function() {
localStorage.setItem(HTMX_HISTORY_CACHE_NAME, 'Invalid JSON')
sessionStorage.setItem(HTMX_HISTORY_CACHE_NAME, 'Invalid JSON')
htmx._('saveToHistoryCache')('url', make('<div>'))
var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
var cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
cache.length.should.equal(1)
})
@ -174,17 +174,17 @@ describe('hx-push-url attribute', function() {
htmx._('saveToHistoryCache')('url2', make('<div>'))
htmx._('saveToHistoryCache')('url3', make('<div>'))
htmx._('saveToHistoryCache')('url2', make('<div>'))
var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
var cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
cache.length.should.equal(3)
})
it('setting history cache size to 0 clears cache', function() {
htmx._('saveToHistoryCache')('url1', make('<div>'))
var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
var cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
cache.length.should.equal(1)
htmx.config.historyCacheSize = 0
htmx._('saveToHistoryCache')('url2', make('<div>'))
cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
should.equal(cache, null)
htmx.config.historyCacheSize = 10
})
@ -195,7 +195,7 @@ describe('hx-push-url attribute', function() {
htmx._('saveToHistoryCache')('url3', make('<div>'))
htmx._('saveToHistoryCache')('url2', make('<div>'))
htmx._('saveToHistoryCache')('url1', make('<div>'))
var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
var cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
cache.length.should.equal(3)
cache[0].url.should.equal('/url3')
cache[1].url.should.equal('/url2')
@ -246,19 +246,19 @@ describe('hx-push-url attribute', function() {
bigContent += bigContent
}
try {
localStorage.removeItem('htmx-history-cache')
sessionStorage.removeItem('htmx-history-cache')
htmx._('saveToHistoryCache')('/dummy', make('<div>' + bigContent + '</div>'), 'Foo', 0)
should.equal(localStorage.getItem('htmx-history-cache'), null)
should.equal(sessionStorage.getItem('htmx-history-cache'), null)
} finally {
// clear history cache afterwards
localStorage.removeItem('htmx-history-cache')
sessionStorage.removeItem('htmx-history-cache')
}
})
if (/chrome/i.test(navigator.userAgent)) {
it('when localStorage disabled history not saved fine', function() {
var setItem = localStorage.setItem
localStorage.setItem = undefined
it('when sessionStorage disabled history not saved fine', function() {
var setItem = sessionStorage.setItem
sessionStorage.setItem = undefined
this.server.respondWith('GET', '/test', 'second')
getWorkArea().innerHTML.should.be.equal('')
var div = make('<div hx-push-url="true" hx-get="/test">first</div>')
@ -269,7 +269,7 @@ describe('hx-push-url attribute', function() {
getWorkArea().textContent.should.equal('second')
var hist = htmx._('getCachedHistory')('/test')
should.equal(hist, null)
localStorage.setItem = setItem
sessionStorage.setItem = setItem
})
}
@ -277,7 +277,7 @@ describe('hx-push-url attribute', function() {
// path normalization has a bug breaking it right now preventing this test
htmx._('saveToHistoryCache')('http://', make('<div>'))
htmx._('saveToHistoryCache')('http//', make('<div>'))
var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
var cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
cache.length.should.equal(2)
cache[0].url.should.equal('http://') // no normalization as invalid
cache[1].url.should.equal('/http') // can normalize this one
@ -285,7 +285,7 @@ describe('hx-push-url attribute', function() {
it('history cache clears out disabled attribute', function() {
htmx._('saveToHistoryCache')('/url1', make('<div><div data-disabled-by-htmx disabled></div></div>'))
var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
var cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
cache.length.should.equal(1)
cache[0].url.should.equal('/url1')
cache[0].content.should.equal('<div data-disabled-by-htmx=""></div>')
@ -337,7 +337,7 @@ describe('hx-push-url attribute', function() {
div1.click()
this.server.respond()
div1.innerHTML.should.equal('Result')
var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
var cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
cache.length.should.equal(1)
path.should.equal('/pushpath')
htmx.off('htmx:pushedIntoHistory', handler)
@ -353,7 +353,7 @@ describe('hx-push-url attribute', function() {
div1.click()
this.server.respond()
div1.innerHTML.should.equal('Result')
var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
var cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
cache.length.should.equal(1)
path.should.equal('/pushpath')
htmx.off('htmx:pushedIntoHistory', handler)
@ -369,7 +369,7 @@ describe('hx-push-url attribute', function() {
div1.click()
this.server.respond()
div1.innerHTML.should.equal('Result')
var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
var cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
should.equal(cache, null)
path.should.equal('')
htmx.off('htmx:pushedIntoHistory', handler)

View File

@ -4,12 +4,12 @@ describe('hx-replace-url attribute', function() {
beforeEach(function() {
this.server = makeServer()
clearWorkArea()
localStorage.removeItem(HTMX_HISTORY_CACHE_NAME)
sessionStorage.removeItem(HTMX_HISTORY_CACHE_NAME)
})
afterEach(function() {
this.server.restore()
clearWorkArea()
localStorage.removeItem(HTMX_HISTORY_CACHE_NAME)
sessionStorage.removeItem(HTMX_HISTORY_CACHE_NAME)
})
it('navigation should replace an element into the cache when true', function() {
@ -21,7 +21,7 @@ describe('hx-replace-url attribute', function() {
div.click()
this.server.respond()
getWorkArea().textContent.should.equal('second')
var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
var cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
cache[cache.length - 1].url.should.equal('/test')
})
@ -35,7 +35,7 @@ describe('hx-replace-url attribute', function() {
div1.click()
this.server.respond()
div1.innerHTML.should.equal('Result')
var cache = JSON.parse(localStorage.getItem(HTMX_HISTORY_CACHE_NAME))
var cache = JSON.parse(sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME))
cache.length.should.equal(1)
path.should.equal('/pushpath')
htmx.off('htmx:replacedInHistory', handler)

View File

@ -5,12 +5,12 @@ describe('Core htmx perf Tests', function() {
beforeEach(function() {
this.server = makeServer()
clearWorkArea()
localStorage.removeItem(HTMX_HISTORY_CACHE_NAME)
sessionStorage.removeItem(HTMX_HISTORY_CACHE_NAME)
})
afterEach(function() {
this.server.restore()
clearWorkArea()
localStorage.removeItem(HTMX_HISTORY_CACHE_NAME)
sessionStorage.removeItem(HTMX_HISTORY_CACHE_NAME)
})
function stringRepeat(str, num) {
@ -39,8 +39,8 @@ describe('Core htmx perf Tests', function() {
}
var start = performance.now()
var string = JSON.stringify(array)
localStorage.setItem(HTMX_HISTORY_CACHE_NAME, string)
var reReadString = localStorage.getItem(HTMX_HISTORY_CACHE_NAME)
sessionStorage.setItem(HTMX_HISTORY_CACHE_NAME, string)
var reReadString = sessionStorage.getItem(HTMX_HISTORY_CACHE_NAME)
var finalJson = JSON.parse(reReadString)
var end = performance.now()
var timeInMs = end - start

View File

@ -7,9 +7,9 @@
<script>
htmx.on("htmx:beforeHistorySave", function(evt){
console.log("Saving history : ", evt.detail);
console.log("History Cache Before:", JSON.parse(localStorage.getItem("htmx-history-cache")))
console.log("History Cache Before:", JSON.parse(sessionStorage.getItem("htmx-history-cache")))
setTimeout(function () {
console.log("History Cache After:", JSON.parse(localStorage.getItem("htmx-history-cache")))
console.log("History Cache After:", JSON.parse(sessionStorage.getItem("htmx-history-cache")))
}, 10);
})
</script>

View File

@ -6,9 +6,9 @@
<script>
htmx.on("htmx:beforeHistorySave", function(evt){
console.log("Saving history : ", evt.detail);
console.log("History Cache Before:", JSON.parse(localStorage.getItem("htmx-history-cache")))
console.log("History Cache Before:", JSON.parse(sessionStorage.getItem("htmx-history-cache")))
setTimeout(function () {
console.log("History Cache After:", JSON.parse(localStorage.getItem("htmx-history-cache")))
console.log("History Cache After:", JSON.parse(sessionStorage.getItem("htmx-history-cache")))
}, 10);
})
</script>

View File

@ -6,9 +6,9 @@
<script>
htmx.on("htmx:beforeHistorySave", function(evt){
console.log("Saving history : ", evt.detail);
console.log("History Cache Before:", JSON.parse(localStorage.getItem("htmx-history-cache")))
console.log("History Cache Before:", JSON.parse(sessionStorage.getItem("htmx-history-cache")))
setTimeout(function () {
console.log("History Cache After:", JSON.parse(localStorage.getItem("htmx-history-cache")))
console.log("History Cache After:", JSON.parse(sessionStorage.getItem("htmx-history-cache")))
}, 10);
})
</script>

View File

@ -6,9 +6,9 @@
<script>
htmx.on("htmx:beforeHistorySave", function(evt){
console.log("Saving history : ", evt.detail);
console.log("History Cache Before:", JSON.parse(localStorage.getItem("htmx-history-cache")))
console.log("History Cache Before:", JSON.parse(sessionStorage.getItem("htmx-history-cache")))
setTimeout(function () {
console.log("History Cache After:", JSON.parse(localStorage.getItem("htmx-history-cache")))
console.log("History Cache After:", JSON.parse(sessionStorage.getItem("htmx-history-cache")))
}, 10);
})
</script>

View File

@ -6,9 +6,9 @@
<script>
htmx.on("htmx:beforeHistorySave", function(evt){
console.log("Saving history : ", evt.detail);
console.log("History Cache Before:", JSON.parse(localStorage.getItem("htmx-history-cache")))
console.log("History Cache Before:", JSON.parse(sessionStorage.getItem("htmx-history-cache")))
setTimeout(function () {
console.log("History Cache After:", JSON.parse(localStorage.getItem("htmx-history-cache")))
console.log("History Cache After:", JSON.parse(sessionStorage.getItem("htmx-history-cache")))
}, 10);
})
</script>

View File

@ -6,9 +6,9 @@
<script>
htmx.on("htmx:beforeHistorySave", function(evt){
console.log("Saving history : ", evt.detail);
console.log("History Cache Before:", JSON.parse(localStorage.getItem("htmx-history-cache")))
console.log("History Cache Before:", JSON.parse(sessionStorage.getItem("htmx-history-cache")))
setTimeout(function () {
console.log("History Cache After:", JSON.parse(localStorage.getItem("htmx-history-cache")))
console.log("History Cache After:", JSON.parse(sessionStorage.getItem("htmx-history-cache")))
}, 10);
})
</script>

View File

@ -6,9 +6,9 @@
<script>
htmx.on("htmx:beforeHistorySave", function(evt){
console.log("Saving history : ", evt.detail);
console.log("History Cache Before:", JSON.parse(localStorage.getItem("htmx-history-cache")))
console.log("History Cache Before:", JSON.parse(sessionStorage.getItem("htmx-history-cache")))
setTimeout(function () {
console.log("History Cache After:", JSON.parse(localStorage.getItem("htmx-history-cache")))
console.log("History Cache After:", JSON.parse(sessionStorage.getItem("htmx-history-cache")))
}, 10);
})
</script>

View File

@ -6,9 +6,9 @@
<script>
htmx.on("htmx:beforeHistorySave", function(evt){
console.log("Saving history : ", evt.detail);
console.log("History Cache Before:", JSON.parse(localStorage.getItem("htmx-history-cache")))
console.log("History Cache Before:", JSON.parse(sessionStorage.getItem("htmx-history-cache")))
setTimeout(function () {
console.log("History Cache After:", JSON.parse(localStorage.getItem("htmx-history-cache")))
console.log("History Cache After:", JSON.parse(sessionStorage.getItem("htmx-history-cache")))
}, 10);
})
</script>

View File

@ -7,9 +7,9 @@
<script>
htmx.on("htmx:beforeHistorySave", function(evt){
console.log("Saving history : ", evt.detail);
console.log("History Cache Before:", JSON.parse(localStorage.getItem("htmx-history-cache")))
console.log("History Cache Before:", JSON.parse(sessionStorage.getItem("htmx-history-cache")))
setTimeout(function () {
console.log("History Cache After:", JSON.parse(localStorage.getItem("htmx-history-cache")))
console.log("History Cache After:", JSON.parse(sessionStorage.getItem("htmx-history-cache")))
}, 10);
})
</script>

View File

@ -1,5 +1,5 @@
var server = makeServer()
var autoRespond = localStorage.getItem('hx-scratch-autorespond') == 'true'
var autoRespond = sessionStorage.getItem('hx-scratch-autorespond') == 'true'
server.autoRespond = autoRespond
ready(function() {
if (autoRespond) {
@ -8,10 +8,10 @@ ready(function() {
})
function toggleAutoRespond() {
if (server.autoRespond) {
localStorage.removeItem('hx-scratch-autorespond')
sessionStorage.removeItem('hx-scratch-autorespond')
server.autoRespond = false
} else {
localStorage.setItem('hx-scratch-autorespond', 'true')
sessionStorage.setItem('hx-scratch-autorespond', 'true')
server.autoRespond = true
}
}