We have now released Aurora 11, soon to become Firefox 11, and wanted to cover all the the things we have improved in this version!
DOM Articles
-
-
getUserMedia is ready to roll!
We blogged about some of our WebRTC efforts back in April. Today we have an exciting update for you on that front: getUserMedia has landed on mozilla-central! This means you will be able to use the API on the latest Nightly versions of Firefox, and it will eventually make its way to a release build.
getUserMedia is a DOM API that allows web pages to obtain video and audio input, for instance, from a webcam or microphone. We hope this will open the possibility of building a whole new class of web pages and applications. This DOM API is one component of the WebRTC project, which also includes APIs for peer-to-peer communication channels that will enable exchange of video steams, audio streams and arbitrary data.
We’re still working on the PeerConnection API, but getUserMedia is a great first step in the progression towards full WebRTC support in Firefox! We’ve certainly come a long way since the first image from a webcam appeared on a web page via a DOM API. (Not to mention audio recording support in Jetpack before that.)

We’ve implemented a prefixed version of the “Media Capture and Streams” standard being developed at the W3C. Not all portions of the specification have been implemented yet; most notably, we do not support the Constraints API (which allows the caller to request certain types of audio and video based on various parameters).
We have also implemented a Mozilla specific extension to the API: the first argument to mozGetUserMedia is a dictionary that will also accept the property
{picture: true}in addition to{video: true}or{audio: true}. The picture API is an experiment to see if there is interest in a dedicated mechanism to obtain a single picture from the user’s camera, without having to set up a video stream. This could be useful in a profile picture upload page, or a photo sharing application, for example.Without further ado, let’s start with a simple example! Make sure to create a pref named “
media.navigator.enabled” and set it totrueviaabout:configfirst. We’ve put the pref in place because we haven’t implemented a permissions model or any UI for prompting the user to authorize access to the camera or microphone. This release of the API is aimed at developers, and we’ll enable the pref by default after we have a permission model and UI that we’re happy with.There’s also a demo page where you can test the audio, video and picture capabilities of the API. Give it a whirl, and let us know what you think! We’re especially interested in feedback from the web developer community about the API and whether it will meet your use cases. You can leave comments on this post, or on the dev-media mailing list or newsgroup.
We encourage you to get involved with the project – there’s a lot of information about our ongoing efforts on the project wiki page. Posting on the mailing list with your questions, comments and suggestions is great way to get started. We also hang out on the #media IRC channel, feel free to drop in for an informal chat.
Happy hacking!
-
Firefox 4: Better performance with Lazy Frame Construction
This is a re-post from Timothy Nikkel’s blog.
Lazy Frame Construction is new to Gecko and allows many DOM operations (appendChild, insertBefore, etc) to not trigger immediate reflows. This can vastly improve the interactive performance of very complex web pages. If you want to test this out, you should get a Firefox Nightly.
Lazy frame construction recently landed on mozilla-central. To explain what this means and how this improves things we need some background. Each node in the DOM tree of a webpage has a frame created for it that is used to determine where on the page the node is drawn and its size. A frame corresponds closely to the concept of a box from the CSS spec. We used to create frames for DOM nodes eagerly; that is as soon as a node was inserted into the document we would create a frame for it. But this can create wasted effort in many situations. For example if a script inserts a large number of nodes into the DOM we would create a frame for each node when it is inserted. But with lazy frame construction we can process all those nodes at once in a big batch, saving overhead. Furthermore the time it takes to create those frames no longer blocks that script, so the script can go and do what it needs to and the frames will get created when they are needed. There are other situations where a script would insert nodes into the document and remove them immediately, so there is no need to ever create a frame for these as they would never be painted on screen.
So now when a node is inserted into a document the node is flagged for needing a frame created for it, and then the next time the refresh driver notifies (currently at 20 ms intervals) the frame is created. The refresh driver is also what drives reflow of webpages and CSS & SVG animations.
Let’s look at two examples where lazy frame construction helps.
In this example we insert 80000 div elements and then we flush all pending layout to time how long it takes before the changes made by the script are done and visible to the user. The script can continue executing without flushing layout, but we do it here to measure how long the actual work takes.
var stime = new Date(); var container = document.getElementById("container"); var lastchild = document.getElementById("lastchild"); for (var i = 0; i < 80000; i++) { var div = document.createElement("div"); container.insertBefore(div, lastchild); } document.documentElement.offsetLeft; // flush layout var now = new Date(); var millisecondselapsed = (now.getTime() - stime.getTime());
With lazy frame construction we are able to process the insertion of all 80000 div elements in one operation, saving the overhead of 80000 different inserts. In a build without lazy frame construction I get an average time of 1358 ms, with lazy frame construction I get 777 ms.
This example comes from a real webpage. We append a div and then set “div.style.position = ‘absolute’;”, and repeat that 2000 times, and then we flush all pending layout to time how long it takes before the changes made by the script are done and visible to the user.
var stime = new Date(); var container = document.getElementById("container2"); for (var i = 0; i < 2000; i++) { var div = document.createElement("div"); container.appendChild(div); div.style.position = "absolute"; } document.documentElement.offsetLeft; // flush layout var now = new Date(); var millisecondselapsed = (now.getTime() - stime.getTime());
With lazy frame construction we don't even bother creating the frame for the div until after the position has been set to absolute, so we don't waste any effort. In a build without lazy frame construction I get an average time of 4730 ms, with lazy frame construction I get 130 ms.
-
saving data with localStorage
This post was written by Jeff Balogh. Jeff works on Mozilla’s web development team.
New in Firefox 3.5,
localStorageis a part of the Web Storage specification.localStorageprovides a simple Javascript API for persisting key-value pairs in the browser. It shouldn’t be confused with the SQL database storage proposal, which is a separate (and more contentious) part of the Web Storage spec. Key-value pairs could conceivably be stored in cookies, but you wouldn’t want to do that. Cookies are sent to the server with every request, presenting performance issues with large data sets and the potential for security problems, and you have to write your own interface for treating cookies like a database.Here’s a small demo that stores the content of a
textareainlocalStorage. You can change the text, open a new tab, and find your updated content. Or you can restart the browser and your text will still be there.
The easiest way to use
localStorageis to treat it like a regular object:>>> localStorage.foo = 'bar' >>> localStorage.foo "bar" >>> localStorage.length 1 >>> localStorage[0] "foo" >>> localStorage['foo'] "bar" >>> delete localStorage['foo'] >>> localStorage.length 0 >>> localStorage.not_set null
There’s also a more wordy API for people who like that sort of thing:
>>> localStorage.clear() >>> localStorage.setItem('foo', 'bar') >>> localStorage.getItem('foo') "bar" >>> localStorage.key(0) "foo" >>> localStorage.removeItem('foo') >>> localStorage.length 0
If you want to have a
localStoragedatabase mapped to the current session, you can usesessionStorage. It has the same interface aslocalStorage, but the lifetime ofsessionStorageis limited to the current browser window. You can follow links around the site in the same window andsessionStoragewill be maintained (going to different sites is fine too), but once that window is closed the database will be deleted.localStorageis for long-term storage, as the w3c spec instructs browsers to consider the data “potentially user-critical”.I was a tad disappointed when I found out that
localStorageonly supports storing strings, since I was hoping for something more structured. But with native JSON support it’s easy to create an object store on top oflocalStorage:Storage.prototype.setObject = function(key, value) { this.setItem(key, JSON.stringify(value)); } Storage.prototype.getObject = function(key) { return JSON.parse(this.getItem(key)); }
localStoragedatabases are scoped to an HTML5 origin, basically the tuple(scheme, host, port). This means that the database is shared across all pages on the same domain, even concurrently by multiple browser tabs. However, a page connecting overhttp://cannot see a database that was created during anhttps://session.localStorageandsessionStorageare supported by Firefox 3.5, Safari 4.0, and IE8. You can find more compatibility details on quirksmode.org, including more detail on the storage event. -
Introducing Aurora 9
We have just released Aurora 9 (download and test Aurora 9), which is planned to be the upcoming Firefox 9. In it, we have a number of new things that we hope will get you excited!
-
HTML5 drag and drop in Firefox 3.5
This post is from Les Orchard, who works on Mozilla’s web development team.
Introduction
Drag and drop is one of the most fundamental interactions afforded by graphical user interfaces. In one gesture, it allows users to pair the selection of an object with the execution of an action, often including a second object in the operation. It’s a simple yet powerful UI concept used to support copying, list reordering, deletion (ala the Trash / Recycle Bin), and even the creation of link relationships.
Since it’s so fundamental, offering drag and drop in web applications has been a no-brainer ever since browsers first offered mouse events in DHTML. But, although
mousedown,mousemove, andmouseupmade it possible, the implementation has been limited to the bounds of the browser window. Additionally, since these events refer only to the object being dragged, there’s a challenge to find the subject of the drop when the interaction is completed.Of course, that doesn’t prevent most modern JavaScript frameworks from abstracting away most of the problems and throwing in some flourishes while they’re at it. But, wouldn’t it be nice if browsers offered first-class support for drag and drop, and maybe even extended it beyond the window sandbox?
As it turns out, this very wish is answered by the HTML 5 specification section on new drag-and-drop events, and Firefox 3.5 includes an implementation of those events.
If you want to jump straight to the code, I’ve put together some simple demos of the new events.
I’ve even scratched an itch of my own and built the beginnings of an outline editor, where every draggable element is also a drop target—of which there could be dozens to hundreds in a complex document, something that gave me some minor hair-tearing moments in the past while trying to make do with plain old mouse events.
And, all the above can be downloaded or cloned from a GitHub repository I’ve created expecially for this article.
The New Drag and Drop Events
So, with no further ado, here are the new drag and drop events, in roughly the order you might expect to see them fired:
dragstart- A drag has been initiated, with the dragged element as the event target.
drag- The mouse has moved, with the dragged element as the event target.
dragenter- The dragged element has been moved into a drop listener, with the drop listener element as the event target.
dragover-
The dragged element has been moved over a drop listener, with the drop listener element as the event target. Since the default behavior is to cancel drops, returning
falseor callingpreventDefault()in the event handler indicates that a drop is allowed here. dragleave- The dragged element has been moved out of a drop listener, with the drop listener element as the event target.
drop- The dragged element has been successfully dropped on a drop listener, with the drop listener element as the event target.
dragend- A drag has been ended, successfully or not, with the dragged element as the event target.
Like the mouse events of yore, listeners can be attached to elements using
addEventListener()directly or by way of your favorite JS library.Consider the following example using jQuery, also available as a live demo:
<div id="newschool"> <div class="dragme">Drag me!</div> <div class="drophere">Drop here!</div> </div> <script type="text/javascript"> $(document).ready(function() { $('#newschool .dragme') .attr('draggable', 'true') .bind('dragstart', function(ev) { var dt = ev.originalEvent.dataTransfer; dt.setData("Text", "Dropped in zone!"); return true; }) .bind('dragend', function(ev) { return false; }); $('#newschool .drophere') .bind('dragenter', function(ev) { $(ev.target).addClass('dragover'); return false; }) .bind('dragleave', function(ev) { $(ev.target).removeClass('dragover'); return false; }) .bind('dragover', function(ev) { return false; }) .bind('drop', function(ev) { var dt = ev.originalEvent.dataTransfer; alert(dt.getData('Text')); return false; }); }); </script>Thanks to the new events and jQuery, this example is both short and simple—but it packs in a lot of functionality, as the rest of this article will explain.
Before moving on, there are at least three things about the above code that are worth mentioning:
-
Drop targets are enabled by virtue of having listeners for drop events. But, per the HTML 5 spec, draggable elements need an attribute of
draggable="true", set either in markup or in JavaScript.Thus,
$('#newschool .dragme').attr('draggable', 'true'). -
The original DOM event (as opposed to jQuery’s event wrapper) offers a property called
dataTransfer. Beyond just manipulating elements, the new drag and drop events accomodate the transmission of user-definable data during the course of the interaction. -
Since these are first-class events, you can apply the technique of Event Delegation.
What’s that? Well, imagine you have a list of 1000 list items—as part of a deeply-nested outline document, for instance. Rather than needing to attach listeners or otherwise fiddle with all 1000 items, simply attach a listener to the parent node (eg. the
<ul>element) and all events from the children will propagate up to the single parent listener. As a bonus, all new child elements added after page load will enjoy the same benefits.Check out this demo, and the associated JS code to see more about these events and Event Delegation.
Using dataTransfer
As mentioned in the last section, the new drag and drop events let you send data along with a dragged element. But, it’s even better than that: Your drop targets can receive data transferred by content objects dragged into the window from other browser windows, and even other applications.
Since the example is a bit longer, check out the live demo and associated code to get an idea of what’s possible with
dataTransfer.In a nutshell, the stars of this show are the
setData()andgetData()methods of thedataTransferproperty exposed by the Event object.The
setData()method is typically called in thedragstartlistener, loadingdataTransferup with one or more strings of content with associated recommended content types.For illustration, here’s a quick snippet from the example code:
var dt = ev.originalEvent.dataTransfer; dt.setData('text/plain', $('#logo').parent().text()); dt.setData('text/html', $('#logo').parent().html()); dt.setData('text/uri-list', $('#logo')[0].src);
On the other end,
getData()allows you to query for content by type (eg.text/htmlfollowed bytext/plain). This, in turn, allows you to decide on acceptable content types at the time of thedropevent or even duringdragoverto offer feedback for unacceptable types during the drag.Here’s another example from the receiving end of the example code:
var dt = ev.originalEvent.dataTransfer; $('.content_url .content').text(dt.getData('text/uri-list')); $('.content_text .content').text(dt.getData('text/plain')); $('.content_html .content').html(dt.getData('text/html'));
Where
dataTransferreally shines, though, is that it allows your drop targets to receive content from sources outside your defined draggable elements and even from outside the browser altogether. Firefox accepts such drags, and attempts to populatedataTransferwith appropriate content types extracted from the external object.Thus, you could select some text in a word processor window and drop it into one of your elements, and at least expect to find it available as
text/plaincontent.You can also select content in another browser window, and expect to see
text/htmlappear in your events. Check out the outline editing demo and see what happens when you try dragging various elements (eg. images, tables, and lists) and highlighted content from other windows onto the items there.Using Drag Feedback Images
An important aspect of the drag and drop interaction is a representation of the thing being dragged. By default in Firefox, this is a “ghost” image of the dragged element itself. But, the
dataTransferproperty of the original Event object exposes the methodsetDragImage()for use in customizing this representation.There’s a live demo of this feature, as well as associated JS code available. The gist, however, is sketched out in these code snippets:
var dt = ev.originalEvent.dataTransfer; dt.setDragImage( $('#feedback_image h2')[0], 0, 0); dt.setDragImage( $('#logo')[0], 32, 32); var canvas = document.createElement("canvas"); canvas.width = canvas.height = 50; var ctx = canvas.getContext("2d"); ctx.lineWidth = 8; ctx.moveTo(25,0); ctx.lineTo(50, 50); ctx.lineTo(0, 50); ctx.lineTo(25, 0); ctx.stroke(); dt.setDragImage(canvas, 25, 25);
You can supply a DOM node as the first parameter to
setDragImage(), which includes everything from text to images to<canvas>elements. The second two parameters indicate at what left and top offset the mouse should appear in the image while dragging.For example, since the
#logoimage is 64×64, the parameters in the secondsetDragImage()method places the mouse right in the center of the image. On the other hand, the first call positions the feedback image such that the mouse rests in the upper left corner.Using Drop Effects
As mentioned at the start of this article, the drag and drop interaction has been used to support actions such as copying, moving, and linking. Accordingly, the HTML 5 specification accomodates these operations in the form of the
effectAllowedanddropEffectproperties exposed by the Event object.For a quick fix, check out the a live demo of this feature, as well as the associated JS code.
The basic idea is that the
dragstartevent listener can set a value foreffectAllowedlike so:var dt = ev.originalEvent.dataTransfer; switch (ev.target.id) { case 'effectdrag0': dt.effectAllowed = 'copy'; break; case 'effectdrag1': dt.effectAllowed = 'move'; break; case 'effectdrag2': dt.effectAllowed = 'link'; break; case 'effectdrag3': dt.effectAllowed = 'all'; break; case 'effectdrag4': dt.effectAllowed = 'none'; break; }
The choices available for this property include the following:
none- no operation is permitted
copy- copy only
move- move only
link- link only
copyMove- copy or move only
copyLink- copy or link only
linkMove- link or move only
all- copy, move, or link
On the other end, the
dragoverevent listener can set the value of thedropEffectproperty to indicate the expected effect invoked on a successful drop. If the value does not match up witheffectAllowed, the drop will be considered cancelled on completion.In the a live demo, you should be able to see that only elements with matching effects can be dropped into the appropriate drop zones. This is accomplished with code like the follwoing:
var dt = ev.originalEvent.dataTransfer; switch (ev.target.id) { case 'effectdrop0': dt.dropEffect = 'copy'; break; case 'effectdrop1': dt.dropEffect = 'move'; break; case 'effectdrop2': dt.dropEffect = 'link'; break; case 'effectdrop3': dt.dropEffect = 'all'; break; case 'effectdrop4': dt.dropEffect = 'none'; break; }
Although the OS itself can provide some feedback, you can also use these properties to update your own visible feedback, both on the dragged element and on the drop zone itself.
Conclusion
The new first-class drag and drop events in HTML5 and Firefox make supporting this form of UI interaction simple, concise, and powerful in the browser. But beyond the new simplicity of these events, the ability to transfer content between applications opens brand new avenues for web-based applications and collaboration with desktop software in general.
-
multiple file input in Firefox 3.6
Firefox 3.6 supports multiple file input. This new capability allows you to get several files as input at once, using standard technologies. This is a big improvement, since you used to be constrained to one file at a time, or needed to use a third party (proprietary) application. This will be particularly useful, for example, for photo uploads.
The input tag
To let your user select a local file, use the input tag on your Web page. This will show the file picker to the user:
<input type="file"/>In Firefox 3.6, the input tag has been expanded to support multiple files:
<input type="file" multiple=""/>The user will still see the same file picker, but will be able to select more than one file.
The form tag
You can still use the classic
formmechanism:<form method="post" action="upload.php" enctype="multipart/form-data"> <input name='uploads[]' type="file" multiple=""/> <input type="submit" value="Send"> </form>
If the server side code is in PHP, don’t forget to make sure that the value of the
nameattribute has brackets. The brackets are not from the HTML specification, but are required to manipulate the result of the request as an array (see PHP documentation).Here’s an example, which goes through the file list and prints each file name:
foreach ($_FILES['uploads']['name'] as $filename) { echo '<li>' . $filename . '</li>'; }
Using File API
Firefox 3.6 also supports FileAPI. This allows you to do extra processing on the client slide before sending the files to the server. You can access the selected files with the
filesproperty of the input DOM element and then manipulate the files using the FileAPI.For example, here’s how to get the name of each file selected by the user. This is done on the client side, unlike the previous PHP example.
var input = document.querySelector("input[type='file']"); // You've selected input.files.length files for (var i = 0; i < input.files.length; i++) { // input.files[i] is a file object var li = document.createElement("li"); li.innerHTML = input.files[i].name; ul.appendChild(li); }
Demo
See this mechanism in action in our multiple file input demo. You’ll need Firefox 3.6 (beta).
Documentation
To learn more about multiple file input, check out the documentation on MDC.
-
DOM MutationObserver – reacting to DOM changes without killing browser performance.
DOM Mutation Events seemed like a great idea at the time – as web developers create a more dynamic web it seems natural that we would welcome the ability to listen for changes in the DOM and react to them. In practice however DOM Mutation Events were a major performance and stability issue and have been deprecated for over a year.
The original idea behind DOM Mutation Events is still appealing, however, and so in September 2011 a group of Google and Mozilla engineers announced a new proposal that would offer similar functionality with improved performance: DOM MutationObserver. This new DOM Api is available in Firefox and Webkit nightly builds, as well as Chrome 18.
At it’s simplest, a MutationObserver implementation looks like this:
// select the target node var target = document.querySelector('#some-id'); // create an observer instance var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { console.log(mutation.type); }); }); // configuration of the observer: var config = { attributes: true, childList: true, characterData: true } // pass in the target node, as well as the observer options observer.observe(target, config); // later, you can stop observing observer.disconnect();
The key advantage to this new specification over the deprecated DOM Mutation Events spec is one of efficiency. If you are observing a node for changes, your callback will not be fired until the DOM has finished changing. When the callback is triggered, it is supplied a list of the changes to the DOM, which you can then loop through and choose to react to.
This also means that any code you write will need to process the observer results in order to react to the changes you are looking for. Here is a compact example of an observer that listens for changes in an editable ordered list:
<!DOCTYPE html> <ol contenteditable oninput=""> <li>Press enter</li> </ol> <script> var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; var list = document.querySelector('ol'); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.type === 'childList') { var list_values = [].slice.call(list.children) .map( function(node) { return node.innerHTML; }) .filter( function(s) { if (s === '<br>') { return false; } else { return true; } }); console.log(list_values); } }); }); observer.observe(list, { attributes: true, childList: true, characterData: true }); </script>
If you want to see this code running, I’ve put it up on jsbin here:
http://jsbin.com/ivamoh/53/edit
If you play with the live example, you’ll notice some quirks in behaviour, in particular that the callback is triggered when you press enter in each li, in particular when the user action results in a node being added or removed from the DOM. This is an important distinction to be made from other techniques such as binding events to key presses or more common events like ‘click’. MutationObservers work differently from these techniques because they are triggered by changes in the DOM itself, not by events generated either via JS or user interaction.
So what are these good for?
I don’t expect most JS hackers are going to run out right now and start adding mutation observers to their code. Probably the biggest audience for this new api are the people that write JS frameworks, mainly to solve problems and create interactions they could not have done previously, or at least not with reasonable performance. Another use case would be situations where you are using frameworks that manipulate the DOM and need to react to these modifications efficiently ( and without setTimeout hacks! ).
Another common use of the Dom Mutation Events api is in browser extensions, and in the next week or so I’m going to publish a follow-up post on how MutationObservers are particularly useful when interacting with web content in a Firefox Add-on.
Resources
- Original Proposal
- W3C Spec
- Screencast by Rafael Weinstein
- Mutation Summary, a JS library that simplifies MutationObserver usage.
-
Announcing Firefox Aurora 10
We’re happy to announce the availability of Aurora 10.
(Download and Test Aurora 10)In additional to the normal improvements that you’ve come to expect like performance, security and bug fixes, Aurora 10 focuses in HTML5 enhancements.
New additions
- HTML5 Visibility API
- createProcessingInstruction
- WebGL antialiasing
- 3D Transforms
- Visibility API
- Document.mozFullScreenEnabled\
Developer Tools
Aurora 10 also implements incremental enhancements like IndexedDB setVersion API changes. Ongoing detailed attention to evolving specifications help to keep Firefox at the front of the Web revolution. (Read more about IndexedDB on MDN.)
DOM
- We now fire a “load” event on stylesheet linking when the sheet load finishes or “error” if the load fails.
- We turn the POSTDATA prompt into an information page (when navigating in session history).
- We only forward event attributes on body/frameset to the window if we also forward the corresponding on* property.
- We no longer allow more than one call to window.open() when we allow popups.
- We fixed a bug where a success callback never fired when a position update is triggered after getCurrentPosition().
- We removed replaceWholeText().
- We fixed an error with createPattern(zero-size canvas).
- We now handle putImageData(nonfinite) correctly.
- We now throw INVALID_STATE_ERR when dispatching uninitialized events.
- We’ve made Document.documentURI readonly.
- We fixed document.importNode to comply with optional argument omitted.
Web workers
- We now allow data URLs.
- We implemented event.stopImmediatePropagation in workers.
- We made XHR2 response/responseType work in Web Workers.
Graphics
- We implement the WebGL OES_standard_derivatives extension.
- We implement minimal-capabilities WebGL mode.
JavaScript
- The function caller property no longer skips over eval frames.
- We fixed E4X syntax so that it is not accepted in ES5 strict mode.
- weakmap.set no longer returns itself instead of undefined.
- We implemented the battery API.
Offline: IndexedDB enhancements
- IndexedDB setVersion API changes
- Added support for IDBObjectStore/IDBIndex.count
- Various methods accept both keys and KeyRanges.
- Added support for IDBCursor.advance.
- Implemented deleteDatabase.
- objectStoreNames are no longer updated on closed databases when another connection adds or removes object stores
- IDBObjectStore.delete and IDBCursor.delete now return undefined.
- No longer throws an error if there are unknown properties in the options objects to createObjectStore/createIndex.
- We now the errorCode to “ABORT_ERR” for all pending requests when IDBTransaction.abort() is called.
- Fixed the sort order for indexes.
Layout
- We have updated the current rule for handling malformed media queries.
- We now support the HTML5 <bdi> element and CSS property unicode-bidi: isolate.
- The CSS3 implementation now supports unicode-bidi: plaintext.
Media
- Implemented Document.mozFullScreenEnabled.
- Enabled the DOM full-screen API on desktop Firefox by default.
-
DOM selectors API in Firefox 3.5
The Selectors API recommendation, published by the W3C, is a relatively new effort that gives JavaScript developers the ability to find DOM elements on a page using CSS selectors. This single API takes the complicated process of traversing and selecting elements from the DOM and unifies it under a simple unified interface.
Out of all the recent work to come out of the standards process this is one of the better-supported efforts across all browsers: Usable today in Internet Explorer 8, Chrome, and Safari and arriving in Firefox 3.5 and Opera 10.
Using
querySelectorAllThe Selectors API provides two methods on all DOM documents, elements, and fragments:
querySelectorandquerySelectorAll. The methods work virtually identically, they both accept a CSS selector and return the resulting DOM elements (the exception being thatquerySelectoronly returns the first element).For example, given the following HTML snippet:
<div id="id" class="class"> <p>First paragraph.</p> <p>Second paragraph.</p> </div>
We would be able to use
querySelectorAllto make the background of all the paragraphs, inside the div with the ID of ‘id’, red.var p = document.querySelectorAll("#id p"); for ( var i = 0; i < p.length; i++ ) { p[i].style.backgroundColor = "red"; }
Or we could find the first child paragraph of a div that has a class of ‘class’ and give it a class name of ‘first’.
document.querySelector("div.class > p:first-child") .className = "first";
Normally these types of traversals would be very tedious in long-form JavaScript/DOM code, taking up multiple lines and queries each.
While the actual use of the Selectors API methods is relatively simple (each taking a single argument) the challenging part comes in when choosing which CSS selectors to use. The Selectors API taps in to the native CSS selectors provided by the browser, for use in styling elements with CSS. For most browsers (Firefox, Safari, Chrome, and Opera) this means that you have access to the full gamut of CSS 3 selectors. Internet Explorer 8 provides a more-limited subset that encompasses CSS 2 selectors (which are still terribly useful).
The biggest hurdle, for most new users to the Selectors API, is determining which CSS selectors are appropriate for selecting the elements that you desire – especially since most developers who write cross-browser code only have significant experience with a limited subset of fully-working CSS 1 selectors.
While the CSS 2 and CSS 3 selector specifications can serve as a good start for learning more about what’s available to you there also exist a number of useful guides for learning more:
- CSS 3 Selectors Explained
- XML.com: CSS 3 Selectors
- jQuery Selector Reference (Note: Also includes custom, non-standard, selectors.)
Implementations in the Wild
The most compelling use case of the Selectors API is not its direct use by web developers, but its use by 3rd-party libraries that already provide DOM CSS selector functionality. The trickiest problem towards adopting the use of the Selectors API, today, is that it isn’t available in all browsers that users develop for (this includes IE 6, IE 7 and Firefox 3). Thus, until those browsers are no longer used, we must use some intermediary utility to recreate the full DOM CSS selector experience.
Thankfully, a number of libraries already exist that provide an API compatible with the Selectors API (in fact, much of the inspiration for the Selectors API comes from the existence of these libraries in the first place). Additionally, many of these implementations already use the Selectors API behind the scenes. This means that you can use using DOM CSS selectors in all browsers that you support AND get the benefit of faster performance from the native Selectors API implementation, with no work to you.
Some existing implementations that gracefully use the new Selectors API are:
It’s important to emphasize the large leap in performance that you’ll gain from using this new API (in comparison to the traditional mix of DOM and JavaScript that you must employ). You can really see the difference when you look at the improvement that occurred when JavaScript libraries began to implement the new Selectors API.
When some tests were run previously the results were as follows:
You can see the dramatic increase in performance that occurred once the libraries began using the native Selectors API implementations – it’s quite likely that this performance increase will happen in your applications, as well.
Test Suite
To coincide with the definition of the Selectors API specification a Selectors API test suite was created by John Resig of Mozilla. This test suite can be used as a way to determine the quality of the respective Selectors API implementations in the major browsers.
The current results for the browsers that support the API are:
- Firefox 3.5: 99.3%
- Safari 4: 99.3%
- Chrome 2: 99.3%
- Opera 10b1: 97.5%
- Internet Explorer 8: 47.4%
Internet Explorer 8, as mentioned before, is missing most CSS 3 selectors – thus failing most of the associated tests.
As it stands, the Selectors API should serve as a simple, and fast, way of selecting DOM elements on a page. It’s already benefiting those who use JavaScript libraries that provide similar functionality so feel free to dig in today and give the API a try.
- This is Page 1 of 2
- You're on page 1
- Go to page 2
- Next
