jQuery-Mobile Plugin Authoring

This HOWTO aims to teach, by example, how to create plugins for jQueryMobile. It is intended to show you the basic flow of the standard plugins by looking at an existing plugin (jQM-Spinbox) and additional bits that may help with larger plugins. As a pre-requisite, you should at least have a passing familiarity with javascript syntax and operation, and the basics of jQuery. This guide is available in both HTML and PDF.


Table of contents:


1. A Few Notes on jQuery Good Practice

Most HOWTO’s that I read give this note section at the end. I’m going to do it up front, with the hope that these will give your code a little speed boost.

1.1. HTML Markup as a String

It is much cheaper to concoct HTML as a string, then convert to jQuery. For instance:

var x = "<div>" + 
  "<h2>A Header</h2>" + 
  "<p>Some text</p>" + 
  "</div>";
x = $(x);

is less compute-intesive than (yes, it is intentionally inefficient, but it’s more the idea..):

var x = $('<div>');
x.append($('<h2>').text('A Header'));
x.append($('<p>').text('Some text'));

1.2. Delay inserting

When you are looping, wait to insert into the DOM - it’s way cheaper to build a big object, and dump it in when your done. Instead of this:

var x = $('<div>').appendTo($('body'));
for ( i = 0; i < 10; i++ ) {
  x.append($('<div>Something</div>'));
}

Try something like this:

var x = $('<div>');
for ( i = 0; i < 10; i++ ) {
  x.append($('<div>Something</div>'));
}
x.appendTo($('body'));

1.3. Be Specific / Caching

When using selectors, cache them if you plan on using them a lot - store it to a variable. For those coming from more structured languages, think of it as a pointer. Be as concise as possible when choosing the initial selectors too. (a/n: There are quite literally hundereds of tutorials out there on how to do this - I won’t repeat them here)

1.4. Lazy operators

When doing comparisons remember:

if ( x === false ) { ... }

is much clearer, and a bit faster than:

if ( x == false ) { ... }

The first version is only true if x is defined, a boolean, and equal to false. The second, x could be empty or zero, and still “work”. Being specific is not only a little quicker, it also prevents edge-case bugs from cropping up later. Also of note is this:

if ( x == undefined ) { ... } 

What if ‘undefined’ gets assigned to something? The entire script can break, or behave oddly. This is however exceedingly clear:

if ( typeof x === 'undefined' ) { ... } 

1.5. Use .remove()

Remove is a powerful, recursive tool. This is lazy, and leaves event listeners and data behind:

$('some element').html('');

1.6. CSS Classes

While very useful .css({}) is expensive. Particularly is used more than once, this is prefered:

$('some element').addClass('someclass');

2. Full Source Code

This is the complete source code of the widget that we will be looking at. It enhances a standard text or number input, with a data-role=”spinbox” tag to include spinbox buttons on either side of the input.

(function($) {
  $.widget( "mobile.spinbox", $.mobile.widget, {
    options: {
      dmin: false,
      dmax: false,
      theme: false,
      initSelector: "input[data-role='spinbox']",
      clickEvent: 'vclick'
    },
    _create: function() {
      var w = this, tmp,
          o = $.extend(
            this.options,
            this.element.jqmData('options')
          ),
          d = {
            input: this.element,
            wrap: this.element
              .wrap(
                '<div style="' +
                'display:inline; ' +
                'white-space:nowrap' +
                '"></div>'
              ).parent()
          };
              
      w.d = d;
          
      if ( o.theme === false ) {
        o.theme = $(this).closest('[data-theme]')
          .attr('data-theme');
          
        if ( typeof o.theme === 'undefined' ) { o.theme = 'c'; }
      }
          
      w.d.input
        .removeClass('ui-input-text ui-corner-all')
        .addClass('ui-slider-input');
          
      if ( o.dmin === false ) { 
        o.dmin = ( typeof w.d.input.attr('min') !== 'undefined' ) 
          ? parseInt(w.d.input.attr('min'),10) 
          : Number.MAX_VALUE * -1; 
      }
      
      if ( o.dmax === false ) {
        o.dmax = ( typeof w.d.input.attr('max') !== 'undefined' ) 
          ? parseInt(w.d.input.attr('max'),10) 
          : Number.MAX_VALUE; 
      }
          
      w.d.up = $('<div>')
        .buttonMarkup({
          icon: 'plus', 
          theme: o.theme, 
          iconpos: 'notext', 
          corners:false, 
          shadow:true, 
          inline:true
        })
        .addClass('ui-corner-right')
        .css({
          'marginLeft':'0px', 
          'marginBottom':'0px', 
          'marginTop':'0px', 
          'paddingLeft':'.4em'
        })
        .appendTo(w.d.wrap);
              
      w.d.down = $('<div>')
        .buttonMarkup({
          icon: 'minus', 
          theme: o.theme, 
          iconpos: 'notext', 
          corners:false, 
          shadow:true, 
          inline:true
        })
        .addClass('ui-corner-left')
        .css({
          'marginRight':'0px', 
          'marginBottom':'0px', 
          'marginTop':'0px', 
          'paddingRight':'.4em'
        })
        .prependTo(w.d.wrap);
              
      w.d.up.on(o.clickEvent, function(e) {
        e.preventDefault();
        if ( !w.disabled ) {
          tmp = parseInt(w.d.input.val(),10) + 1;
          if ( tmp <= o.dmax ) { w.d.input.val(tmp); }
        }
      });
          
      w.d.down.on(o.clickEvent, function(e) {
        e.preventDefault();
        if ( !w.disabled ) {
          tmp = parseInt(w.d.input.val(),10) - 1;
          if ( tmp >= o.dmin ) { w.d.input.val(tmp); }
        }
      });
          
      if ( typeof $.event.special.mousewheel !== 'undefined' ) {
        w.d.input.on('mousewheel', function(e,d) {
          e.preventDefault();
          if ( !w.disabled ) {
            tmp = parseInt(w.d.input.val(),10) + ((d<0)?-1:1);
            if ( tmp >= o.dmin && tmp <= o.dmax ) { 
              w.d.input.val(tmp); 
            }
          }
        });
      }
    },
    disable: function(){
      this.d.input.attr("disabled",true);
      this.d.input.addClass("ui-disabled").blur();
      this.disabled = true;
    },
    enable: function(){
      this.d.input.attr("disabled", false);
      this.d.input.removeClass("ui-disabled");
      this.disabled = false;
    },
  });
    
  $( document ).bind( "pagecreate create", function( e ){
    $.mobile.spinbox.prototype.enhanceWithin( e.target, true );
  });
  
})( jQuery );

3. The Plugin

In this section, we will look at each individual section of the plugin, explaining each part - what the intent is, when it is called, and why certain things are done.

3.1. noConflict Safe Operation

Although it is rarely a problem, occasionally other, non-jQuery scripts may overwrite the ”$” variable. To make sure that this doesn’t break the entire script, we use a closure:

(function($) {
  // All Code Goes here
})( jQuery );

We are simply enclosing a function, defining the variable passed to it as ”$”, and then explicitly passing the “jQuery” object to the function.

3.2. Widget Object Instance

jQuery UI and jQueryMobile have already done a lot of the work for us. So, we simply want to extend the already existing mobile.widget object.

$.widget( "mobile.spinbox", $.mobile.widget, {
  // All widget code goes here.
});

Note the name “mobile.spinbox” - we will use this later to call the widget via $(element).spinbox(); This name can be anything you like, so long as it does not conflict with another widget (A/N: I’m not sure what would happen if it did - Likely the new definition would overwrite the old one, but it’s also possible that it would “merge” the two - at any rate, be careful not to do this)

As you can probably see, the widget is in fact just a giant object - much like jQuery or jQueryMobile is - one monolithic function, with many independant parts.

3.3. The “options” object

The options object is where all of the widget run-time options are stored. This object can be as big or small as you like. In our widget:

options: {
  dmin: false,
  dmax: false,
  theme: false,
  initSelector: "input[data-role='spinbox']",
  clickEvent: 'vclick'
},

The options we defined are as follows:

3.4. The “disable” Function

This function overrides / extends the default “disable” logic for the widget. Here, we can control any internal functions that need to be disabled if the end user chooses to disable the input.

disable: function(){
  this.d.input.attr("disabled",true);
  this.d.input.addClass("ui-disabled").blur();
  this.disabled = true;
},

In this example, we set the “disabled” HTML tag on the input, then add the “.ui-disabled” class to the input, followed by bluring focus on the element. Finally, we set an internal flag named “disabled” to true, which we will check later when operating the widget controls.

3.5. The “enable” Function

This function overides / extends the default “enable” logic for the widget. It should do the opposite of the “disable()” function.

enable: function(){
  this.d.input.attr("disabled", false);
  this.d.input.removeClass("ui-disabled");
  this.disabled = false;
},

Here, we unset the “disabled” HTML tag, remove the “.ui-disabled” class, and finally set the internal “disabled” flag to false.

3.6. The “_create” Function

This function is where the bulk of the work is done. It is called immediately on .spinbox(). In this section, I will explain line-by-line, or section-by-section what is happening. Please refer to the full source code to see it all together.

3.6.1. Standard variables

Here, we set up a few standard variable conventions. First, we store the widget to the “w” variable. Some folks, including the jQM use “self” for this purpose. I use “w” as it is much quicker to type, and I equate “w” with “widget”.

var w = this, tmp,

Next, we want to pull the options into “o”. Also on this line is pulling the options from the element itself. In the next section, we will look at the different ways of setting options.

o = $.extend(this.options, this.element.jqmData('options')),

The “d” object I like to call the “display” object. I try and keep everything about the display contained here. In this object, I define two items:

d = {
  input: this.element,
  wrap: this.element
    .wrap(
      '<div style="display:inline; white-space:nowrap"></div>'
    ).parent()
};

Finally, lets pass the “d”isplay option back to the widget, so we can call it as a sub-item:

w.d = d;

This could also be done with extend, if you had more to pass back - i.e, from DateBox:

$.extend(w, {
  d: d,
  ns: ns,
  drag: drag,
  touch: touch
});

3.6.2. Setting the theme

Next is setting the theme for the control elements. Of course, we’d like to use inheritance as much as posible. If the element has a theme declaration, use that, if not, it’s parents, and so on up the DOM tree. Finally, if no theme has been defined in the document, default back to “c” like the standard controls do. This is where setting the “theme” option to false comes in to play - it allows us to specificly set a theme outside of the standard methods if we have a good reason to do that (for instance, on a per-site basis for these type of inputs).

if ( o.theme === false ) {
  o.theme = $(this).closest('[data-theme]').attr('data-theme');
  if ( typeof o.theme === 'undefined' ) { o.theme = 'c'; }
}

3.6.3. Styling the original element

Next, we wish to style the original element - in this case, we’re going to cheat, and simply use the styles that are defined for the slider input rather than the text input that they already have. We are also going to turn off rounded corners, if they are on

w.d.input
  .removeClass('ui-input-text ui-corner-all')
  .addClass('ui-slider-input');

3.6.4. Setting dmin/dmax

dmin and dmax are our data limits for this widget. In the following two blocks, if the option is still specifically false (see my early note about lazy operators), we want to first check if a “min” or “max” attribute exists on the element. If it does not, we will set a sane default - in this case, +/- MAX_INT (roughly 1.7e308)

if ( o.dmin === false ) { 
  o.dmin = ( typeof w.d.input.attr('min') !== 'undefined' ) 
    ? parseInt(w.d.input.attr('min'),10) 
    : Number.MAX_VALUE * -1; 
}
   
if ( o.dmax === false ) {
  o.dmax = ( typeof w.d.input.attr('max') !== 'undefined' ) 
    ? parseInt(w.d.input.attr('max'),10) 
    : Number.MAX_VALUE; 
}

3.6.5. Generating +/- Buttons

Generating the plus/minus buttons is pretty simple. We need only create a DIV, and give it the appropriate style markup. We also want to only round the outside edge, and remove excess margins so that the buttons line up with the input. Finally, we either append, or prepend it to the wrap surrounding the input.

w.d.up = $('<div>')
  .buttonMarkup({
    icon: 'plus', 
    theme: o.theme, 
    iconpos: 'notext', 
    corners:false, 
    shadow:true, 
    inline:true
  })
  .addClass('ui-corner-right')
  .css({
    'marginLeft':'0px', 
    'marginBottom':'0px', 
    'marginTop':'0px', 
    'paddingLeft':'.4em'
  })
  .appendTo(w.d.wrap);
           
w.d.down = $('<div>')
  .buttonMarkup({
    icon: 'minus', 
    theme: o.theme, 
    iconpos: 'notext', 
    corners:false, 
    shadow:true, 
    inline:true
  })
  .addClass('ui-corner-left')
  .css({
    'marginRight':'0px', 
    'marginBottom':'0px', 
    'marginTop':'0px', 
    'paddingRight':'.4em'
  })
  .prependTo(w.d.wrap);

3.6.6. Make the +/- Buttons work

Next, we need to make the buttons do something. Here we will bind the early defined “clickEvent” to each of them, then process some simple logic. First, we need to stop the click event from performing it’s default action, then we need to check if the “disabled” flag is true - note the we specifically use a lazy operator here, as if neither “enable()” or “disable()” have been called, the flag is not defined. Finally we pull the current value of the input in, parse it to an integer, and if the action won’t make the value invalidate the min/max requirements, push it back into the input.

w.d.up.on(o.clickEvent, function(e) {
  e.preventDefault();
  if ( !w.disabled ) {
    tmp = parseInt(w.d.input.val(),10) + 1;
    if ( tmp <= o.dmax ) { w.d.input.val(tmp); }
  }
});
     
w.d.down.on(o.clickEvent, function(e) {
  e.preventDefault();
  if ( !w.disabled ) {
    tmp = parseInt(w.d.input.val(),10) - 1;
    if ( tmp >= o.dmin ) { w.d.input.val(tmp); }
  }
});

3.6.7. Mousewheel Operation

Brandon Aaron has made a wonderful, simple plugin for mousewheel operation. If we wish to use the mousewheel, and the plugin is loaded, we want to use it. First, we check to make sure it exists, if it does, we bind to the event. When an event happens, the workflow is the same as above - if it’s not disabled, and the value won’t invalidate min/max, then send the new value back to the input.

if ( typeof $.event.special.mousewheel !== 'undefined' ) {
  w.d.input.on('mousewheel', function(e,d) {
    e.preventDefault();
    if ( !w.disabled ) {
      tmp = parseInt(w.d.input.val(),10) + ((d<0)?-1:1);
      if ( tmp >= o.dmin && tmp <= o.dmax ) { 
        w.d.input.val(tmp); 
      }
    }
  });
}

3.7. Auto-Enhance

If we wish to auto-enhance the inputs (and you most likely do, as part of the jQM philosophy is that is “just works”), then we need to add a “pagecreate” listener. Outside of the widget definition, add this:

$( document ).bind( "pagecreate create", function( e ){
  $.mobile.spinbox.prototype.enhanceWithin( e.target, true );
});

Note where the name of the widget goes. Also, “enhanceWithin()” is inherited from the .mobile.widget object.


4. Moving Beyond This Example

This next section deals with some other techniques that I use frequently when developing for jQM - including a number of other nameing conventions that exist in some plugins. Many of these techniques are present in DateBox.

4.1. Parsing options

Parsing options can be done a number of ways.

The first is to use a data-options=’{}’ tag, where a javascript object is passed directly to the widget. The bonus of this method is that it is namespace safe. To read these in, it looks something like this:

this.options = $.extend(
  this.options, 
  this.element.jqmData('options')
);

Next, is the option of doing things on a per-option basis. It looks something like this:

o = $.extend(this.options, {
  icon: this.element.jqmData( "icon" ) || 'default',
  mini: this.element.jqmData( "mini" ) || false
}, options ),

Obviously, the downside is that this takes a lot more code. The bonus is that defaults are here, rather than in the widget wide options object. To use these, it would read data-icon=”something” etc…

Finally, there is this method to read options:

_getLongOptions: function(element) {
  var key, retty = {}, prefix, temp;
  
  if ( $.mobile.ns === "" ) { 
    // This is the prefix name
    prefix = "spinbox";
  } else { 
    // Again, the prefix name - this time capitalized.
    prefix = $.mobile.ns.substr(0, $.mobile.ns.length - 1) 
      + 'Spinbox';
  }

  for ( key in element.data() ) {
    if ( 
      key.substr(0, prefix.length) === prefix && 
      key.length > prefix.length 
    ) {
      temp = key.substr(prefix.length);
      temp = temp.charAt(0).toLowerCase() + temp.slice(1);
      retty[temp] = element.data(key);
    }
  }
  return retty;
},

And somewhere in create:

this.options = $.extend(
  this.options, 
  this._getLongOptions(this.element)
);

This way, you’d pass options like data-spinbox-dmin=”10” etc… It has the bonus of namespacing all your options, with the downside that if you pass a lot of options, it will be much slower than the first method. In DateBox, I fall back on this method only if data-options is not set.

4.2. Making Buttons

Buttons are probably the most used element when playing with widgets. This is a quick reference to the .buttonMarkup() function. These are passed to buttonMarkup as a object:

NameTypeDescription
iconStringName of the icon class
iconposStringPosition of icon, or “notext”
themeStringTheme swatch to use
inlineBoolDisplay button inline
shadowBoolAdd a shadow under the button
cornersBoolRound all corners
iconshadowBoolShow shadow under icon
miniBoolUse mini version
$('<div>').buttonMarkup({
  icon: 'minus', 
  theme: o.theme, 
  iconpos: 'notext', 
  corners:false, 
  shadow:true, 
  inline:true
})

4.3. Naming Conventions

Some nameing conventions exist to ease the development of plugins. They are:

4.3.1. open() Function

This function is typically used to open a dialog or popup.

4.3.2. close() Function

This function is typically used to close a dialog or popup.

4.3.3. refresh() Function

This function is typically used to refresh the controls that the widget created. For instance, in the listview plugin, this will re-read the list, and add the markup for new items.

4.3.4. destroy() Function

This function is called when the widget is destroyed (on page close if not caching for instance). It is usually used to remove data structures that might exist in the DOM independantly from the widget data. In DateBox, this is used to remove the created dialog page, which would otherwise be left.

4.3.5. _init() Function

This function is called immediatly after _create(). Typically, I’ve seen it used when a widget can be re-initialized. If you put all of the things that refresh() needed to do here, you could just point refresh() to init for instance.

4.4. Enviroment Checking

These are a couple of enviroment settings that you might need to look at from time to time.

4.4.1. Touchscreen support

To check if the widget is running on a touchscreen, check for ontouchstart support:

touch = (typeof window.ontouchstart !== 'undefined');

Some sane events with touch vs. mouse for drag support might be:

DragStart = (touch ? 'touchstart' : 'mousedown');
DragMove =  (touch ? 'touchmove' : 'mousemove');
DragEnd =   (touch ? 'touchend' : 'mouseup');

4.4.2. Namespace detection

The check if jQM is namespaced, just look at mobile.ns:

ns = (typeof $.mobile.ns !== 'undefined') ? $.mobile.ns : '';

4.4.3. The Active Page

Getting the active page is pretty easy. The safe way to do it is:

thePage = $.mobile.activePage;

Other methods, such as looking for .closest(‘[data-role=”page”]’) may be less reliable.

4.5. The apply() function

I had a hard time finding documentation on this - basically, if you want to call a function in your widget later, the context of “this” can change. Using apply can get around this.

w.somefunction.apply(w, arguments);

This will call somefunction(), with “arguments”, and make sure than when it runs the context of “this” will be your widget - which is what we have been assuming in everything I’ve shown so far.

4.5.1. Edge Case - apply() with setTimeout()

Often, the issue of “this” context comes up with setTimeout. Namely, you get an error that this.function() is undefined. Here’s a workaround:

setTimeout(function(that) { 
  return function () { that.destroy(); };
}(this), 1000);

This would call “this.destroy()” 1 second after execution.


5. A Few More Things

These next bits provide a bit more information on some ways that I’ve done things in the past. They are not strictly jQM specific, but have proven useful to me - enough that I wish to document how I did it.

5.1. Overloading Options

Lets say that you have an option that you wish to be able to provide a list of options to - which we did above for the options object in a variety of ways. But another useful feature might be to provide a “shortcut” if the defaults that you’ve defined are almost always sane. I ran across this issue when I wanted to provide a set of buttons, each with an associated function. (a/n: This came up in SimpleDialog To this end, I decided that ordinarily I’d only provide the function, but just sometimes, I’d also want to add other options. This is what I came up with:

props = $.isFunction( props ) ? { click: props } : props;
props = $.extend({
  text   : name,
  id     : name + self.internalID,
  theme  : o.themeButtonDefault,
  icon   : 'check',
  iconpos: 'left',
  corners: 'true',
  shadow : 'true',
  args   : [],
  close  : true
}, props);

The first bit checks to see if I have given a function - If I have, we need to store it in a sub-key of the props object:

props = $.isFunction( props ) ? { click: props } : props;

The next statement gives all of the sane defaults for all of the other possible options. So, I could call this one of two ways:

'Button': function () { return true; }

OR

'Button': {
  click: function() { return true; },
  icon: 'plus',
  //etc...
}

This is exactly how the buttons are defined in SimpleDialog.

5.2. The DateBox i18n System

One of the bits of DateBox I am particularly proud of is the adaptive i18n system. While the backend generation of the i18n files (which uses crowdin and gettext extensivly) is a bit beyond the scope of this, the implementation in the script is now. When DateBox decides it needs to output a string of text, it goes through a process:

  1. First, If an overrideStringName exists, use it
  2. Next, use the ‘lang’ specific stringName value
  3. Finally, use the default stringName value

To do this, we need to define a few data structures in the “options” object:

useLang: 'en', 
// The "default" language name
// which can be overriden later.

lang: {
  'en': {
    'hello': 'hello"
  }
}

If we wished to define a second language, in some other file, we need only:

jQuery.extend(
  jQuery.mobile.<plugin name>.prototype.options.lang,
  {
    'es': {
      'hello': 'hola'
    }
  }
);

And setting that language active would be as simple as passing in useLang=”es” as an option. The interesting bit is how we make JavaScript behave as if it is gettext aware. First, we define a function to do it:

__ : function(val) {
  var o = this.options,
    oride = 'override' + 
      val.charAt(0).toUpperCase() + 
      val.slice(1);
    
  if ( typeof o[oride] !== 'undefined' ) { 
    return o[oride];
  }
  
  if ( typeof o.lang[o.useLang][val] !== 'undefined' ) { 
    return o.lang[o.useLang][val]; 
  }
  
  return o.lang['default'][val];
}

Then, to use it in the plugin itself, we would do something like this:

something = $('<div>' + w.__('hello') + '</div>');
something.appendTo(somethingElse);

Which would result in a translated version of “hello” in your plugin. Of course, I would be remiss in not mentioning that this can only be effective with a wonderful group of translators (which thankfully, DateBox has).

5.3. Callback Functions

One of the early requests for both of my plugins was the ability to supply a callback to be run on either open or close (or really any other time you want - you could place it anywhere you like). Of course, with the nature of inline options that we use, passing a function to the widget was not the easiest thing in the world. So, I came up with this spiffy little bit of code: (a/n: When I say I, I mean myself a few other folks who helped out on the idea). The option name is “openCallback”, and it has a number of modes of operation. First, the code block:

if ( o.openCallback !== false ) {
  if ( ! $.isFunction(o.openCallback) ) {
    if ( typeof window[o.openCallback] !== 'undefined' ) {
      o.openCallback = window[o.openCallback];
    } else {
      o.openCallback = new Function(o.openCallback);
    }
  }
  o.openCallback.apply(w, o.openCallbackArgs);
}

First, if the option is set (not false), and it is a function, we just run it. Easy enough, and it allows us to pass the function in via an earlier code block. Next, we need to deal with the case that we have just passed in the name of a function:

if ( typeof window[o.openCallback] !== 'undefined' ) {
  o.openCallback = window[o.openCallback];
}

Here, we check if the string that was passed to the option exists in the global window. Now, we are assuming it is a function, which might be dangerous, but that is the sort of problem that would be immediatly apparent in the testing phase. Next:

else {
  o.openCallback = new Function(o.openCallback);
}

This decides that if it is just a string, it’s probably just a stringified function - so lets convert it to a function. Again, jsLint will argue with us here, as “eval” is evil - in this case, it’s a nessesary evil. Finally, lets run the function:

o.openCallback.apply(w, o.openCallbackArgs);

This calls the function, in the context of the widget, with the option “openCallBackArgs” (an array or a single item) as the list of arguments.

5.4. Automatic Titles

Sometimes, we need to title our work in some way - and often on form elements, we already have done this - either by providing a title attribute, or maybe setting a label. Here is how DateBox does this:

_grabLabel: function() {
  var w = this, tmp,
    id = w.d.input.attr('id'),
    page = $.mobile.activePage,
    o = this.options;
    
  if ( typeof o.overrideDialogLabel === 'undefined' ) {
  
    tmp = w.d.input.attr('title');
    if ( typeof tmp !== 'undefined' ) { return tmp; }
    
    tmp = page.find('label[for='+id+']').text();
    if (  tmp !== '' ) { return tmp; }
    
    return false;
  }
  return o.overrideDialogLabel;
}

First, we check to see if there is already an overrideDialogLabel option defined - which would allow us to specify a label if we wanted. Next, let’s see if there is a title:

tmp = w.d.input.attr('title');
if ( typeof tmp !== 'undefined' ) { return tmp; }

Last, we check to see if there is a label associated with the id of the element:

tmp = page.find('label[for='+id+']').text();
if (  tmp !== '' ) { return tmp; }

Finally, the function returns false if none of these are available.


6. Credits and Closing

That’s all I have right now. If you think something should be added, please either feel free to add it yourself, or shoot me an e-mail with the subject and I’ll see what I can do.

Currently all written by J.T.Sage. If you contribute, please add your name here.