Posts Tagged ‘javascript’

Interaction classes – seperating CSS styles from Javascript interactions

Something I’ve been doing for a while in my web development work is applying separate classes, interaction classes, to DOM elements that interact with Javascript. Basically, an interaction class is applied to any DOM element touched by Javascript code – an element bound to an event handler, an input element with it’s value being read or written, an element selected for animation, etc. The goal being to de-couple styling from interaction, allowing style changes to not interfere with JS code, and vice-versa.

Below is a bit of code to demonstrate what I’m talking about. As a convention, I apply a “ia-” prefix to my interaction classes.

<a href="#" class="btn-primary ia-begin-testing">Begin Testing</a>
  • btn-primary has the CSS rules for styling the anchor as a button
  • ia-begin-testing is bound to a JS event that triggers some arbitrary “begin testing” action

If the future, if I want to change the button to a link (remove the btn-primary class), change it to a secondary button (btn-primary to btn-secondary), or change styling in any other way, the Javscript code is unaffected and requires no changes.

In addition, the ia-begin-testing class can also be applied to other elements (another button, a link, an anchor wrapping an image, etc.) and is automatically bound to the same interaction functions, without writing additional JS DOM selection code. The ia-begin-testing class can also be removed, or changed to another interaction class, and the styling on the button remains the same.

While IDs and data attributes are also good choices for architecting this sort of style/interaction separation, I like classes for 2 reasons:

  • Selection via class is relatively fast across all browsers compared to selection via data attributes
  • Compared to IDs, classes can be re-used allowing the same interactions to be shared by multiple elements (e.g. a button and a link can both trigger the same function)

One of the reasons I wrote this post is as an alternative to the the “grouping of selectors” approach presented in Chris Coyier’s Can You “Over Organize” JavaScript? article. With interaction classes, there’s little need for grouping selectors. Aside from the benefit of de-coupling styling and interaction, you get the advantage of a single class (ia-whatever), on whatever and however many elements, mapping all said elements to their necessary JS functions. With grouping of selectors, some sanity is brought to the scattered DOM selection code, but you’re left with the burden of maintaining a pool of different element IDs, classes, etc.; a chore that only gets harder as the codebase grows and changes.

Notes on web workers

Recently I’ve been working on parallelizing some HTML5 canvas image processing code with web workers and hit a few stumbling blocks, mostly due to some unexpected limits and misunderstandings about workers. Below are some notes I’ve jotted down while working.

  • There’s a limit on the number of web workers being executed, with Firefox I hit a limit of 20.
  • With Firefox, there’s no warning when you hit the limit, your worker simply doesn’t run. There’s no queuing with workers either, so you can’t fire off a worker and expect the browser to schedule it to run when you have less than the maximum number of workers executing.
  • I attempted to implement my own queuing mechanism, but this was a waste of time. You can’t precisely predict when the browser will delete the worker. I tried posting a message from the worker thread to the caller (via postMessage) at the end of the worker onmessage processing function, but this only accurately signals that the processing is done, not that the worker is terminated, and you therefore can’t predict when it’s possible to spawn off another worker.
    EDIT: Experimenting a bit more, I’ve discovered this to be incorrect. To actually terminate the worker, you can use the close method from within the worker, or the terminate method from the caller, so a queuing mechanism is a feasible solution to deal with browser limits on the number of executing web workers.
  • When posting a message from worker to caller (via postMessage), the handler function within the caller seems to be executed within the worker thread.
  • You can’t pass a function to a worker. You pass data between worker and caller by copying the data (serializing/de-serializing an object by structured cloning) or giving ownership of the object to the worker (transferable objects). This allows for a great deal of thread-safety, but in a language like Javascript where functions are first-class citizens, it’s hard not to see the elegance of being able to simply spawn off a worker from a function.
  • Given an upper limit on the number of executing web workers, a spawn-and-forget (or spawn-and-wait) model for concurrency is not practical. A thread pool pattern may be appropriate. In general, each worker should be thought of as a heavy-weight processing engine.
    EDIT:This is perhaps more true not because of browser limits on the number of workers, but the fact that workers were designed with the expectation of them being long-lived, with high performance and memory costs.

All my work was in Firefox, so the points above may not necessarily be true for other browsers.

A better toggle button, with jQuery and CSS3

One of my more popular articles on this blog has been my jQuery toggle button article. However, my thinking has changed a lot since writing that post and, approaching the problem now, I’d likely do things differently. There were a few key flaws in my approach:

  • Using an additional image for the border. border-radius was still new at the time, but even so I think it would have been better to develop for the bleeding edge, and then deal with fallback for older tech.
  • Using jQuery animate. While I like Javascript as a control mechanism for animations, Javascript-based animations are another matter entirely. CSS3 transitions and transformations are a much better solution, they’re easier to handle and performance is, almost always, much better. Again, this was because I was avoiding the bleeding edge and focusing too much on backward compatibility, as transition and transform were still very new at the time.
  • Not making it a jQuery plugin. There was so much of a dependency on jQuery for selection, modifying the DOM, etc. that wrapping everything in a jQuery plugin made sense. My thinking was always that by not being bound too closely to jQuery, you can always just rip-out the jQuery bits and have a slim, pure JS component. However, there are 2 problems with this idea:
    • it’s not always that easy to rip-out jQuery, and doing so usually means re-creating much of the abstraction and utility functions offered by jQuery
    • plugin or not, you can still end up coupling heavily with jQuery
  • Widgetizing the HTML/CSS. The flaw here is writing out HTML and inline CSS from a Javascript string to create the button, which is usually not a good idea. It’s usually better to leave markup and styling in the document, where they can be readily manipulated or re-style. This is in line with my rant about Enyo (and widget frameworks in general), where I mentioned that widgets lead to a significant loss of flexibility. There is a case to be made for a toggle button widget, as it enables you to automatically replace checkboxes with toggle buttons without modifying any HTML but, in the general, I don’t think it’s worth it.

Below is an updated toggle button accompanied by the code that creates it. As in my previous post, the button itself is a single image, applied as a background-image to an anchor tag, and state transitions are done by shifting the background-position of the element.

Live Demo

HTML

<!-- toggle button markup -->
<a id="btn-toggle" class="btn-toggle" href="#">
    <
input name="yesno" type="checkbox" checked="checked" />
</
a>

CSS

/* toggle button style, styled for initial state (off) */
a.btn-toggle { margin:0; padding:0; display:block; border-radius:32px; background:url(base.png) -57px 0px no-repeat transparent; width:98px; height:64px; }

/* toggle button active state (on) */
a.btn-toggle.active { background-position:-7px 0px; border-color:#40A1EC; }

/* hide the underlying input checkbox of the toggle button */
a.btn-toggle input { display:none; }

Javascript

// jQuery plugin for toggle button
(function( $ ){

    $.fn.makeToggleButton =
function() {
        
    
this.each(function() {
            
            
var elem = $(this);
                    
            
// get the state of the underlying input checkbox
            
elem.val = function() {
                
return elem.find('input').is(':checked');
            }
                    
            
// function to toggle button state
            
elem.toggle = function() {
                    
                
var chkbx = elem.find('input');                    
                
if (!chkbx.is(':checked')) { // not check, switch on
                        
                    
elem.addClass('active');
                    chkbx.attr(
'checked', true);
                }
                
else {

                    elem.removeClass(
'active');
                    chkbx.attr(
'checked', false);
                }
            }
                    
            
// click handler
            
elem.click(function(e) {                    
                elem.toggle();
                e.preventDefault();                    
            });
                    
            
// adjust state to initial value of input checkbox
            
if(elem.find('input').is(':checked'))
            {
                elem.addClass(
'active');
            }
                    
            
// setTimeout to prevent transition upon setting initial state to active
            
setTimeout(function() {
                elem.css(
'transition', 'background-position 0.4s');
                elem.css(
'-webkit-transition', 'background-position 0.4s');
                elem.css(
'-moz-transition', 'background-position 0.4s');
                elem.css(
'-o-transition', 'background-position 0.4s');
                elem.css(
'-ms-transition', 'background-position 0.4s');
            }, 50 );
                
        });


    
return this;
            
    };
})( jQuery );        


// make toggle button when document is ready
$(document).ready(function() {
    $(
'#btn-toggle').makeToggleButton();
});

 

SVG Animations

I decided to play around a bit with animating SVG content. There are actually multiple ways to animate SVG: CSS (transition, transform, @keyframes), Javascript, or Synchronized Multimedia Integration Language (SMIL). Where possible, I tend to prefer Javascript, as you have far more flexibility compared to markup languages; however, out of curiosity, I did try my hand at SMIL as well.

Method 1: Javascript

Here’s the SVG markup for the object being animated.

<svg version="1.1" x="0px" y="0px" width="74px" height="74px" viewBox="-1.751 -1.751 74 74" enable-background="new -1.751 -1.751 74 74" xml:space="preserve">
    <
g id="circularMarker" transform="rotate(0 35.163 35.521)">                
        <
path fill="#27AAE1" d="M46.336,7.908c2.174,0.678,4.236,1.538,6.199,2.54V3.69c-1.417-0.607-2.875-1.146-4.377-1.616
            C37.794-1.161,27.147-0.491,17.793,3.242v6.663C26.41,5.825,36.518,4.843,46.336,7.908z"/>
        <
path fill="#27AAE1" d="M62.423,46.338c-0.679,2.173-1.537,4.236-2.538,6.199h6.757c0.605-1.417,1.147-2.877,1.615-4.382
            c3.235-10.364,2.564-21.006-1.167-30.362h-6.664C64.505,26.41,65.489,36.52,62.423,46.338z"/>
        <
path fill="#27AAE1" d="M23.992,62.42c-2.171-0.678-4.236-1.536-6.199-2.538v6.759c1.418,0.604,2.877,1.146,4.381,1.616
            c10.364,3.233,21.009,2.564,30.362-1.17v-6.664C43.921,64.505,33.81,65.488,23.992,62.42z"/>
        <
path fill="#27AAE1" d="M7.909,23.994c0.678-2.174,1.538-4.237,2.538-6.2H3.691c-0.606,1.416-1.147,2.878-1.617,4.38
            c-3.234,10.364-2.564,21.012,1.168,30.365h6.664C5.825,43.921,4.843,33.813,7.909,23.994z"/>
        <
circle fill="#00AEEF" cx="35.163" cy="35.521" r="11.331"/>
    </
g>
</
svg>    

To animate, the transform attribute on the #circularMarker group element is updated every frame to do a simple rotation at a rate of 0.275 deg/ms. You can see the result in the iframe below.

Here’s the Javascript code that makes it happen:

<script src="jquery-1.8.2.min.js" type="text/javascript"></script>
<
script type="text/javascript">

// setup window.requestAnimationFrame
(function ()
{
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
                                 window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
window.requestAnimationFrame = requestAnimationFrame;
})();


// set initial frame time (in milliseconds)
var ft1 = new Date().getTime();

// set initial angle (in degrees)
var angleDeg = 0;

// function to rotate circle
function rotateCircularMarker()
{
var ft = new Date().getTime();
var ftDelta = ft - ft1;
ft1 = ft;

// rotate at a rate of 0.275 deg/ms
angleDeg += ftDelta * 0.275;

if (angleDeg >= 360) {
// full circle!, reset angleDeg
angleDeg = 0;
}

// transform the #circularMarker group
// Note: the rotation is about the center of the circle element (35.163, 35.521)
$('#circularMarker').attr('transform', 'rotate(' + angleDeg + ' 35.163 35.521)');

// call requestAnimationFrame to continue animating
requestAnimationFrame(rotateCircularMarker);
}

$(document).ready(
function ()
{

rotateCircularMarker();

});
            
</script>

Note that window.requestAnimationFrame is used, so a modern browser is required. For older browsers it is possible to use window.setInterval as a fallback.

Method 2: SMIL

With SMIL, the SVG code remains the same, with the exception of an animateTransform tag within the #circularMarker group element.

<animateTransform attributeName="transform" attributeType="XML" type="rotate" from="0 35.163 35.521" to="360 35.163 35.521" begin="0s" dur="1.336996s" repeatCount="indefinite"/>

The attributes of animateTransform describe the animation, mainly type, from, to, begin, dur, and repeatCount.

Obviously SMIL yields less code and removes all Javascript dependencies, but it does come at the cost of losing flexibility (as you can only perform transformations and timing operations supported by SMIL attributes) and having to learn yet another markup language.

Circular stipple patterns on an HTML5 canvas

In my previous post on stipple patterns, I presented code to draw a few simple stipple patterns based on drawing single pixels at fixed locations. In this post, I’ll present something just a bit more complex: drawing circles to create a circular stipple patten, again writing a shader that makes use of the GraphicsCore and FXController classes.

Shader.circleStippleShader = function (imageData, bufWrite, index, x, y, r, g, b, a, passNum, frameNum, maxFrames)
{
var alpha = 1.0;
var r1 = r / 255.0;
var rF = Math.floor((alpha * r1 + (1.0 - alpha)) * 255.0);

var circleMaxDiam = 12; // circle at every 12th pixel, also defines max diameter of circle,
var circleMaxRadius = circleMaxDiam / 2; // maximum radius of circle

// figure out the x, y indices of the circle we're within
// x,y need to be shifted by the circle radius b/c circleMaxDiam defined the offset of the circle center
// ... e.g. going along the x-axis, we are within the next circle not at x/circleMaxDiam, but at (x+6)/circleMaxDiam
var iX = Math.floor((x + circleMaxRadius) / circleMaxDiam);
var iY = Math.floor((y + circleMaxRadius) / circleMaxDiam);

// multiply the circle indices by the diameter to get the actual coordinates of the circle's center
var targetX = iX * circleMaxDiam;
var targetY = iY * circleMaxDiam;

// calculate squared distance to the circle we are within
var dist = (targetX - x) * (targetX - x) + (targetY - y) * (targetY - y);

if (dist < 25) {
GraphicsCore.setPixel(bufWrite, index, rF, 0, 0, 255);
}
else {
GraphicsCore.setPixel(bufWrite, index, r, g, b, 255);
}

}
Shader.circleStippleShader.numPassesRequired = 1;    

Circle stipple

Conceptually, we define the center of a circle at every 12th pixel (both along the x and y axis). At every pixel (x,y) we figure out which circle we are within, and calculate the distance to the center. If the distance is less than our threshold (25), we change the color of the pixel (use the red channel only).

Stipple patterns on an HTML5 canvas

I wanted to play around a bit with stipple patterns after seeing stippling done with photos on the LinkedIn news feed. However, what I’m going to present is not what LinkedIn does. LinkedIn applies the stipple pattern as a background-image on a DOM element above a <img> element with a (fairly low resolution) JPEG – the stippling may help to alleviate the negative visual impact of the low-resolution image. What I’m going to show is how to do stippling on an HTML5 canvas, which allows for a much greater degree of freedom in terms of what’s possible, but is also slower and requires a modern browser.

I’m going to make use of the GraphicsCore and FXController classes in a previous post, Gaussian blur on an HTML5 canvas. In that post I presented the concept of writing shaders as plug-in to the FXController class to apply different per-pixel effects. What I’m going to present are shaders for a few simple stipple patterns. Applying the shader is simply a matter of passing it into the constructor for the FXController class, e.g.

var theShader = Shader.crossStippleShader;
var fxCtrlr = new FXController(ctxSource, ctxDest, theShader, width, height, 100, 1);
fxCtrlr.init();

Checkerboard Stipple

This shader has the effect of creating a checkerboard pattern.
The source pixel is preserved if (x+y) % 2 == 0, otherwise the pixel’s alpha is reduced to 66.

Shader.checkerboardStippleShader = function (imageData, bufWrite, index, x, y, r, g, b, a, passNum, frameNum, maxFrames)
{                
    
if( (x+y)%2 == 0) {
        GraphicsCore.setPixel(bufWrite, index, r, g, b, 255);
    }
    
else {
        GraphicsCore.setPixel(bufWrite, index, r, g, b, 66);
    }
}
Shader.checkerboardStippleShader.numPassesRequired = 1;    

Checkerboard Stipple

Dot Stipple

This shader blends a white pixel into the source image where x%2 == 0 && y%2 == 0, in effect creating a dotted grid pattern.

The alpha blending code is a straightforward implementation of alpha compositing, but since we’re blending with white (where r=1.0, g=1.0, and b=1.0) the equation is simplified and there is no second color value; we’re just biasing the source color by the alpha value. Also note that this is different than simply changing the alpha of the source pixel (as was done in the Checkerboard Stipple shader), here we’re always blending with white, in the previous shader we’re blending with whatever is the background of the DOM element.

Shader.dotStippleShader = function (imageData, bufWrite, index, x, y, r, g, b, a, passNum, frameNum, maxFrames)
{    
    
var alpha = 0.8;
        
    
var r1 = r / 255.0;
    
var rF = Math.floor((alpha*r1 + (1.0-alpha)) * 255.0);
            
    
var g1 = g / 255.0;
    
var gF = Math.floor((alpha*g1 + (1.0-alpha)) * 255.0);

    
var b1 = b / 255.0;
    
var bF = Math.floor((alpha*b1 + (1.0-alpha)) * 255.0);            
            
    
if( x%2 == 0 && y%2 == 0) {
        GraphicsCore.setPixel(bufWrite, index, rF, gF, bF, 255);
    }
else {
                
        GraphicsCore.setPixel(bufWrite, index, r, g, b, 255);
    }            

}
Shader.dotStippleShader.numPassesRequired = 1;

Dot Stipple

Quincunx Stipple

With this shader we blend in a white pixel at every 4 pixels (x%4 == 0 && y%4 == 0, the target pixel) and also at the 4 orthogonally adjacent pixels around the target, creating a quincunx pattern.

Shader.quincunxStippleShader = function (imageData, bufWrite, index, x, y, r, g, b, a, passNum, frameNum, maxFrames)
{    
    
var alpha = 0.78;
        
    
var r1 = r / 255.0;
    
var rF = Math.floor((alpha*r1 + (1.0-alpha)) * 255.0);
            
    
var g1 = g / 255.0;
    
var gF = Math.floor((alpha*g1 + (1.0-alpha)) * 255.0);

    
var b1 = b / 255.0;
    
var bF = Math.floor((alpha*b1 + (1.0-alpha)) * 255.0);            
            
    
if( (x%4 == 0 && y%4 == 0) ||
        ((x+1)%4 == 0 && y%4 == 0) ||
        ((x-1)%4 == 0 && y%4 == 0) ||
        (x%4 == 0 && (y+1)%4 == 0) ||
        (x%4 == 0 && (y-1)%4 == 0) )
    {
        GraphicsCore.setPixel(bufWrite, index, rF, gF, bF, 255);
    }
    
else
    
{
        GraphicsCore.setPixel(bufWrite, index, r, g, b, 255);
    }            

}
Shader.quincunxStippleShader.numPassesRequired = 1;

Quincunx Stipple

Cross Stipple

Similar to the quincunx stipple, but we blend in a white pixel at every 6 pixels (x%6 == 0 && y%6 == 0, the target pixel), the 4 orthogonally adjacent pixels around the target, and 4 additional pixels extending beyond the orthogonals, creating a cross (“+”) pattern.

Shader.crossStippleShader = function (imageData, bufWrite, index, x, y, r, g, b, a, passNum, frameNum, maxFrames)
{    
    
var alpha = 0.78;
        
    
var r1 = r / 255.0;
    
var rF = Math.floor((alpha*r1 + (1.0-alpha)) * 255.0);
            
    
var g1 = g / 255.0;
    
var gF = Math.floor((alpha*g1 + (1.0-alpha)) * 255.0);

    
var b1 = b / 255.0;
    
var bF = Math.floor((alpha*b1 + (1.0-alpha)) * 255.0);            
            
    
if( (x%6 == 0 && y%6 == 0) ||
        ((x+1)%6 == 0 && y%6 == 0) ||
        ((x-1)%6 == 0 && y%6 == 0) ||
        ((x+2)%6 == 0 && y%6 == 0) ||
        ((x-2)%6 == 0 && y%6 == 0) ||                
        (x%6 == 0 && (y+1)%6 == 0) ||
        (x%6 == 0 && (y-1)%6 == 0) ||
        (x%6 == 0 && (y+2)%6 == 0) ||
        (x%6 == 0 && (y-2)%6 == 0)                 
        )
                
    {
        GraphicsCore.setPixel(bufWrite, index, rF, gF, bF, 255);
    }
    
else
    
{
        GraphicsCore.setPixel(bufWrite, index, r, g, b, 255);
    }            

}
Shader.crossStippleShader.numPassesRequired = 1;    

Cross Stipple

That’s all for now. There’s tons of variations possible with only minor code changes to alter blending, color, and the shape of the stipple patten.

Ellipsizer

A significant number of projects I’ve worked on have, at some point, involved truncating text and tacking on an ellipsis. The problem is, it’s difficult to do it precisely, as doing so requires exact metrics for fonts used and exact dimensions of whatever area the text is contained within; information usually not readily available. So it’s usually necessary to do some sort of approximation of the maximum number of characters that can fit within the container. However, this method only works well for fixed-width fonts; with variable-width fonts the result is a significant error/over-estimate (so the string is cut off well before it needs to be), as the width of characters can be significantly different (e.g. “i” vs “m”).

One idea I had to reduce this error was to specify a relative width (i.e. weight) for certain characters. In general, most characters would have a width of 1.0, but whitespace, period, comma, lowercase ‘i’, semicolon, etc. would have a width less than 1.0. With these widths, a length function can be defined which is a sum of the character widths, and the result is a value that indicates the length of the string relative not only to the number of characters rendered but also to the width of the characters rendered. A maximum threshold is still needed, but it no longer specified an absolute maximum number of characters, instead it’s the maximum number of full-width (width = 1.0) characters that can be rendered in the given area.

Of course, someone else had the same idea, but I didn’t like the code presented – the String.replaceAll() call every iteration made me cringe. Even with immutable strings, this is something that can be done in O(n) time as long as you can reference individual characters and grab a sub-string. However, one issue presented in the post that I didn’t think about was that strings should only be truncated at certain points (a space, a hyphen) and not simply chop off words in the middle.

My solution is in Javascript, but the operations should carry over easily to any language.

Ellipsizer class

Ellipsizer = function(_maxLength) {

    
this.maxLength = _maxLength;        

    
this.getLetterWidth = function(letter)
    {                
        
if(letter == '.' || letter == ' ' || letter == ',' || letter == '|' || letter == '\'' || letter == ':' || letter == ';' || letter == '!')
            
return 0.25;
    
        
if(letter == 'j' || letter == 'l' || letter == 'i' || letter == '^' || letter == '(' || letter == ')' || letter == '[' || letter == ']' || letter == '"')
            
return 0.5;
    
        
return 1;
    }
    
    
this.isCharCutpoint = function(ch)
    {
        
if(ch == ' ' || ch == '-' || ch == '.' )
            
return true;
            
        
return false;
    }
    
    
this.cut = function(str)
    {
    
        
var strWeightedLength = 0;
        
var lastCutpoint = 0; // point of whitespace, hyphen, etc. (point where we can cut string)
        
var curSubStr = '';
        
for(var i=0; i<str.length; i++)
        {
            
var letter = str.charAt(i);
            strWeightedLength +=
this.getLetterWidth(letter);
            
            
if(this.isCharCutpoint(letter))
            {
                lastCutpoint = i;
            }
            
            
if(strWeightedLength >= this.maxLength)
            {
                curSubStr = str.substr(0, lastCutpoint);
                
break;
            }
        }
    
        
var result = curSubStr + "…";
        
return result;
    }
    
}

Example

$(document).ready(function() {

    $(
'#ellipsize').click(function() {
    
        $(
'#text-to-cut p').each(function() {
            
var str = $(this).text();
            
var ellipsizer = new Ellipsizer(64);                    
            $(
this).text( ellipsizer.cut(str) );                        
        });
        
        
return false;
    });                
});

Maximum length of 64 to provide for the following constraints:

Ellipsizer

Ellipsizer

Live Demo

There’s quite a bit that can be improved here, mainly in recognizing more characters as quarter- or half-width characters, or having more classes of widths (0.3, 0.75, etc.) and assigning characters to them. However, for a first-pass, this seems to work pretty well and it’s important to remember that this is still a pretty rough approximation.

Elegant <img> presentation

While it’s easy to decry Flash and espouse the merits of HTML5, it’s worth taking a look at some of the positive elements found on many Flash sites and seeing what can be brought over to the HTML side. One very obvious aspect is the presentation of images. Flash sites almost never render an image in the way that an <img> tag is rendered by the browser. Flash sites will (typically) elegantly animate or fade an image in/out whereas the default browser behavior for HTML is to progressively render the image as it is received (sometimes images are 2D interlaced for a slightly better effect). Even worse, many HTML sites leave width and height as attributes to be determined dynamically for the <img> tag, resulting in page content shifting around as images are loaded.

Better image presentation is certainly possible with HTML + Javascript, and there are tons of beautiful JS image galleries on the web, but outside of such galleries most developers don’t attempt anything beyond the typical <img> presentation.

So, I did a little experiment with 2 goals in mind:

  • Elegant presentation of images with HTML + JS
  • Very simple markup, one iota above what’s necessary for a typical <img> tag

I’m pretty happy with the results.

elegant img loading

View the demo
(disable your browser cache so that loading latency is accurately taken into account)

Here’s what I did:

1. Make the <img> elements.

e.g.

<img
data-src="http://aautar.digital-radiation.com/elegant-img-loading/p1.jpg"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAAQSURBVHjaYvj//z8DQIABAAj8Av7bok0WAAAAAElFTkSuQmCC"
width="200"
height="150"
alt="photo" />

Things to note:

  • The data-src attribute is an HTML 5 custom data attribute. It specifies the URL of the image to be loaded.
  • The src attribute uses the data URI scheme to load and set the initial image displayed in the img element. Leaving the src attribute blank is not desirable as it will collapse the img element and only show the alt tag (the same behavior occurs initially if a URL to an image is specified). Specifying a data URI allows the image to be loaded as part of the document itself. The data URI shown above is for a 1×1, transparent PNG. A GIF preloader would probably be a good idea here.
  • The width and height attributes are specified. This is not strictly necessary, but in keeping with the theme of elegance, this prevent other content on the page from shifting after images are loaded.

2. Write the Javascript code

<script src="jquery-1.6.2.min.js" type="text/javascript"></script>
<
script type="text/javascript">

window.onload = function ()
{
$(
'img').each(function ()
{
var imgElem = $(this);
var src = imgElem.attr('data-src');

var img = new Image();
img.src = src;
img.onload =
function ()
{

imgElem.fadeTo(
'fast', 0.0001, function ()
{
imgElem.attr(
'src', src);
imgElem.fadeTo(
'slow', 1)
});

}

});
}

</script>

For simplicity, jQuery is used.

After the page is loaded, the data-src attribute of every <img> element is read. The image, specified by the data-src attribute, is loaded via a Javascript Image object and, once loaded (Image.onload event fires):

  • The <img> element is faded out (actually faded to an alpha of 0.0001 because a total fadeOut to 0 will effectively remove the element from the document rendering).
  • The src attribute is changed to the the URL of the loaded image.
  • The <img> element is faded in.

The important bit is changing the src attribute; the effect itself, whether it’s fading, sliding, etc. is not terribly important.

jxNotify

I made a little JavaScript notification system, somewhat inspired by webOS and also by the type of notifications you see on Gmail.

jxNotify code

The central idea was to have an elegant system that could sensibly display the progress of AJAX operations; meaning notifications stays up while the operation is being done (i.e. while sending request and waiting for a reply from the server), then a success or failure message is posted upon completion, which fades away automatically.

While designed for AJAX calls, this could certainly be used in other cases as well.

jxNotify notifyPre

jxNotify notifyPostError


Initializing

// optional argument = icon, recommended size of 18x18
jxNotify.init('jx-notify.content/sdotspott-notify-icon.png');


Notify of operation in progress (notifyPre)

jxNotify.notifyPre('doing stuff...');


Notify of operation completed (notifyPost)

jxNotify.notifyPost('finished!');


Notify of operation failure (notifyPostError)

jxNotify.notifyPostError('something bad happened!');

 

jQuery toggle button

On most of the mobile platforms you’ve probably seen a toggle, switch-style, button used as a replacement for a checkbox. I took a stab at doing something similar in HTML, CSS and Javascript.

toggle button

You can see the final result here (it’s a pain in the ass to embed it)

Note that while I used jQuery, this is not a jQuery extension. It doesn’t use that much jQuery and I really don’t get the desire to make everything-and-the-kitchen-sink a jQuery plugin.

The button depends upon 2 images, a base, containing the design and both states of the button:

toggle button base

… and a frame (optional if you can get away with using CSS borders):

toggle button frame

(note, the middle is transparent, not white)

The HTML and CSS consists of a:

  • A div, which has the its background-image set to the base and sized to the button’s inner area, roughly half the width (in this case, plus a few pixels as some pixels were shared by both states of the button) of the base and the same height
  • A block-level anchor element within the div, which has its background-image set to the frame and sized to the same area as the frame image. The anchor allows the area to be clickable and we’ll respond to the click event that occurs on this element.
  • An input checkbox which will store the checked/unchecked state of the button.
<div style="margin:0; padding:0; background:url(base.png) -41px 1px no-repeat transparent; width:46px; height:20px;">
<a class="toggle-button" href="#" style="margin:0; padding:0; display:block; background:url(frame.png) 0 0 no-repeat transparent; width:48px; height:20px;">
<input style="display:none;" type="checkbox" />
</a>
</div>

The Javascript code to handle the click event, where the background is shifted left or right when the button’s state is toggled using jQuery’s animate function,

$('.toggle-button').click(function ()
{
if (!$('input', this).is(':checked')) {
$(
this).parent().animate({ "background-position": "0px 1px" }, "slow");
$(
'input', this).attr('checked', true);
}
else {

$(
this).parent().animate({ "background-position": "-41px 1px" }, "slow");
$(
'input', this).attr('checked', false);
}

return false;
});

This all works great, but it’s not-so-great as a reusable component, so I encapsulated the code so that I could easily transform a div, such as the one shown below, into the toggle button.

<div id="my_toggle_button"></div>

Central to this is creating a ToggleButtonFactory, which will make the button by inserting the necessary HTML/CSS code into the DOM and bind the anchor to the click event. There’s also a ToggleButton object created by the factory which will have methods to toggle the button state (.toggle) and get the state of the button (.val).

function ToggleButton(_element, _funcSelectYes, _funcSelectNo)
{
this.jqDomElement = _element;
this.funcSelectYes = _funcSelectYes;
this.funcSelectNo = _funcSelectNo;

this.val = function ()
{
return $(this.jqDomElement).find('input').is(':checked');
}

this.toggle = function (funcSelectYes, funcSelectNo)
{
if (!this.jqDomElement.find('input').is(':checked')) {
this.jqDomElement.animate({ "background-position": "0px 1px" }, "slow");
this.jqDomElement.find('input').attr('checked', true);

if (this.funcSelectYes) {
this.funcSelectYes();
}
}
else {

this.jqDomElement.animate({ "background-position": "-41px 1px" }, "slow");
this.jqDomElement.find('input').attr('checked', false);

if (this.funcSelectNo) {
this.funcSelectNo();
}
}
}
}

var ToggleButtonFactory = {};
ToggleButtonFactory.makeButton =
function (element, initialState, funcSelectYes, funcSelectNo)
{
if ($(element).is('div')) {

var elemId = $(element).attr('id');
var newDivId = '__toggle_button_div_' + Math.ceil((Math.random() * 100000));
$(element).replaceWith(
'<div id="' + newDivId + '" style="margin:0; padding:0; background:url(base.png) -41px 1px no-repeat transparent; width:46px; height:20px;"><a class="toggle-button" href="#" style="margin:0; padding:0; display:block; background:url(frame.png) 0 0 no-repeat transparent; width:48px; height:20px;"><input id="' + elemId + '" name="' + elemId + '" style="display:none;" type="checkbox" /></a></div>');

var newElem = $('#' + newDivId);
var tb = new ToggleButton(newElem, funcSelectYes, funcSelectNo);

newElem.find(
'a').click(function ()
{
tb.toggle();
return false;
});

if (initialState) {
tb.toggle();
}

return tb;
}
}

Note there’s some additional code here to deal with setting the button to an initial state and callbacks for when the button is set to the “Yes” or “No” state.

Now, to transform the my_toggle_button div shown above into a toggle button, the following is done:

var btn = ToggleButtonFactory.makeButton('#my_toggle_button', false, function () { }, function () { });

(the call can be shorter, this shows calling with all arguments and capturing the return value [the ToggleButton object])

For another take on this, see the jQuery LightSwitch plugin.