question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Preserve/save state of list on browser back button, direct page links

See original GitHub issue

Here is some rough code I’ve written to preserve/save the state of a list as a URL query string. e.g. mypage.html?q=jon

It can be used to bookmark a page or share a link with specific search, sort & pagination options chosen, as well as using the browser history API so the browser back button works as you’d expect.

Feel free to knock it into better shape and submit as a pull request if you want to. Note that the “plugins” parameter support was removed from list.js in v1.5.0, which is why this is just a simple function.

Gotchas:

  • Overwrites any existing query string in your page URL
  • Doesn’t save filter state
  • Doesn’t save sort or search state if you call those functions directly - only the ‘automagical’ sort/search
  • Ignores “data-order” and “data-insensitive” attributes for sort
  • Requires browser History API ( https://caniuse.com/#feat=history ) to save state, but not to load it
function ListState(list) {
  var savestate = function() {
    // Preserve DOM state so can be restored on browser back button, etc
    // overwrites any existing URL query string
    var q = '';
    if (list.pagination !== undefined) {
      if (list.i > 1) q += "&i="+list.i;
      if (list.page != list.pageDefault) q += "&n="+list.page;
    };
    // TODO: read 'data-order'? and 'data-insensitive' attributes for sort
    for (var i = 0, els = list.utils.getByClass(list.listContainer, list.sortClass); i < els.length; i++) {
      var s = list.utils.classes(els[i]);
      if (s.has('desc')) q += "&sd="+i; // numeric sort field gives a shorter URL
        else if (s.has('asc')) q += "&sa="+i;
    };
    if (list.searched) {
      var el = list.utils.getByClass(list.listContainer, list.searchClass, true);
      if (el) q += "&q="+encodeURIComponent(el.value);
    };
    history.replaceState(null, null, window.location.href.split('?')[0] + q.replace('&','?'));
  };
    
  function getUrlParameter(name) {
    name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
    var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
    var results = regex.exec(location.search);
    return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
  };

  // Restore page/search/sort state from query string if set
  // order of operations is important: search => sort => set page
  var sa = getUrlParameter('sa'), sd = getUrlParameter('sd'), q = getUrlParameter('q');
  if (q.length) {
    var el = list.utils.getByClass(list.listContainer, list.searchClass, true);
    if (el) el.value = q;
    list.search(q);
  };
  if (sa.length || sd.length) {
    var els = list.utils.getByClass(list.listContainer, list.sortClass);
    var el = els[parseInt(sa.length ? sa : sd,10)];
    var valueName = el ? list.utils.getAttribute(el, 'data-sort') : 0;
    if (valueName) list.sort(valueName, {order:sa.length ? "asc" : "desc"});
  };
  if (list.pagination !== undefined) {
    var i = parseInt(getUrlParameter('i'),10), n = parseInt(getUrlParameter('n'),10);
    list.pageDefault = list.page;
    if (i || n) list.show(i ? i : 1, n ? n : list.page);
  };      
  if (!!(window.history && history.pushState)) list.on('updated', savestate);
};

Issue Analytics

  • State:open
  • Created 5 years ago
  • Comments:9 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
Julianoecommented, Jun 25, 2019

Indeed i tested it out and it works, adding my search keyword to the url, updated live. It does not update when clicking the filters so i added this little piece of code: It can work for a list of radio buttons. I’ll try to work out a more universal solution.

if (list.filtered){
 var filter = list.listContainer.querySelector('input:checked').value;
 if (filter) q += "&q="+encodeURIComponent(filter);
};
0reactions
Julianoecommented, Jul 10, 2019

@Prkns where are you stuck at?

I took the code that @sheffieldnick put on the first comment of this issue. I added the part i talked about, see following code:

function ListState(list) {
  var savestate = function() {
    // Preserve DOM state so can be restored on browser back button, etc
    // overwrites any existing URL query string
    var q = '';
    if (list.pagination !== undefined) {
      if (list.i > 1) q += "&i="+list.i;
      if (list.page != list.pageDefault) q += "&n="+list.page;
    };
    // TODO: read 'data-order'? and 'data-insensitive' attributes for sort
    for (var i = 0, els = list.utils.getByClass(list.listContainer, list.sortClass); i < els.length; i++) {
      var s = list.utils.classes(els[i]);
      if (s.has('desc')) q += "&sd="+i; // numeric sort field gives a shorter URL
        else if (s.has('asc')) q += "&sa="+i;
    };
    if (list.searched) {
      var el = list.utils.getByClass(list.listContainer, list.searchClass, true);
      // adding the search in the url
      if (el) q += "&s="+encodeURIComponent(el.value);
    };
    

    // THIS THE PART I ADDED
    if (list.filtered){
      var filter = list.listContainer.querySelector('input:checked').value;
      // adding the filter in the url
      if (filter) q += "&q="+encodeURIComponent(filter);
    };

    history.replaceState(null, null, window.location.href.split('?')[0] + q.replace('&','?'));
  };

  function getUrlParameter(name) {
    name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
    var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
    var results = regex.exec(location.search);
    return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
  };

  // Restore page/search/sort state from query string if set
  // order of operations is important: search => sort => set page
  var sa = getUrlParameter('sa'), sd = getUrlParameter('sd'), q = getUrlParameter('q');
  if (q.length) {
    var el = list.utils.getByClass(list.listContainer, list.searchClass, true);
    if (el) el.value = q;
    list.search(q);
  };
  if (sa.length || sd.length) {
    var els = list.utils.getByClass(list.listContainer, list.sortClass);
    var el = els[parseInt(sa.length ? sa : sd,10)];
    var valueName = el ? list.utils.getAttribute(el, 'data-sort') : 0;
    if (valueName) list.sort(valueName, {order:sa.length ? "asc" : "desc"});
  };
  if (list.pagination !== undefined) {
    var i = parseInt(getUrlParameter('i'),10), n = parseInt(getUrlParameter('n'),10);
    list.pageDefault = list.page;
    if (i || n) list.show(i ? i : 1, n ? n : list.page);
  };
  if (!!(window.history && history.pushState)) list.on('updated', savestate);
};

I’ve put all this in a file named listState.js and imported in the file i use List.js

<script src="./js/list.min.js"></script>
<script src="./js/listState.js"></script>

then i have my code to initialize all this

<script type="text/javascript">
            var options = {
              valueNames: [
                { data: ['color']}
              ]
            };
            var myList = new List('items', options);
            ListState(myList);
            <?php // allows the checked status to be set accordingly to the url leading you to the page
            $page = get_query_var('q');
            if( ! empty( $page ) ) : ?>
            jQuery("input[value=<?= $page ?>]").attr('checked', true);
            <?php endif; ?>

            function resetList(){
              myList.search();
              myList.filter();
              myList.update();
              jQuery(".filter-all").prop('checked', true);
              jQuery('.filter').prop('checked', false);
              jQuery('.search').val('');
            };

            function updateList(){
              var values_color = jQuery("input[name=color]:checked").val();

              myList.filter(function (item) {
                var colorFilter = false;

                if(values_color == "all")
                {
                  colorFilter = true;
                } else {
                  colorFilter = item.values().color == values_color;

                }
                return colorFilter
              });
              myList.update();
            }

            jQuery(function(){
              jQuery("input[name=color]").change(updateList);

              myList.on('updated', function (list) {
                if (list.matchingItems.length > 0) {
                  jQuery('.no-result').hide()
                } else {
                  jQuery('.no-result').show()
                }
              });
            });
             
            // adding some sort of animation when updating see issue 366
            // https://github.com/javve/list.js/issues/366
            myList.on('updated', function (list) {
              jQuery('.list--list-item').addClass('animated fadeIn');
            });
          </script>

As you can see i’m using PHP. I simply take the value of query the user is coming from and check the corresponding radio button accordingly. The little bit i added allows it to update the query in the url when i click on of the different “color” radio buttons that i use to filter my list.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Preserve page state for revisiting using browser back button
The browser loads the page as it was first received. Any DOM modifications done via javascript will not be preserved.
Read more >
Oracle Alta UI Patterns: Browser Back Button Navigation
The browser Back button is a fundamental navigation control that users are ... a page that hides or shows elements of the UI...
Read more >
How to make browser to go back to previous page using ...
There is two popular way to make browsers go back to the previous page by clicking JavaScript event, both methods are described below:....
Read more >
Handling back button click on browsers using visual force pages
We have a multistep wizard which uses Vf pages. We want to throw an alert message to Users if they click back button...
Read more >
Broken product links after pressing the browser back button
I fixed it in the short term by deleting the preventDefault in line 506 in gtm4wp-woocommerce-enhanced.js. The page I need help with: [log...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found