Question

I am trying to develop a search filter and making use of the HTML5 history API to reduce the number of requests sent to the server. If the user checks a checkbox to apply a certain filter I am saving that data in the history state, so that when the user unchecks it I am able to load the data back from the history rather than fetching it again from the server.

When the user checks or unchecks a filter I am changing the window URL to match the filter that was set, for instance if the user tries to filter car brands only of a certain category I change the URL like 'cars?filter-brand[]=1'.

But when mutiple filters are applied I have no way of figuring out whether to load the data from the server or to load it from the history.

At the moment I am using the following code.

pushString variable is the new query string that will be created.
var back = [],forward = [];
if(back[back.length-1] === decodeURI(pushString)){   //check last back val against the next URL to be created
                back.pop();
                forward.push(currentLocation);
                history.back();
                return true;
            }else if(forward[forward.length-1] === decodeURI(pushString)){
                forward.pop();
                back.push(currentLocation);
                history.forward();
                return true;
            }else{
                back.push(currentLocation); //add current win location
            }
Was it helpful?

Solution

You can check if your filters are equivalent.

Comparing Objects

This is a simple function that takes two files, and lets you know if they're equivalent (note: not prototype safe for simplicity).

function objEqual(a, b) {
  function toStr(o){
    var keys = [], values = [];
    for (k in o) {
      keys.push(k);
      values.push(o[k]);
    }
    keys.sort();
    values.sort();
    return JSON.stringify(keys) 
    + JSON.stringify(values);
  }
  return toStr(a) === toStr(b);
}

demo

Using the URL

Pass the query part of the URL (window.location.search) to this function. It'll give you an object you can compare to another object using the above function.

function parseURL(url){
  var obj = {}, parts = url.split("&");
  for (var i=0, part; part = parts[i]; i++) {
    var x = part.split("="), k = x[0], v = x[1];
    obj[k] = v;
  }
  return obj;  
}

Demo

History API Objects

You can store the objects with the History API.

window.history.pushState(someObject, "", "someURL")

You can get this object using history.state or in a popState handler.

Keeping Track of Things

If you pull out the toStr function from the first section, you can serialize the current filters. You can then store all of the states in an object, and all of the data associated.

When you're pushing a state, you can update your global cache object. This code should be in the handler for the AJAX response.

var key = toStr(parseUrl(location.search));
cache[key] = dataFromTheServer;

Then abstract your AJAX function to check the cache first.

function getFilterResults(filters, callback) {
    var cached = cache[toStr(filters)]
    if (cached != null) callback(cached);
    else doSomeAJAXStuff().then(callback);
}

You can also use localstorage for more persistent caching, however this would require more advanced code, and expiring data.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top