Extending jQuery selectors and understanding the options

There are tons of posts around which bang on about how to extend jQuery to include your own selectors, but to find one that actually explains your options is a pain, hence this post.

jQuery has an extend function baked in which looks something like $.extend(target, object1, object2); and will extend target (which should be an object) with the properties and methods of object1 and object2. You can add as many additional objects (think object3, object4 and object5) to the end as you like.

What we want to do it extend a very specific part of jQuery itself. The $.expr[‘:’] object which stores the built in selectors and any we add.

Indeed when you console.log($.expre[‘:’]) you see a full list of the selectors available in the version of jQuery you are using:

animated
button
checkbox
checked
data
disabled
empty
enabled
file
focus
focusable
has
header
hidden
image
input
onbranch
parent
password
radio
reset
selected
submit
tabbable
text
visible

NB: I’ve removed the ui- selectors to avoid confusion.

So now you know what not to call your selector (unless you want to replace a built in one), we can define our own…

$.extend($.expr[':'],{
	block: function(e,i,m,s) {
		return $(e).css('display') === 'block';
	}
});

The first thing you will notice about the above is that the selector function takes up to (although may need fewer) four argument; e, i, m and s.

e is the element currently being checked.
i is the index of that elements within all elements being checked (starts at zero)
m stands for match and is additional information that we can use in our function.
s stands for stack and is all elements in the current selection or stack

so $(e) makes the current element e into a jQuery object
i allows us to check for the 3rd, 6th, 23rd thing
m contains the following:

[0] is the actual selector called. For example :anyof(“div,4,hr,10″)
[1] is just the selector name. For example: anyof
[2] is the type of quotes used (if any) in the selector. So ” would be returned for :anyof(“div,4,hr,10”)
[3] is any parameters used in the form of a string so “div,4,hr,10” from above

and s contains an array of one or more html elements.

So now we know what is actually returned we can write a cleverer selector…

$.extend($.expr[':'],{
	anyof: function(e,i,m,s) {
		if(m[3].length)
		{
			$(m[3].split(',')).each(function(i2,e2)
			{
				if(parseInt(e2))
				{
					// correct index?
					if(parseInt(e2) == i)
					{
						return true;
					}
				}
				else
				{
					// match selector
					if($(e).is(e2))
					{
						return true;
					}
				}
			});
			return false;
		}
		else
		{
			return false;
		}
	}
});

or if we wanted to check for the unique items in the stack:

$.extend($.expr[':'],{
	isunique: function(e,i,m,s) {
		
		$(s).each(function(i2,e2)
		{
			if($(e2).prop('tagName') == $(e).prop('tagName'))
			{
				return false;
			}
		});

		return true;
	}
});

NB: Since jQuery 1.6 you should use .prop() for tagName not attr()

The above selector will check all other elements in the stack and return only those with a unique tag. We could expand it to check other things about the element to see if it is truly unique such as it’s classes or styles.

NB: I haven’t checked all the code above and certainly haven’t optimised it, but it is likely to work as is or with a small number of fixes.

Grouping by time periods or timeslices in Mysql

Sometimes when you pull back a whole load of results from a database table and order them by their timestamps you might want to either sum any result which happened at a time close to each other or you might want to only bring back one type of something within a period of time to stop that item flooding your results.

I’ve used this technique a couple of time now. The first time was to make lots and lots or data fit onto a relatively small graph, so I needed some form of average. The second time was because if the same event happened in a set period of time I only wanted to know about it once.

The query looks something like this once we include our timeslices / time periods in our select and then follow it up by grouping by them.

SELECT name, 
type, 
value,
ROUND((CEILING(UNIX_TIMESTAMP(`timestamp`) / 600) * 600)) AS timeslice
FROM reading
GROUP BY timeslice

Here I have selected timeslices of 10 minutes (600 seconds) but you can group larger or shorter periods of time if you require them.

So there you have it, grouping by timeslices for summing readings or limiting duplicates in results.

A jQuery plugin for the masses

Every wonder how to hit the ground running with a jQuery plugin? Here is the absolute complete basics for you (and me) to copy and paste…

First we need to define our Class. I’ll call mine Boom.


var Boom = function(element, options){
	this.init('boom', element, options);
};

So now we have something to focus our plugin at we can add a prototype method to it. This makes the ‘new Boom’ code super slick and gives us a better control over the ‘this’ variable because we can whack it inside a function…


Boom.prototype = {

	init:function(type, element, options)
	{
		// do stuff on create

		// makes it available almost anywhere as this.$element
		this.$element = $(element);

		// then maybe do something with the options
	},

	another_one:function()
	{
		// do some other clever stuff.
	}

};

And finally we need to connect it up to the jQuery bit…


$.fn.boom = function (options) {

	// return this makes it chainable
	return this.each(function ()
	{
		new Boom(this, options);
	});
};

$.fn.boom.Constructor = Boom;

Very basic, but worth having here for future use. You can now call it like so…


$('li.bang').boom();

Considering local, dev and live environments

When you develop a website for a client you should usually be considering three or even four (if you also include staging) environments where one code base may be used. Local, development and live.

In this case you may want the code to behave slightly differently depending on where it’s being used.

A good example I regularly come across is when and where to send any emails that a system generates. When you are testing locally or on a development server you certainly don’t want to send out emails to people who have no knowledge of the system.

Continue reading

Code 365

I do some sort of coding pretty much every day. It might be a tweak to a site’s design, some hardcore php Yii coding or even a bit of applescript on my mac, but I usually have something I can write, explain or rant about.

Code 365 is an attempt by me to publish a post to this blog each day through to 1st July 2013. It may be that some posts are very short and I queue several up occasionally, but the plan is generally for one each day.

I’ll be posting them to my @hjbme twitter account if you want to keep up with what I’m banging on about.

So coming up shortly, day 1 of 365. Let me know how I’m getting on.