Skip to content

Instantly share code, notes, and snippets.

@mklabs
Created March 8, 2011 13:26
Show Gist options
  • Select an option

  • Save mklabs/860253 to your computer and use it in GitHub Desktop.

Select an option

Save mklabs/860253 to your computer and use it in GitHub Desktop.
new version with (nearly) detailled information of albumtable script
/*
* Script for albumtable.html template
*/
$(document).ready(function() {
$("#voteInfo").hide();
$('#albumList').dataTable();
//album covers
$.each($('.cover'), function(){
var album = $(this).parent().parent().attr("id");
//album id
var id = album.substring(6);
//displayCover(id, $(this));
});
//vote link
$('a.voteLink').click(function(){
var albumId = parseInt(this.id);
clickVote(albumId);
//only one vote is possible for an album
$('#'+albumId+'-clickVote').hide();
});
});
var clickVote = function(id) {
$.ajax({
url: '/vote',
type: "POST",
data: 'id=' + id,
complete: function(req) {
//success
if (req.status == 200) {
var newTotal = req.responseText;
$('#nbVotes'+id).text(newTotal);
$("#voteInfo").slideDown("slow").delay(3000).slideUp("slow");
}
}
});
};
var displayCover = function(id, albumMarkup){
var root = '/public/shared/covers';
var markup = '<img src="' + root + '/' + id + '" width="200" height="197">';
albumMarkup.bt(markup,
{
width: 200,
fill: 'white',
cornerRadius: 20,
padding: 20,
strokeWidth: 1,
trigger: ['mouseover', 'click']
});
}
/*
* albumtable.js refactored
*
* 1. No need to define those function (clickVote, displayCover) globally. This is an anti-pattern. Use the closure block scope
* to not pollute the global namespace.
* - prefer using var name = function() {} rather than function name() {} due to js hoisting behaviour. It's preferable
* to declare functions this way and prior use. Always a good idea to follows the single var pattern, as it mades clear
* which variables are used within the scope of this function.
* 2. Beware of how scopes work in javascript. Blocks don't have scope (unlike java), only functions does. Declaring var in an if
* or for statement for instance is considered an anti-pattern.
* 3. IDs versus Classes
* - always a confusing thing for most of web developpers.
* - IDs should only be used where there is only one kind of element in a page (#header, #footer, #main, ...)
* - Classes are preferable since they allow simpler modular code and promotes re-usability.
* 4. parseInt function
* - never forget the often unknown second parameter which is radix (var intValue = parseInt(string[, radix]);). While this
* parameter is optional, always specify it to eliminate reader confusion and to guarantee predictable behavior. Different
* implementations produce different results when a radix is not specified. Javascript does funny assumption depending on
* the input string (try with something beginning with 0x, 0X , 0, aha)
*
*
* jQuery
* 1. One of the first thing we should understand with jQuery is how it works and behaves.
* - ($ is an alias to the jQuery function) the $ functions is not magic! It does pretty good stuff but it does
* not prevent you from doing non-optimal stuff (like re-querying the dom for something we juste have already).
* - each time $ is invoked against a selector, it could return you an instance of jQuery that is a "collection" (an array) of
* 0, 1 or more elements. The value returned by the $ method could and should be "cached" in a variable for further use. Even though
* jQuery is pretty efficient, it is a performance costly to unnecessary query the dom again and again (just like it is when you deal with
* db operation).
* -
* 2. Hiding on page load is considered bad practice as the use could see it before js even begin to execute, use css class instead in the
* server-side generated markup(really just a display: none).
* 3. $.each / $.fn.each: http://api.jquery.com/each
*
*
*
* General
* 1. Mixing html markup and JS is considered bad practice.
* 2. Always defined your script in a wrapping closure. use parameters to enforce the identity of $ (and possibly munging others)
*
*/
(function($, global) {
$(document).ready(function() {
var voteInfo = $('#voteInfo'),
voteLink = $('a.voteLink'),
list = $('#albumList'),
covers = $('.cover'),
nbVotes = $('span[id^="nbVotes"]'),
clickVote = function(e) {
/* * /
function(){
var albumId = parseInt(this.id, 10); // do not forget radix, btw why bother parseInt?
clickVote(albumId);
// only one vote is possible for an album
$('#'+albumId+'-clickVote').hide();
}
/* */
// in this context, e.target is the same as this
var t = $(e.target),
id = t.attr('id').split('-')[0],
voteTarget = nbVotes.filter("[id$=" + id + "]");
// only one vote is possible for an album
// why requery the dom???? You already have a reference to that this bro..
// $('#'+albumId+'-clickVote').hide();
t.hide(); // $(this).hide() or $(e.target)
$.ajax({
url: '/vote',
type: "POST",
// use hash object instead
// data: 'id=' + id,
data: {id: id},
complete: function(req) {
// beware of js particular scope
var newTotal = req.responseText;
if (req.status === 200) {
voteTarget.text(newTotal);
// even better, use css3 anims to do that
voteInfo.slideDown("slow").delay(3000).slideUp("slow");
}
}
});
},
displayCover = function(id, albumMarkup){
var root = '/public/shared/covers';
// Mixing html markup and JS is considered bad practice.
var markup = '<img src="' + root + '/' + id + '" width="200" height="197">';
albumMarkup.bt(markup, {
width: 200,
fill: 'white',
cornerRadius: 20,
padding: 20,
strokeWidth: 1,
trigger: ['mouseover', 'click']
});
};
// Hiding on page load is considered bad practice as the use could see it before js even begin to execute, use css instead.
// voteInfo.hide();
list.dataTable();
covers.each(function(i, val) {
// same there always cache, especially in a loop
var t = $(this);
// ids are bad, especially to store app's data (used to determine img src path).
// prefer using html5 data-attribute. Since jQuery 1.4, $.data method handle them gracefully.
// also. prefer using the super nifty closest method
// var album = t.parent().parent().attr("id");
var album = t.closest('tr').attr("id");
// get album id: regex ftw. substring is so ...
//var id = album.substring(6);
var id = album.match(/album-(\d)/)[1];
displayCover(id, t);
});
/** jQuery plugin version of the previous stuff... since we're iterating on a collection and doing some stuff (calling another plugin..) ** /
$.fn.displayCover = function(o) {
// default options, merge in with user defined options (o)
var options = $.extend({
root: '/public/shared/covers',
imgTmpl: '<img src="/public/shared/covers/${id}" width="200" height="197">'
}, o);
if(!this.length) {
return; // don't act on absent element
}
return this.each(function(i, el) {
var t = $(this), // = el
albumId = t.closest('tr').attr('id').match(/album-(\d+)/)[1];
t.bt(options.imgTmpl.replace("${id}", albumId), {
width: 200,
fill: 'white',
cornerRadius: 20,
padding: 20,
strokeWidth: 1,
trigger: ['mouseover', 'click']
});
});
};
Use: $('.cover').displayCover(); //... use it (or not!)
/* */
// vote link
// why not considering clickVote as the event handler?
/* * /
voteLink.click(function(){
var albumId = parseInt(this.id, 10); // do not forget radix
clickVote(albumId);
// only one vote is possible for an album
$('#'+albumId+'-clickVote').hide();
});
/* */
// Use it with $.proxy for even more flexibility (and possibly get some organization)
voteLink.click(clickVote);
// even more cool, use event delegation to handle links (especially links in a table)
// voteLink.live('click', clickVote)
// even more cool, use the new delegate event
// $(".my-super-awesome-table").deleaget('a.voteLink', 'click', clickVote)
});
})(this.jQuery, this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment