Posts Tagged ‘jquery’

jQuery listSplitter plugin

Monday, February 1st, 2010

A very short post to announce my third  jQuery plugin: listSplitter, which takes a long list of categorised items (where the categories can overlap) and creates a tabbed interface to show only one category at once. I haven’t done a demo yet (well, I have, buthaving teh same old problem transferring to the server as the server runs on UNIX while my laptop is Windows. I recently found out why this causes a problem, but no easy fix has presented itself) so you’ll just half to take my word for it, though before too many months have gone by I will use it as the basis for a new portfolio.

*edit: Here is a demo

And it can be used with jQuery themeroller, i.e. use the tool at jqueryui.com/themeroller, to design how it shoudl look, then after clicking “download theme” make sure you have ‘tabs’ ticked underneath widgets.

My jQuery plugin writing tips

Wednesday, September 2nd, 2009

I’ve now written two jQuery plugins, and have a pretty good idea for a third. As you might expect, I’ve learned quite a few things along the way. When I started there was a lot I didn’t know about javascript and jQuery. There still is, but one thing that struck me is that there are a lot of great tutorials on writing jQuery plugins out there but none of them individually captures all the things that took my plugins from being nice ideas with mediocre implementations to being something I’m proud of.

So here is my definitive combination of them all, together with one or two of my own contributions (a bit scant on detail, but follow the links for more info).

1. Build a good test page

My test page for any given plugin has 3 instances of a type of element the plugin is suppose to effect. eg a list. They are marked up as follows:

<ul class="single" >...</ul>
<ul class="multiple" >...</ul>
<ul class="multiple" >...</ul>

And I run the following script in the document head

$(document).ready(function() {
  $('.single').myPlugin();
  $('.multiple').myPlugin();
});

This will make sure your plugin works when called more than once on a page, and when one instance affects more than one element. For testing your code for speed the same page with various bits of html copied lots of times does the trick.

2. Follow jQuery’s recommended tutorial

It really is very good, and covers most important points. The other items on the list are either little tweaks that I think make the code neater or, in just one or two cases (highlighted in red), things its missed out. Read the tutorial

3. Make sure your plugin won’t conflict with other javascript libraries

The most effective way to do this is to wrap your whole plugin in a function where $ is its parameter, and then call it on jQuery, like so:

(function($) {
  $.fn.pluginName = function(options) { ... };
})(jQuery);

More info

4. Make your plugin’s settings changeable, both when the plugin is called, and by tweaking the default settings

The way to do this is to set up your plugin’s defaults as a JSON which is a public property of the plugin object. So within your plugin add these lines (where options is the name of the JSON passed to the plugin as a parameter).

this.defaults = {parameter:'value', ...};
options = $.extend(defaults,options);

More info

5. Make sure your plugin keeps track of DOM elements effectively

Structure your code as follows in order to make sure your variables are accessible to the right instances of the plugin, but aren’t stored in memory longer than they need to be.

$.fn.pluginName = function(options) {
  var constructor1, constructor2, ...;//elements that are used only when the plugIn is constructing instances of itself at the start and play no role in handling later events;
  return this.each(function() {
     var tracker1, tracker2, ...;//elements that are important for events and other later changes. Also, if your plugin's constructor involves some AJAX it may be best to put all variables in here
     subFunction() {
	//this should always be able to call the variables it needs, in particular access the right DOM elements
     }
  });
};

6. Optimise, opimise, optimise

You never know how demanding the website using your plugin is going to be: how many times is it going to be called on one page; will the dom objects (eg sorting a list of items) it has to handle be a lot bigger than the ones you’ve tested it on. My crossSelect plugin was, I discovered, before version 4/5, really sluggish on lists larger than about 20 items.

So I found a great list of techniques to optimise your code, the most pertinent to writing plugins I have put below:

  • Make sure your optimisations are optimisations

    To keep track of whether your optimisations are having the desired effect, run your plugin on a page full of huge elements for it to process and then use firebug to measure the time it takes.

  • Don’t over-query the DOM

    jQuery is principallya tool for accessing and manipulating the DOM, but it doesn’t pay to do this too often as your code will be a lot quicker if you keep it to a minimum. Techniques for avoiding this include:

    • store DOM elements you know you will have to access a few times in variables (but be careful – see point 5)
    • Add new DOM elements as html, . So instead of
      $('selector').append('<ul>');
      for(i=0;i<limit;i++)
      {
        $('selector > ul').append('<li>'+i-th text+'<li>');
      }

      use

      string = '<ul>';
      for(i=0;i<limit;i++)
      {
        string+='<li>'+i-th text+'<li>';
      }
      $('selector').append(string);

      The improvement in speed of code is astounding.

    • Use contexts. A lot. It will both reduce code size (as shown below) and speed up the code (because jQuery only has to search a subset of the DOM). So instead of
      var theContainer = $('selector');
      $(theContainer).children('ul').children('li').aMethod();

      use

      var theContainer = $('selector');
      $('ul>li', theContainer).aMethod();

And that’s yer lot. Please let me know if you have any comments or suggested additions.

jQuery.each() for single objects

Thursday, July 9th, 2009

While refining  jQuery.crossselect.js recently I was briefly faced with a problem which often rears its ugly head, though this time I found a solution.

Consider a function alters_item(), which can be applied to certain DOM elements. Further, consider that it can be triggered in two distinct ways:

  1. By a click (or other event) on the item to be altered, so the DOM element is the context and can be accessed via the pseudo-variable this
  2. Just applied like a normal javascript function, which means the DOM element needs to be passed in as a parameter, i.e. you need to call the function using alters_item(element)

So to have a function usable in both circumstances I would write some conditionals at the start which check if an argument has been passed, if it hasn’t then set var element = this, etc…

But there is another way.

For the second case instead of

alters_item(element)

we can write

$(element).each(alters_item)

because jQuery.each works even on jQueries that return only one object.

Doing this is a bit of a trade off – the second line of code I bet takes measurably longer to execute, but it does mean my functions get to be simpler, so it’s my weapon of choice at the moment.

But it does make me think that jQuery should have a call() method, that runs a function on an object, but also setting the object as the context.

Incidentally, if anyone knows of a better way of dealng with this isue than the one I’ve found, please let me know.

Optimize how?

Thursday, July 2nd, 2009

Yesterday and today I’ve been rewriting my jQuery crossSelect plugin (probably over half of the code has changed) to; a) Fix the serious bugs brought about by trying to bring my plugin closer in line with how it’s supposed to be done, without fully understanding the implications in advance; b) make the code more efficient, in part applying the ideas in this excellent article; and c) prepare the code for bringing in more functionality in later releases.

With regard to c), the main thing I needed to do was rewrite all my selection and removal functions so that moving many items into the selected column at once could just be the move one item function iterated a number of times. I’ve now ( I think) found a pretty efficient solution (each move many function is only 3 lines long), but along the way I came across an interesting dilemma.

My selectOne() function essentially moved a list item and then checks how many items are in each list before adjusting the buttons appropriately. Now, to do a selectAll() or a selectMany() the obvious thing to do is just to iterate that selectOne() function over all list items – just a handful of lines of code – … but this unfortunately leads to a less efficient (and probably slower) function. Writing a selectAll()/selectMany() function from scratch would enable me to only adjust the buttons once and, in the selectAll() case, not have to care about tracking which list item I’m dealing with as they all get moved over in the end… but this way would not only be less elegant, I feel, but also lead to more lines of code.

I’d always assumed that optimising code meant two things – faster and smaller – and I’d always thought that one more or less implies the other. Turns out I was wrong.

In the end, the escape from this trade off involved removing the button adjustment from selectOne() and putting it in selectNow(), a new function triggered by a click. But this required a feature of jQuery which I’ll talk about in some other post.

It’s all about context

Saturday, May 30th, 2009

One big selling point of jQuery – so everyone says – is the ability to nest/chain selectors, so that using oen string you can, in theory, pick out any element in the DOM. But this has eluded me somewhat until today; until I realised the usefulness of setting a context for jQuery selectors.

I noticed a few days ago that my new crossSelect plugin was a bit inefficient, in that it queried the DOM far too often. It used to have something like the following code:

$(this).parent().parent().find('.target1').children('li')...
$(this).parent().parent().find('.target2').children('li')...

But the next iteration was going to have something like this instead – far sleeker.

var context = $(this).parent().parent()
$(context).find('.target1').children('li')...
$(context).find('.target2').children('li')...

But it bugged me that I couldn’t pick out the <li>’s with a string selector. This is because context is an object, not a string, and therefore can’t be a part of a selector string. But then I discovered setting the context on selectors (easy to miss as it’s not linked to from the selectors bit of the documentation site). So that led to the following:

$(context).find('.target2').children('li');

is equivalent to

$('.target2', context).children('li');

which can be rewritten

$('.target2 > li', context);

Far more elegant, I think you’ll agree. So it’s jQuery context setting all the way for me. From now on I will only use find when I need to find a child element on the same line that I’ve already done something to the parent; it really has no place at the begining of a line of jQuery code.

crossSelect jQuery plugin

Wednesday, May 27th, 2009

I thought I should write a short post to say I have written another jQuery plugin, for making multiple select form elements more intuitive to use. It’s called crossSelect, and here is a demo.

One of these days I will make a proper portfolio site/subsite which collects all the stuff I make together.

I will, I will, I will!

In defence of jQuery browser detection

Friday, May 22nd, 2009

I read somewhere the other week that jQuery is deprecating its jQuery.browser method, which means that in the future (though not yet, as the method still works but will not continue to be developed/supported, and will eventually be dropped) you will not be able to directly ask jQuery which browser it is being run in.

The rationale behind doing this is on the face of it sound: The reason you want to check what the browser is is that you want to check if a feature of javascript is available in that browser… so why not just ask if that feature is available, then the question of which browser it is becomes irrelevant. This will enable your script to continue to function (most probably) even in the unlikely event that eg Microsoft bring out a patch for ie6 that makes it fully support javascript.

But there is a flaw in this reasoning. It assumes that the only reason you would want to serve some different script to a browser is because of flaws in the browser’s javascript implementation, but in fact there are other reasons for doing this too, principally the browser’s failure to render CSS correctly.

While the new jQuery.support method does include tests for boxModel (ie6 and 7’s incorrect rendering of the CSS box model being the principle reason behind serving different CSS to different browsers) and one or two other CSS bugs, this simply isn’t up to the task. The box model is broken in ie6 and ie7, but broken in different ways, so normally to fix a layout bug I will want to serve different CSS to both.

Using various combinations of jQuery.support, eg

!jQuery.support.boxModel && jQuery.support.objectAll

you can still detect ie6 and ie7… but is this really an improvement on jQuery.browser?

jQuery.browser.msie && jQuery.browser.version.substr(0,1)=="6"

With jQuery.browser it is wholly transparent what you’re trying to detect, and if the code within the conditional’s braces contains a hefty amount of CSS rules your average developer should be able to work out the reason for the browser detection is to fix CSS bugs.

One other reason for explicitly detecting a browser (which I need at the moment as I’m developing a jQuery plugin) is that different browsers render form elements differently (the most obvious black sheep is safari’s making most elements look blue and glossy, but there are subtle differences in other browsers too). If I, say, want to fake a <select> element  by using a <ul>, and want it to look convincing in all browsers I need to supply each browser with differentiated CSS. jQuery.browser would be the obvious way to do this. No matter how many CSS bugs get fixed in browsers, how to render form elements by default isn’t specified by CSS/html, so there will always be this reason to want to detect browsers.

jQuery.browser is a very useful feature and it’ll be a shame when it goes.

fullTextArea jQuery plug-in

Friday, April 17th, 2009

I’ve just written it, and have to provide a homepage for it. will add more details later.

Demo: fulltextarea.html

Current release

jqueryfulltextarea031

Release details:

  • Tidied up and optimised code

Previous versions

Better jQuery plugins search

Tuesday, April 14th, 2009

Everyone wants one and, let’s face it, anyone cool already seems to have one.

I’m talking of course about overlays, or modal windows, those dialog boxes that grey out most of the browser screen to focus your attention on a  short but important form that needs completing, or to show you an image gallery, or basically make things which could look cooler, look cooler.

So I needed to make one of these for work, and off I went to the jQuery site to search its plug-ins.

However, to give an indication of how good the jQuery site search is, the top result for “overlay” is

IE6 crashing when you click an image

… and this is when the search is restricted to the plug-ins subsite!

So I did what any other person who is me would do in a situation like this – I searched Google, restricting the search to site:plugins/jquery.com/project, and was so impressed with the results that I created the Better jQuery plugins search.

jNice styled forms fix for select change event

Saturday, March 21st, 2009

I’m working so can’t be bothered writing out in full, but in case anyone else spends hours trying to work out why their fancy looking jQuery select boxes don’t seem to have a change event, add this line near the end of the selectUpdate function, just before return false;

$select.trigger(“change”);