Articles Tagged ‘JavaScript’

Going jQuery-free Part 1: Planning

Removing jQuery from your website is a big thing. We have become so reliant upon it that there are far more JavaScript plugins and modules that rely on it than that don’t. You have to be so careful when we try to remove it, or you’ll end up losing functionality. We need to plan to remove this code – we need to know what could break when we take jQuery away, and we need to know what holes we will need to plug to make our code work again.

What browsers do you need to support?

First things first, if you are going jQuery-free we need to talk about what browsers your JavaScript will support. jQuery does a lot of good things, especially for old IE, polyfilling a huge amount of code that is only available in IE9 or later. Beyond this, old Android has significant bugs and lacks support for a number of different technologies. You need to decide what the baseline will be for your usage, and apply polyfills to suit.

If you have to support IE8 I do not recommend that you ditch jQuery. IE8 doesn’t have many of the fundamentals to be considered “modern” and work without significant polyfilling in a way that modern Webkit browsers do. The Guardian uses a simple script to detect whether your browser is modern:

isModernBrowser:('querySelector'in document&&'addEventListener'in window&&'localStorage'in window&&'sessionStorage'in window&&'bind'in Function&&(('XMLHttpRequest'in window&&'withCredentials'in new XMLHttpRequest())||'XDomainRequest'in window))

This checks for the following functions:

  • querySelector
  • EventListener
  • Local Storage and Session Storage
  • Function.bind
  • Standards-based AJAX
  • CORS (Cross-origin resource scripting)

If you’re modern, The Guardian will give you everything. If not, you get a gracefully degraded experience. IE8 will fail all but the local storage part of that test, and polyfilling that much code will negate all the benefits of removing jQuery.

With some browsers, it is possible to polyfill small parts of this functionality and still remove jQuery. For example, Yell supports iOS 4 and above, which doesn’t have Function.bind, Android 2.3 doesn’t support Element.ClassList or SVG, and IE10 doesn’t support Element.data. We chose to polyfill these functions for the older browsers, but not SVG or Element.data, as these can be resolved by other techniques or just coding differently.

In the end, it’s your choice what browsers you support, but you have to be careful. There is a complete list of ECMAScript 5 functions and browsers that support them on Kangax’s Github page (as well as pages for ES6 and ES7 if you’re interested) that is incredible for helping you make this decision.

Plugins & Third-party code

jQuery made JavaScript very accessible to the novice coder because large amounts of complex code can be contained in plugins, and there are lots of them. Because jQuery is the starting point, so many bits of third-party code will need jQuery rather than using vanilla javascript and so are completely unsuitable for going jQuery-free. This can be quite a big problem.

There will be plenty of times that you will be able to find alternatives to plugins that work without jQuery. The simple way to find them is to use Google, Github, StackOverflow and Twitter to search for alternatives. I wish that there was a repository that told you the good alternatives for common jQuery plugins – but there isn’t one (note to self: do this). This can be laborious, and involve a lot of trial and error to find alternatives that match the feature set you’re looking for.

I went through this same process with my team for yell.com’s mobile site. Luckily, there was only one plugin that we needed to keep, Photoswipe - a cool plugin that creates a touch-friendly lightbox from a list of images. We looked high and low for vanilla JS alternatives that 1. were mobile-friendly, and 2. worked on Windows Phone 8 & Firefox OS and Firefox mobile. That last part was the hard bit, and I’m sad to say that we didn’t find an answer. So, we had to build this ourselves – you can see it on any business profile page on your smartphone like this one (though you’ll have to change your browser user agent to a mobile phone to see it).

TL;DR: you’ll need to find replacements for all plugins, and if you don’t, your options are to write it yourself, or drop the functionality.

Auditing your JavaScript

Once you’ve found solutions for third party code, you need to focus on your own custom code. To find out what could break, you’ll need to look over your JS and see what jQuery methods and properties are in use. You can do this manually, but I don’t fancy looking over 10,000 lines of code by hand. I asked “how can I do this” on Stack Overflow and got a great answer from Elias Dorneles:

You need a tool that understands JavaScript, like grasp. You could try to do the counting using the -o option for grasp and adding a sed filter to get only the function names: grasp '$(__).__' -e -o *.js | sed 's/.*[.]//' | uniq -c. This fails for some code for the same reasons that grep, but maybe it can help you get an estimate.

You can run this on one file, or an entire directory. Here’s what Bootstrap.js looks like (after I’ve tabulated it in excel):

Function Count
on 13
data 6
js 5
each 3
is 2
ready 2
off 2
height 2
insertAfter 1
remove 1
appendTo 1
target 1
parentsUntil 1
parents 1
width 1
one 1
trigger 1

This is the list of functions that you will have to find alternatives to in order for your JS to function correctly, along with an approximate count of the number of times a function is used. I say approximate, because in my experience, the grasp script doesn’t get everything, especially where chained functions are concerned. The good news with this set is that there aren’t many complex functions in use – the vast majority can be replaced with a line or two.

The results of this query can bring back all sorts of jQuery functions, things like .live, .die, .delegate, .browser, .size, .toggle, and other deprecated functions from over the years. These are the warning signs that the rest of your code may not be ready for a move away from jQuery, and if you get these, you should seriously consider why you’re doing this. I listed my reasons in the introduction post and there are more besides, like minimal memory footprint whilst adding Windows Phone 8 and Firefox OS support. You may end up spending a lot more effort on your code than you originally intended, just to bring it up to par with the current state of web standards. Clearly, this isn’t a bad thing, but your boss may be wondering why it’s taking so much time. For a great article on technical debt, try Paying Down your Technical Debt from Chris Atwood’s Coding Horror site.

Up next, replacing individual functions with standards-based code

That’s it for this part, in the next one, I’ll cover replacing the functions identified above with standards-compliant code to create your own min.js.

Interacting with HTML5 Video Players

HTML5 video players are incredibly useful, enabling developers to display video on non-flash devices (I’m looking at you, Apple). It’s really simple to get started with HTML5 video, but when you want to do something more complicated, there’s not much documentation. Thankfully, it’s really quite simple, and this article will show you how to use the HTML5 video JavaScript API to interact with the videos.

Basic video


This is the HTML:

<video id="firstvideo" style="width:100%" controls>
	<source src="http://mirrorblender.top-ix.org/peach/bigbuckbunny_movies/big_buck_bunny_480p_stereo.ogg" >
	<source src="http://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_640x360.mp4" />
</source></video>

This is your standard HTML5 video player, with the standard browser video controls. The video has multiple sources so browsers that only support certain codecs (FireFox and Ogg for example) get the right video.

Let’s say you want the video to start when you click a button. That’s pretty easy, and looks like this:

var firstvideo = document.getElementById('firstvideo');
var playButton = document.getElementById('playfirstVideo');
playButton.addEventListener('click', function(e) { 
	firstvideo.play();
});
//jQuery version
$('#playfirstVideo').on('click', function(e) {
	$('#firstvideo').play();
});


And if you want to pause it:

var stopButton = document.getElementById('stopfirstVideo');
stopButton.addEventListener('click', function(e) { 
	firstvideo.pause();
});
//jQuery version
$('#stopfirstVideo').on('click', function(e) {
	$('#firstvideo').pause();
}


Subscribable events

That’s cool, you can interact with the element through JavaScript. If you’re a fan of the regular video controls, you can still access events fired by the video. The main ones you’ll be interested in are:

  • play – triggered when the video starts playing
  • playing – triggered whilst the video is playing
  • canplay – triggered when the video has been loaded and it can be played
  • pause – triggered when the video is paused
  • ended – triggered when the video has finished

There are lots of others too, a full list can be seen on the W3C demo for HTML5 video. These include events triggered when the video is seeked (skipped forward or backwards in time) and when the time on the video has changed.

It’s simple to hook these up using event listeners to see what’s going on.

Video state:

Try it out using this video, watching the label which says which event was fired last.

	var secondvideo = document.getElementById('secondvideo');
	var statelabel = document.getElementById('videoState');
	secondvideo.addEventListener('play', function(e) { // Repeat this for other events
		// The video is playing
		statelabel.innerHTML = "Playing";
	});
	// jQuery example
	var statelabel = $('#videoState');
	$('#secondvideo').on('play', function(e) { // Repeat this for other events
		// The video is playing
		statelabel.html("Playing");
	});

That's the basics, from there you can use the timing event to make a caption system (tag words with data elements and mark them when their time hits in the video).

What about YouTube?

With YouTube and other embedded video players there's no direct access to the video element, which makes interacting with videos in the way shown above very difficult.
Thankfully, the kind folks at Google have thought of this and provided an API to help us through. The whole documentation is on YouTube developers and there's a great player demo, but here's the gist of it.

There's a few gotchas you need to be aware of:

  1. You've got to be using a served web page, local files won't work.
  2. If you're trying to interact more than one YouTube embed on a page, you will need to address each one individually with unique IDs, otherwise the browser won't know which video to interacti with

Got it? Great, let's head into the code.
This first section, is the embed code needed to include the video in the page. This should be standard YouTube embedding stuff.

// The element that the player will be inserted into
<div id="ytplayer"></div>
<script>
// Load the IFrame Player API code asynchronously.
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/player_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
 
// Replace the 'ytplayer' element with an <iframe> and
// YouTube player after the API code downloads.
var player;
function onYouTubePlayerAPIReady() {
  player = new YT.Player('ytplayer', {
    height: '390',
    width: '640',
    videoId: 'g8evyE9TuYk',
    events: {
      'onStateChange': "onytplayerStateChange"
    }
  });
}</iframe></script>

Video state:

That's your YouTube video, now we need to subscribe to the events N.B. there's no need to subscribe to the onYouTubePlayerReady event as you've effectively done this when the API loads

function onytplayerStateChange(e) {
	document.getElementById('ytvideoState').innerHTML = e.data;
}
// jQuery version
function onytplayerStateChange(e) {
	$('#ytvideoState').html(e.data);
}

As the state changes, a different value appears at the bottom. These are: unstarted (-1), ended (0), playing (1), paused (2), buffering (3), video cued (5). From there you can access any function of the YouTube JavaScript API, which mimics the HTML5 spec with a few small changes, such as the play command is playVideo() instead of just play(). Take a look at the documentation for full details about what you can do.

Vimeo too?

Yes, Vimeo can do this too. The API is similar to YouTube, but isn't quite as simple without their Frogaloop library, so have a look at their documentation for the Vimeo JavaScript API.

Summary

So, there you have it, a few bits of code to control different types of HTML5 video that are common on the web. They're really useful for counting the number of views of a video, captions, and generally making videos more interactive. Let me know what uses you come up with for it in the comments.

Improving Javascript XML Node Finding Performance by 2000%

In my work, I’m parsing web services all of the time. Most of the time, they’re XML, which does not make the best use of bandwidth/CPU time (compared to JSON), however, if it’s all that you’re given then you can certainly get by. I’ve been looking into ways to speed up the XML document traversal in with jQuery after the current best practice method was removed.

The basic way to find certain nodes in an XML web service is to use the .find() method. This is used heavily by the SPServices jQuery helper (which is, in general, a great library).

$(xData.responseXML).find("[nodeName='z:row']").each(function() {
// Do stuff
});

That’s absolutely fine – it’s going to find the attribute nodeName with a value of z:row. However, since jQuery 1.7, this method does not work. I raised this regression in the jQuery bug tracker and was encouraged to find a solution; another selector that worked in all browsers. Unfortunately, at the time I couldn’t come up with anything better than this:

$(xData.responseXML).find("z\\:row, row").each(function() {
// Do stuff
});

The “z\\:row” selector works in IE and Firefox, and the “row” selector works in Chrome and Safari (I’m unable to test in Opera here, sorry). This was flagged as the solution to the problem and they wouldn’t be making any fixes to the jQuery core.

After a few weeks of using this method, I noticed that the site had been slowing down, especially in IE, and I thought this new selector was the cause. So, I looked into the performance numbers using jsPerf and I raised a bug too. My first test was to see what the current solution was doing, and whether jQuery 1.7 had made things worse.
Test case: http://jsperf.com/node-vs-double-select/4

So, performance in Chrome is identical for each of the selectors (and it’s the same in Firefox and Safari) but IE drops nearly half of its operations because it has to perform that second selector.

It’s still not very high performance though, and so I looked for other solutions.

Dmethvin suggested:

Did you try the custom plugin in the ticket? If you’re having performance issues that should be much faster.

The plugin he’s referring to is this:

jQuery.fn.filterNode = function(name){
   return this.filter(function(){
      return this.nodeName === name;
   });
});

This filters content by their nodeName and compares it against the name that you gave it. The issue with this is that .filter() does not traverse down the tree, staying at the level of the set of objects that it was given. Therefore, a quick solution was this:

$(xData.responseXML).children().children().children().children().children().children().children().filterNode('z:row').each(function() {
// Do stuff
});

jsPerf Test: http://jsperf.com/node-vs-double-select/1

Wow, that’s about 50 times faster. Even IE beats Chrome when doing this operation. The simple reason is that it’s got a smaller set of objects to go through and it’s comparing a single attribute rather than parsing the text of the XML to try and find the namespaced element.

Still, I wasn’t satisfied as in order to achieve that performance, I had to know how deep I was going to be going in order to retrieve the set. So, back to the bug and another suggestion by dmethvin:

If you’re going that deep, use a filter function passed to .find(). How does that fare?

After a few attempts, a colleague of mine came up with this beauty:

$.fn.filterNode = function(name) {
      return this.find('*').filter(function() {
        return this.nodeName === name;
      });
    };

jsPerf test: http://jsperf.com/node-vs-double-select/3

Incredible performance increase using .find('*').filterNode('z:row')
http://jsperf.com/node-vs-double-select/3

Using .find(‘*’).filter() increased performance to 200x faster than the original .find(‘z:row’) selector

I mean, wow, that’s incredible. On the graph, those tiny little bits of colour are the original selectors, and those only 20% of the way up are the previous massive performance increase by using filter. It should also be noted that IE8 performance using this selector increased in jQuery 1.7 in comparison to when using jQuery 1.6.

Side-note: IE10′s javascript performance is almost equal to that of Google Chrome. In comparison, IE9 (not shown) is about half of that.

The reason for this massive increase is that it’s backed by native selectors. A .find(‘*’) will translate into element.querySelectorAll(‘*’) which is very fast when compared to doing 8 .children() calls.

Summary
Dealing with large amounts of data from web services needs to be fast. Using a simple .find() on the node name no-longer works and alternatives have been investigated. The fastest method, using a short one-line plug-in, improves performance by up to 2000% compared to the old methodology.

I’ll be notifying the SPServices group of this post, and hopefully they can improve the performance of their library.

Sketchnotes from #LWS3D – A 50-line WebGL app

After a summer break, London Web Standards was back with an evening of WebGL with Ilmari Heikkenen from Google and a short demo from Carlos Ulloa of HelloEnjoy. Sketchnotes are below

Carlos Ulloa of Brighton-based HelloEnjoy showed off two demos that he made using Three.js and WebGL. The first was HelloRacer, an interactive look at the 2010 Ferrari F1 car that you can even drive and do handbrake turns in. The second demo got it premiere at LWS, an interactive music video for Ellie Goulding’s “Lights”. Honestly, it was extremely cool, on a Ro.me “3 dreams of black” scale. It’ll appear at the linked URL in the next week or so. There’s a great Q&A session on the London Web Standards blog of the event for more detail on how they did it.

Ilmari Heikkenen showed the gathered crowd how to make a basic WebGL app using Three.js in about 50 lines. He showed off all of the components that you need: a renderer, scene, camera, mesh and lights (and shadows). He went into more depth about vertex shaders and fragment shaders, the GPU effects that make everything look a lot more real.

Ilmari gave examples of a few uses, including games, 3D bar charts and scatter graphs. He then started animating all of these, including a 10,000 point scattergraph that moved in real-time. Finally, he demonstrated a loader for Collada meshes (supported by Maya) and brought in a monster that with a few lines of code started walking around the screen.

Overall, it was a great introduction to the subject, one worth a lot more of your time.

Ilmari’s slides can be found on his blog.