I'm trying to improve my javascript coding style and want to start writing my code more object-oriented. The code below is getting a JSON-stringified list of locations from a cookie and appending a li
to a list for each location. I would appreciate some helpful critique and any general pointers. I have some questions too:
Am I using
self
correctly? It feels like I'm over using it i.e. on function calls but when its not there I cannot access those functions. Some plugins I've used are written in this way and some are not, why is that?If I wanted to use this as a static class, how can I code it differently instead of instantiating it?
I have experience with vb/c# if that helps when explaining things.
var favourites = function(list, settings) {
var self = this;
self.options = {
maxitems: 5,
cookiename: "fav-locations"
};
self._$list = $(list) || $();
self._locations = [];
//extend defaults with any settings
$.extend(self.options, settings);
//load the locations
var init = function (aryLocations) {
var index = 0;
if (aryLocations) {
for (index; index < aryLocations.length; index++) {
self.add(aryLocations[index]);
}
self.loadLocations();
self.bindRemoveButtons();
}
};
self.add = function (loc) {
if ((self._locations.length < self.options.maxitems) && self.find(loc)) {
self._locations.push(loc);
$.cookie(self.options.cookiename, JSON.stringify(self._locations), { path: '/' });
}
else {
console.log('exceeded limit');
}
};
self.remove = function (loc) {
var location_index = self.find(loc);
console.log(location_index);
if (location_index > -1) {
self._locations.splice(location_index, 1);
$.cookie(self.options.cookiename, JSON.stringify(self._locations), { path: '/' });
self.setCount();
}
else {
console.log('location not found.');
}
};
self.populateLocations = function () {
self.clearList();
$.each(self._locations, function (index, item) {
//get this locations name and id - format here is: name|id
var location = {
name: item.split('|')[0] || '',
id: item.split('|')[1] || 0
};
//create a new li element
var $thisItem = $('<li/>').appendTo(self._$list[0]).addClass('wrap');
$thisItem.data("loc", item);
$thisItem.html('<button class="remove-icon">×</button> <a href="/home/search-handler.asp">' + location.name + '</a>');
});
};
self.setCount = function () {
//set count value
var countelem = self._$list.prev().find('.count');
if (countelem.length > 0) { countelem.text(self._locations.length); };
};
self.loadLocations = function () {
self.populateLocations();
self.setCount();
};
self.clearList = function () {
$(self._$list).find('li').remove();
};
self.find = function (loc) {
var location_index = -1;
for (var index = 0; index < self._locations.length; index++) {
if (self._locations[index] === loc) {
location_index = index;
break;
}
}
return location_index;
};
//remove button click handler
self.bindRemoveButtons = function () {
$(document).on('click', '.remove-icon', function (e) {
var elem = $(this).parent();
e.preventDefault();
elem.slideUp('fast', function () {
self.remove(elem.data("loc"));
elem.remove();
});
});
};
//init the list
init(JSON.parse($.cookie(self.options.cookiename)));
};
var favLocs = new favourites("ul.favourites-list", { tester: 1 });
UPDATE: Using the feedback below I've refactored the code implementing as much as I can. The events are still there as I'm not sure how to resolve that one but I think the code has improved.
var Favourites = (function ($) {
'use strict';
//constructor
function Favourites(list, settings) {
this.options = {
maxitems: 5,
cookiename: "fav-locations",
searchurl: null
};
this._$list = $(list) || $();
this._locations = [];
//extend defaults with any settings
$.extend(this.options, settings);
//add locations to the array
var index = 0,
locations = JSON.parse($.cookie(this.options.cookiename));
for (index; index < locations.length; index++) {
this.add(locations[index]);
}
this.loadLocations();
this.bindRemoveButtons();
this.bindLocationLinkHandler();
}
//methods
Favourites.prototype = {
add: function (loc) {
if ((this._locations.length < this.options.maxitems) && this.find(loc)) {
this._locations.push(loc);
$.cookie(this.options.cookiename, JSON.stringify(this._locations), { path: '/' });
} else {
console.log("exceeded limit");
}
},
remove: function (loc) {
var location_index = this.find(loc);
console.log(location_index);
if (location_index > -1) {
this._locations.splice(location_index, 1);
$.cookie(this.options.cookiename, JSON.stringify(this._locations), { path: '/' });
this.setCount();
} else {
console.log("location not found.");
}
},
populateLocations: function () {
var self = this;
this.clearList();
$.each(this._locations, function (item) {
//get this locations name and id - format here is: name|id
var location = {
name: item.split('|')[0] || '',
id: item.split('|')[1] || 0
},
//create a new li element
$thisItem = $('<li/>').appendTo(self._$list[0]).addClass("wrap");
$thisItem.data("location", location.name);
$thisItem.data("location-id", location.id);
$thisItem.html('<button class="remove-icon">×</button> <a href="' + self.options.searchurl + '" class="fav-link">' + location.name + '</a>');
});
},
setCount: function () {
//set count value
var countelem = this._$list.prev().find('.count');
if (countelem.length > 0) { countelem.text(this._locations.length); }
},
loadLocations: function () {
if ($(".search-nav:visible .current").length > 0) {
$(".favourites, .search-box").fadeIn(300);
}
this.populateLocations();
this.setCount();
},
clearList: function () {
$(this._$list).find('li').remove();
},
find: function (loc) {
var location_index = -1,
index = 0;
for (index; index < this._locations.length; index++) {
if (this._locations[index] === loc) {
location_index = index;
break;
}
}
return location_index;
},
bindRemoveButtons: function () {
var self = this;
$(document).on('click', '.remove-icon', function (e) {
var elem = $(this).parent();
e.preventDefault();
elem.slideUp('fast', function () {
self.remove(elem.data("location") + '|' + elem.data("location-id"));
elem.remove();
});
});
},
bindLocationLinkHandler: function () {
var self = this;
$(document).on("click", ".fav-link", function (e) {
var elem = $(this).parent();
self.forwardToSection(elem.data("location"), elem.data("location-id"), e);
});
},
forwardToSection: function (loc, locid, event) {
var section = $(".search-nav:visible .current a").text(),
qs = '?loc_Location=' + encodeURI(loc) + '&loc_exactlocation=' + locid;
if (section) {
if (event) {
event.currentTarget.href = event.currentTarget.href + qs + '&searchSite=' + section.toLowerCase();
} else {
window.location = this.options.searchurl + qs + '&searchSite=' + section.toLowerCase();
}
} else {
e.preventDefault();
alert("Please select a section above");
}
}
};
return Favourites;
}(jQuery));
var favLocs = new Favourites("ul.favourites-list", { searchurl: "/home/search-handler.asp" });