To personalize the select
so that it shows a list of option
s sorted in the order of "Most Frequently Used" ones, we can have best of both the world's in your wishlist. Let's create one using jQuery and cookies. And also, make a re-usable solution which is ready to use. With the added advantage of you being able to modify it according to your liking.
Before we do that, just a note of caution. If you are looking to personalize a lot of things, then it is better to do that server-side and create a personalization subsystem in your app. Relying on client-side cookies is not advisable for such scenarios. However, if your use-case is just a one-off scenario like this one, where you simply want to enhance user experience by providing an extra enhancement over and above the working feature-set without making changes to the server-side code, then it makes perfect sense to implement such functionality client-side. That is exactly why cookies and localstorage are there. Just like advertisers use tracking cookies, this is also like a tracking cookie which is tracking what your users are using more frequently. Isn't it?
That said, let's create a jQuery plugin which you can consume as-is. It will also allow you to apply it to more than one select
. Let's call it .mfu()
.
Note: The code here is just a crude demo for you. It is not optimized and perhaps needs re-factoring. Currently, this plugin will fail, if you do not give a unique id
to all your select
s, as it depends on id
to attach and retreive the frequency.
You can view the complete demo here: http://jsfiddle.net/abhitalks/u5z868d0/
Select an option from both the select
s and keep clicking "run" in the fiddle. You will see that the most frequently used options keep getting to top.
We'll start with a blank plugin template which looks like this:
(function ($) {
$.fn.extend({
mfu: function () {
....
return this.each(function () {
...
});
}
});
})(jQuery);
We go through each of the select
s on which the plugin was called and return all of them back for chaining.
You can then call this plugin on all your selects like this:
$(select).mfu();
We will store our option
details along with the frequencies in an object array for all of the select
s specified in the selector on which the plugin is called. The object array looks like this:
[
{
'id': 'drop1',
'mfu':
[
{ 'value': 'a', 'caption': 'Red', 'frequency': 0 },
{ 'value': 'b', 'caption': 'Green', 'frequency': 0 },
...
]
},
{
'id': 'drop2',
'mfu':
[
{ 'value': '1', 'caption': 'One', 'frequency': 0 },
{ 'value': '2', 'caption': 'Two', 'frequency': 0 },
...
]
}
];
Now, let's start coding the plugin. First thing to do is some initialize stuff. We will store the "frequency" of each option
in each select
for our "Most Frequently Used" items in a cookie. So, our initialize code will include code to read the cookie on initialize and load the "Most Frequently Used" items in an object array. We will also need to write back the cookie, so let's create a function to do that here itself. Because, we will be working with an object array, we will use JSON to stringify()
it to write to cookie, and parse()
on read.
So, our plugin now starts looking like this:
(function ($) {
$.fn.extend({
mfu: function () {
var self = this, cookieName = 'mfu',
defaultExpiry = new Date(2050, 1, 1),
getCookie = function(name) {
var results = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
return results ? unescape(results[2]) : null;
},
setCookie = function(name, value) {
var cookieString = name + "=" + escape(JSON.stringify(value));
cookieString += "; expires=" + defaultExpiry.toGMTString();
document.cookie = cookieString;
},
mfuStored = getCookie(cookieName),
mfuList = JSON.parse(mfuStored)
;
return self.each(function () {
...
});
}
});
})(jQuery);
We have hard-coded the cookie name as "mfu". We have also set a far expiry date for the cookie. In real-life code, you must look into dynamically setting this. Then there are routine read/write functions for cookies. We read the cookie and load the MFU list into a variable mfuList
which is an object array.
Next, we go through each of the select
s on which the plugin was called and then search in our object array for the id
of each select
. If not found, that means it has been run for the first time (and/or cookie is not available), in this case we create new object to hold the frequencies. If found, we then find the option
s in that select
and check if the frequencies are available on cookie-read-load. If not found, we create the new objects to hold the option
details.
Once that is done, we sort our object array for the current select
and then replace the select
option
s in the sorted order. We can also show the frequencies to the user if required.
Now, all that remains, is to bind the change
event on each select
to update the frequency of the selected option
in our object array and save that back to the cookie. In this demo, we are saving it on change
event. In real life, you could save the cookie when the page unloads.
So, now our complete plugin looks like this:
(function ($) {
$.fn.extend({
mfu: function () {
var self = this, cookieName = 'mfu',
defaultExpiry = new Date(2050, 1, 1), // far ahead expiry of cookie
getCookie = function(name) { // routine to read cookie
var results = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
return results ? unescape(results[2]) : null;
},
setCookie = function(name, value) { // routine to write cookie
var cookieString = name + "=" + escape(JSON.stringify(value));
cookieString += "; expires=" + defaultExpiry.toGMTString();
document.cookie = cookieString;
},
mfuStored = getCookie(cookieName), // read the cookie
mfuList = JSON.parse(mfuStored) // parse object array from the string
;
return self.each(function () {
var select = this, mfu;
if (! mfuList) { // for the first time, create object
mfuList = []; mfuList.push({ 'id': select.id, 'mfu': [] });
}
// search for the current select
mfu = mfuList.filter(function(o) {
return o.id === select.id;
})[0];
if (!mfu) { // if not found, create object and add to array
mfuList.push({ 'id': select.id, 'mfu': [] });
mfu = mfuList[mfuList.length - 1].mfu;
} else {
mfu = mfu.mfu; // if found, use the mfu property
}
// for each option search the object arrat
[].map.call(select.options, function(option) {
var opt = mfu.filter(function(o) {
return o.value === option.value;
})[0];
if (! opt) { // if not found, create object and add to array
opt = {} ;
opt.value = option.value;
opt.caption = option.innerText;
opt.frequency = 0;
mfu.push(opt);
}
});
// sort the array on frequency
mfu.sort(function(a, b) { return a.frequency < b.frequency });
// empty the select
$(select).empty();
// loop thru our object array and to select in sorted order
mfu.forEach(function(item) {
var $opt = $("<option>");
$opt.val(item.value);
$opt.text(item.caption + ': (' + item.frequency + ')');
$(select).append($opt);
});
// add event listener for change
$(select).on("change", function() {
var value = this.value;
// search our object array
var opt = mfu.filter(function(o) {
return o.value === value;
})[0];
opt.frequency++; // increment the frquency
setCookie(cookieName, mfuList); // write back the cookie
});
});
}
});
})(jQuery);
// call the plugin on all selects
$("select").mfu();
That's all there is to it. You can now improvise and improve this code further if required, otherwise it is ready to be used as-is.
.