Reputation: 81
I'm facing problem with push duplicate on top of stack History
api stack
let say i have user interface with click pattern (1 to 4 pages ,it can be random as per user choice)
Question: i want to push all pages in the same way as it is clicked without duplicate on top of history api
, so that when browser forward,backward button is clicked url should change(right now 2 clicks are required)
here is how my project setup will look like (click same page 2 times to see actual issue):
$('li').on('click',function(){
var baseUrl = location.href; //'http://www.siliconvalley.com/pages/';//it can be current url also
var page = $(this).attr('data-page');
var urlObj = new URL(baseUrl);
urlObj.searchParams.append('page',page);
$('#loadedPageContainer').prepend(`<p>${urlObj.href}</p>`);
try{
history.pushState({}, null, urlObj.href); //wanted to avoid duplicate on top of history api stack
}catch(e){}
});
window.addEventListener('popstate', function(e){
console.log('page navigated',e); // called 2 times in my project
//navigateToCurrentUrlRoute();
});
ul{
list-style:none;
display:flex;
justify-content: space-evenly;
}
ul li{
border: 1px solid red;
padding: 50px;
cursor:pointer;
}
#loadedPageContainer{
//display: flex;
width: 500px;
height: 200px;
box-shadow: 0 1px 2px 3px #c7c7c7;
margin: 0 auto;
/* overflow:auto; */
overflow-y:auto;
line-height: 38px;
padding:15px;
}
#loadedPageContainer p{
box-shadow: 0 1px 2px 0px #dee5ea;
}
#loadedPageContainer p:first-child {
box-shadow: 0 4px 2px 1px #de004b;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li data-page="1">page 1</li>
<li data-page="2">page 2</li>
<li data-page="3">page 3</li>
<li data-page="4">page 4</li>
</ul>
<p>Please click same pages 2 or 3 times to see actual issue below or in url on forward,backward browser button click</p>
<div id="loadedPageContainer">
</div>
Note: only top of the history
api stack should not have duplicates
For better view here is a codepen link: https://codepen.io/eabangalore/pen/KKgOwgN Please help me thanks in advance!!
Upvotes: 2
Views: 1585
Reputation: 347
I'm not sure I understand the question.
This is how I understand it. You have two contexts we can refer to as a "history". One context is the page url parameters (eg. &page=1?page=2 etc.). I'll refer to the history in that context as PAGE STATES. The second context is the state of the history, as the browser understands it. I'll refer to the history in that context as the HISTORY STACK.
Run the snippet and let me know if it provides the behavior you're looking for. If not, I've left comments on what I've written to code to do, and what the goals were for each edit.
Here's a preview in plain English on what the code does:
It adds 1 to 4 items into your PAGE STATES, without duplicates, and it allows any number of states in the HISTORY STACK, without immediate duplicates.
If you click between page 1 and page 2 multiple times, there will be 2 items in the PAGE STATE. If you click between pages 1 through 3, there will be 3 items in the PAGE STATE, and so on.
Since we're not leaving duplicates in the PAGE STATE, and we're not erasing any history, if all 4 pages were visited, there will always be 4 items in the PAGE STATE, with the least recently visited pages presented from right to left - and the most recently visited page being presented as the first url parameter. As far as the HISTORY STACK goes, there will be as many entries in the HISTORY STACK as there are changes in the PAGE STATE. I hope I presented that in a fashion which makes sense.
/*
* Edit #1: Adding PARAM STATE / page array and previous page marker
*/
let pageArray = []; // array holding page param history (aka 'PARAM STATES')
let prevPage; // most recently visited page
/* END Edit #1 */
$('li').on('click',function(){
var baseUrl = 'http://www.siliconvalley.com/pages/';//it can be current url also
var page = $(this).attr('data-page');
var urlObj = new URL(baseUrl);
/*
* Edit #2: Using new variables above to hold/dictate non-duplicate states.
*/
// If 'revisiting' previous (current) page...
if (page === prevPage){
return; // ...keep both STACK and PARAM STATES as they are...
}
else prevPage = page; // ...else continue.
pageArray.unshift(page); // Set current page as first in PARAM STATES...
pageArray = [...new Set(pageArray)]; // ...remove duplicates...
pageArray = pageArray.slice(0,4); // ...and limit PARAM STATES to 4 pages.
urlObj.searchParams.delete('page');// Delete previous PARAM STATE.
/* When we now create the search param items using PARAM STATE...
*
* 1.) We've assured PARAM STATE has no duplicates.
* 2.) We've allowed up to 4 states to be presented in PARAM STATES.
* 3.) We've kept immediate/repeat/duplicate history states out of the
* HISTORY STACK so that clicking 'back' in a browser does not result
* in staying on the same page / in the same state.
*/
pageArray.forEach((item) => {urlObj.searchParams.append('page',item)});
/* END Edit #2 */
try{
history.pushState({}, null, urlObj.href); //wanted to avoid duplicate on top of history api stack
}catch(e){}
$('#loadedPageContainer').prepend(`<p>${urlObj.href}</p>`);
});
window.addEventListener('popstate', function(e){
console.log('page navigated',e); // called 2 times in my project
//navigateToCurrentUrlRoute();
});
ul{
list-style:none;
display:flex;
justify-content: space-evenly;
}
ul li{
border: 1px solid red;
padding: 50px;
cursor:pointer;
}
#loadedPageContainer{
//display: flex;
width: 500px;
height: 200px;
box-shadow: 0 1px 2px 3px #c7c7c7;
margin: 0 auto;
/* overflow:auto; */
overflow-y:auto;
line-height: 38px;
padding:15px;
}
#loadedPageContainer p{
box-shadow: 0 1px 2px 0px #dee5ea;
}
#loadedPageContainer p:first-child {
box-shadow: 0 4px 2px 1px #de004b;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li data-page="1">page 1</li>
<li data-page="2">page 2</li>
<li data-page="3">page 3</li>
<li data-page="4">page 4</li>
</ul>
<p>Please click same pages 2 or 3 times to see actual issue below or in url on forward,backward browser button click</p>
<div id="loadedPageContainer">
</div>
Upvotes: 0
Reputation: 2220
Your problem is that you used .append
It adds the parameter a second and third and so on time. Do you understand? So look at this:
$('li').on('click',function(){
var baseUrl = location.href; //'http://www.siliconvalley.com/pages/';//it can be current url also
var page = $(this).attr('data-page');
var urlObj = new URL(baseUrl);
urlObj.searchParams.set('page',page); // use set instead of append
$('#loadedPageContainer').prepend(`<p>${urlObj.href}</p>`);
try{
history.pushState({}, null, urlObj.href); //wanted to avoid duplicate on top of history api stack
}catch(e){}
});
window.addEventListener('popstate', function(e){
console.log('page navigated',e); // called 2 times in my project
//navigateToCurrentUrlRoute();
});
ul{
list-style:none;
display:flex;
justify-content: space-evenly;
}
ul li{
border: 1px solid red;
padding: 50px;
cursor:pointer;
}
#loadedPageContainer{
//display: flex;
width: 500px;
height: 200px;
box-shadow: 0 1px 2px 3px #c7c7c7;
margin: 0 auto;
/* overflow:auto; */
overflow-y:auto;
line-height: 38px;
padding:15px;
}
#loadedPageContainer p{
box-shadow: 0 1px 2px 0px #dee5ea;
}
#loadedPageContainer p:first-child {
box-shadow: 0 4px 2px 1px #de004b;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li data-page="1">page 1</li>
<li data-page="2">page 2</li>
<li data-page="3">page 3</li>
<li data-page="4">page 4</li>
</ul>
<p>Please click same pages 2 or 3 times to see actual issue below or in url on forward,backward browser button click</p>
<div id="loadedPageContainer">
</div>
That code works without duplicate parameters. If you want to avoid duplicate pushes (I think this is your main problem), you have to check if the URL has changed:
$('li').on('click',function(){
var baseUrl = location.href; //'http://www.siliconvalley.com/pages/';//it can be current url also
var page = $(this).attr('data-page');
var urlObj = new URL(baseUrl);
urlObj.searchParams.set('page',page); // use set instead of append
$('#loadedPageContainer').prepend(`<p>${urlObj.href}</p>`);
if (baseUrl != urlObj) { // check url change
try{
history.pushState({}, null, urlObj.href); //wanted to avoid duplicate on top of history api stack
}catch(e){}
}
});
window.addEventListener('popstate', function(e){
console.log('page navigated',e); // called 2 times in my project
//navigateToCurrentUrlRoute();
});
ul{
list-style:none;
display:flex;
justify-content: space-evenly;
}
ul li{
border: 1px solid red;
padding: 50px;
cursor:pointer;
}
#loadedPageContainer{
//display: flex;
width: 500px;
height: 200px;
box-shadow: 0 1px 2px 3px #c7c7c7;
margin: 0 auto;
/* overflow:auto; */
overflow-y:auto;
line-height: 38px;
padding:15px;
}
#loadedPageContainer p{
box-shadow: 0 1px 2px 0px #dee5ea;
}
#loadedPageContainer p:first-child {
box-shadow: 0 4px 2px 1px #de004b;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li data-page="1">page 1</li>
<li data-page="2">page 2</li>
<li data-page="3">page 3</li>
<li data-page="4">page 4</li>
</ul>
<p>Please click same pages 2 or 3 times to see actual issue below or in url on forward,backward browser button click</p>
<div id="loadedPageContainer">
</div>
The popstate
event should now also only be called once per click.
Upvotes: 0
Reputation: 81
To avoid duplicate entries in the History API stack, it is necessary to compare the current page address with the new address that is going to be added using the History API.
In addition, you need to use urlObj.searchParams.set('page', page)
instead of urlObj.searchParams.append('page', page)
since append
doesn't actually change the query parameter name
. append
just adds new query parameter with the same name to the URL.
Try this code, it must help:
$('li').on('click', function () {
var baseUrl = location.href;
var page = $(this).attr('data-page');
var urlObj = new URL(baseUrl);
urlObj.searchParams.set('page', page); // this line was changed
if (urlObj.href !== baseUrl) { // href check was added
$('#loadedPageContainer').prepend(`<p>${urlObj.href}</p>`);
try {
history.pushState({}, null, urlObj.href);
} catch (e) {}
}
});
window.addEventListener('popstate', function (e) {
console.log('page navigated', e);
});
Upvotes: 2