This is a re-post from Rob Cambell’s personal weblog. Firebug 1.5 is the first release that will work with the upcoming Firefox 3.6 and also also works with Firefox 3.5. It’s currently in beta and will be available before the release of Firefox 3.6.
As of this minute, Firebug 1.5 is sitting comfortably in its third beta and available for download. This version is shaping up to be our best release yet and initial reports have been very positive regarding its stability, UI improvements and new features. So let’s take a look at some of the new features.
Improved Net Panel accuracy
One of the problems with Firebug’s Net panel in the past has been inaccurate timings. Because Firebug is entirely written in JavaScript some network and UI activity could block Firebug during long operations and cause the timings displayed there to be less than accurate. This has finally been corrected with the landing of a new service called the http-activity-distributor. For more details on the implementation and use of the newly-improved Net panel, see Honza’s blog post on the topic.
New Break Functionality
In Firebug 1.4, we introduced the concept of “break-on-next” to the Script panel. This was a “pause” button sitting between the inspect icon and the Console tab. In 1.5, we’ve extended this concept to the Console, HTML and Net panels to allow more exciting types of breaks.
In the Console, we’ve replaced the mini-menu Break-on-Errors option with the pause button. The reasons for this possibly contentious change was it made for a more consistent use of menus and the break button. Now, to enable Break on Errors, select the Console panel and hit the pause button. You’ll see that familiar glow to indicate that it’s waiting for an error. Now whenever an error occurs on the page, you’ll be dropped into the script panel at the line where the error occurred.
The HTML panel’s break button is a little different. This is the Break-on-Mutation feature. When this is enabled, whenever a bit of JavaScript modifies an HTML element, you’ll be taken to the Script panel and the modifying code while be highlighted. Related to this, you should be able to see modified HTML occurring in real-time in the HTML panel with affected elements and attributes being highlighted as they change in the page.
Finally in the Net panel, the break button acts as a Break-on-XHR button. This is intended to help debug AJAX apps by allowing you to halt the debugger during an XmlHttpRequest send. As in the other break types, you’ll be transported to the script panel when an XHR object fires off its request and you’ll be given the option to copy the message.
John Barton and Honza have written a great interactive demo page describing these new features on getfirebug.com.
Mixed Development
We made a few tweaks to the UI in this version. We replaced the “Off” label with a single “power” button (or window close button on Mac) as promised during the last release.
Kevin Decker added the search panel originally intended for version 1.4 with some nice options.
Persist option on Console and Net Panel. Save your data!
Improvements to the Inspector.
Still more to come. Between now and final release we plan on hunting down a few more bugs to make this even more stable. Feel free to download and give it a try.
This post was written by David Mandelin who works on Mozilla’s JavaScript team.
Firefox 3.5 has a new JavaScript engine, TraceMonkey, that runs many JavaScript programs 3-4x faster than Firefox 3, speeding up existing web apps and enabling new ones. This article gives a peek under the hood at the major parts of TraceMonkey and how they speed up JS. This will also explain what kinds of programs get the best speedup from TraceMonkey and what kinds of things you can do to get your program to run faster.
Why it’s hard to run JS fast: dynamic typing
High-level dynamic languages such as JavaScript and Python make programming more productive, but they have always been slow compared to statically typed languages like Java or C. A typical rule of thumb was that a JS program might be 10x slower than an equivalent Java program.
There are two main reasons JS and other dynamic scripting languages usually run slower than Java or C. The first reason is that in dynamic languages it is generally not possible to determine the types of values ahead of time. Because of this, the language must store all values in a generic format and process values using generic operations.
In Java, by contrast, the programmer declares types for variables and methods, so the compiler can determine the types of values ahead of time. The compiler can then generate code that uses specialized formats and operations that run much faster than generic operations. I will call these type-specialized operations.
The second main reason that dynamic languages run slower is that scripting languages are usually implemented with interpreters, while statically typed languages are compiled to native code. Interpreters are easier to create, but they incur extra runtime overhead for tracking their internal state. Languages like Java compile to machine language, which requires almost no state tracking overhead.
Let’s make this concrete with a picture. Here are the slowdowns in picture form for a simple numeric add operation: a + b, where a and b are integers. For now, ignore the rightmost bar and focus on the comparison of the Firefox 3 JavaScript interpreter vs. a Java JIT. Each column shows the steps that have to be done to complete the add operation in each programming language. Time goes downward, and the height of each box is proportional to the time it takes to finish the steps in the box.
In the middle, Java simply runs one machine language add instruction, which runs in time T (one processor cycle). Because the Java compiler knows that the operands are standard machine integers, it can use a standard integer add machine language instruction. That’s it.
On the left, SpiderMonkey (the JS interpreter in FF3) takes about 40 times as long. The brown boxes are interpreter overhead: the interpreter must read the add operation and jump to the interpreter’s code for a generic add. The orange boxes represent extra work that has to be done because the interpreter doesn’t know the operand types. The interpreter has to unpack the generic representations of a and i, figure out their types, select the specific addition operation, convert the values to the right types, and at the end, convert the result back to a generic format.
The diagram shows that using an interpreter instead of a compiler is slowing things down a little bit, but not having type information is slowing things down a lot. If we want JS to run more than a little faster than in FF3, by Amdahl’s law, we need to do something about types.
Getting types by tracing
Our goal in TraceMonkey is to compile type-specialized code. To do that, TraceMonkey needs to know the types of variables. But JavaScript doesn’t have type declarations, and we also said that it’s practically impossible for a JS engine to figure out the types ahead of time. So if we want to just compile everything ahead of time, we’re stuck.
So let’s turn the problem around. If we let the program run for a bit in an interpreter, the engine can directly observe the types of values. Then, the engine can use those types to compile fast type-specialized code. Finally, the engine can start running the type-specialized code, and it will run much faster.
There are a few key details about this idea. First, when the program runs, even if there are many if statements and other branches, the program always goes only one way. So the engine doesn’t get to observe types for a whole method — the engine observes types through the paths, which we call traces, that the program actually takes. Thus, while standard compilers compile methods, TraceMonkey compiles traces. One side benefit of trace-at-a-time compilation is that function calls that happen on a trace are inlined, making traced function calls very fast.
Second, compiling type-specialized code takes time. If a piece of code is going to run only one or a few times, which is common with web code, it can easily take more time to compile and run the code than it would take to simply run the code in an interpreter. So it only pays to compile hot code (code that is executed many times). In TraceMonkey, we arrange this by tracing only loops. TraceMonkey initially runs everything in the interpreter, and starts recording traces through a loop once it gets hot (runs more than a few times).
Tracing only hot loops has an important consequence: code that runs only a few times won’t speed up in TraceMonkey. Note that this usually doesn’t matter in practice, because code that runs only a few times usually runs too fast to be noticeable. Another consequence is that paths through a loop that are not taken at all never need to be compiled, saving compile time.
Finally, above we said that TraceMonkey figures out the types of values by observing execution, but as we all know, past performance does not guarantee future results: the types might be different the next time the code is run, or the 500th next time. And if we try to run code that was compiled for numbers when the values are actually strings, very bad things will happen. So TraceMonkey must insert type checks into the compiled code. If a check doesn’t pass, TraceMonkey must leave the current trace and compile a new trace for the new types. This means that code with many branches or type changes tends to run a little slower in TraceMonkey, because it takes time to compile the extra traces and jump from one to another.
TraceMonkey in action
Now, we’ll show tracing in action by example on this sample program, which adds the first N whole numbers to a starting value:
function addTo(a, n){for(var i =0; i < n;++i)
a = a + i;return a;}var t0 =new Date();var n = addTo(0, 10000000);print(n);print(new Date()- t0);
TraceMonkey always starts running the program in the interpreter. Every time the program starts a loop iteration, TraceMonkey briefly enters monitoring mode to increment a counter for that loop. In FF3.5, when the counter reaches 2, the loop is considered hot and it’s time to trace.
Now TraceMonkey continues running in the interpreter but starts recording a trace as the code runs. The trace is simply the code that runs up to the end of the loop, along with the types used. The types are determined by looking at the actual values. In our example, the loop executes this sequence of JavaScript statements, which becomes our trace:
a = a + i;// a is an integer number (0 before, 1 after)++i;// i is an integer number (1 before, 2 after)if(!(i < n))// n is an integer number (10000000)break;
That’s what the trace looks like in a JavaScript-like notation. But TraceMonkey needs more information in order to compile the trace. The real trace looks more like this:
trace_1_start:++i;// i is an integer number (0 before, 1 after)
temp = a + i;// a is an integer number (1 before, 2 after)if(lastOperationOverflowed())
exit_trace(OVERFLOWED);
a = temp;if(!(i < n))// n is an integer number (10000000)
exit_trace(BRANCHED);goto trace_1_start;
This trace represents a loop, and it should be compiled as a loop, so we express that directly using a goto. Also, integer addition can overflow, which requires special handling (for example, redoing with floating-point addition), which in turn requires exiting the trace. So the trace must include an overflow check. Finally, the trace exits in the same way if the loop condition is false. The exit codes tell TraceMonkey why the trace was exited, so that TraceMonkey can decide what to do next (such as whether to redo the add or exit the loop). Note that traces are recorded in a special internal format that is never exposed to the programmer — the notation used above is just for expository purposes.
After recording, the trace is ready to be compiled to type-specialized machine code. This compilation is performed by a tiny JIT compiler (named, appropriately enough, nanojit) and the results are stored in memory, ready to be executed by the CPU.
The next time the interpreter passes the loop header, TraceMonkey will start executing the compiled trace. The program now runs very fast.
On iteration 65537, the integer addition will overflow. (2147450880 + 65537 = 2147516417, which is greater than 2^31-1 = 2147483647, the largest signed 32-bit integer.) At this point, the trace exits with an OVERFLOWED code. Seeing this, TraceMonkey will return to interpreter mode and redo the addition. Because the interpreter does everything generically, the addition overflow is handled and everything works as normal. TraceMonkey will also start monitoring this exit point, and if the overflow exit point ever becomes hot, a new trace will be started from that point.
But in this particular program, what happens instead is that the program passes the loop header again. TraceMonkey knows it has a trace for this point, but TraceMonkey also knows it can’t use that trace because that trace was for integer values, but a is now in a floating-point format. So TraceMonkey records a new trace:
trace_2_start:++i;// i is an integer number
a = a + i;// a is a floating-point numberif(!(i < n))// n is an integer number (10000000)
exit_trace(BRANCHED);goto trace_2_start;
TraceMonkey then compiles the new trace, and on the next loop iteration, starts executing it. In this way, TraceMonkey keeps the JavaScript running as machine code, even when types change. Eventually the trace will exit with a BRANCHED code. At this point, TraceMonkey will return to the interpreter, which takes over and finishes running the program.
Here are the run times for this program on my laptop (2.2GHz MacBook Pro):
System
Run Time (ms)
SpiderMonkey (FF3)
990
TraceMonkey (FF3.5)
45
Java (using int)
25
Java (using double)
74
C (using int)
5
C (using double)
15
This program gets a huge 22x speedup from tracing and runs about as fast as Java! Functions that do simple arithmetic inside loops usually get big speedups from tracing. Many of the bit operation and math SunSpider benchmarks, such bitops-3bit-bits-in-byte, ctrypto-sha1, and math-spectral-norm get 6x-22x speedups.
Functions that use more complex operations, such as object manipulation, get a smaller speedup, usually 2-5x. This follows mathematically from Amdahl’s law and the fact that complex operations take longer. Looking back at the time diagram, consider a more complex operation that takes time 30T for the green part. The orange and brown parts will still be about 30T together, so eliminating them gives a 2x speedup. The SunSpider benchmark string-fasta is an example of this kind of program: most of the time is taken by string operations that have a relatively long time for the green box.
Here is a version of our example program you can try in the browser:
Numerical result:
Run time:
Average run time:
Understanding and fixing performance problems
Our goal is to make TraceMonkey reliably fast enough that you can write your code in the way that best expresses your ideas, without worrying about performance. If TraceMonkey isn’t speeding up your program, we hope you’ll report it as a bug so we can improve the engine. That said, of course, you may need your program to run faster in today’s FF3.5. In this section, we’ll explain some tools and techniques for fixing performance of a program that doesn’t get a good (2x or more) speedup with the tracing JIT enabled. (You can disable the jit by going to about:config and setting the pref javascript.options.jit.content to false.)
The first step is understanding the cause of the problem. The most common cause is a trace abort, which simply means that TraceMonkey was unable to finish recording a trace, and gave up. The usual result is that the loop containing the abort will run in the interpreter, so you won’t get a speedup on that loop. Sometimes, one path through the loop is traced, but there is an abort on another path, which will cause TraceMonkey to switch back and forth between interpreting and running native code. This can leave you with a reduced speedup, no speedup, or even a slowdown: switching modes takes time, so rapid switching can lead to poor performance.
With a debug build of the browser or a JS shell (which I build myself – we don’t publish these builds) you can tell TraceMonkey to print information about aborts by setting the TMFLAGS environment variable. I usually do it like this:
The minimal option prints out all the points where recording starts and where recording successfully finishes. This gives a basic idea of what the tracer is trying to do. The abort option prints out all the points where recording was aborted due to an unsupported construct. (Setting TMFLAGS=help will print the list of other TMFLAGS options and then exit.)
(Note also that TMFLAGS is the new way to print the debug information. If you are using a debug build of the FF3.5 release, the environment variable setting is TRACEMONKEY=abort.)
Here’s an example program that doesn’t get much of a speedup in TraceMonkey.
function runExample2(){var t0 =new Date;var sum =0;for(var i =0; i <100000;++i){
sum += i;}var prod =1;for(var i =1; i <100000;++i){eval("prod *= i");}var dt =new Date - t0;
document.getElementById(example2_time').innerHTML = dt + ' ms';
}
Run time:
If we set TMFLAGS=minimal,abort, we’ll get this:
Recording starting from ab.js:5@23
recording completed at ab.js:5@23 via closeLoop
Recording starting from ab.js:5@23
recording completed at ab.js:5@23 via closeLoop
Recording starting from ab.js:10@63
Abort recording of tree ab.js:10@63 at ab.js:11@70: eval.
Recording starting from ab.js:10@63
Abort recording of tree ab.js:10@63 at ab.js:11@70: eval.
Recording starting from ab.js:10@63
Abort recording of tree ab.js:10@63 at ab.js:11@70: eval.
The first two pairs of lines show that the first loop, starting at line 5, traced fine. The following lines showed that TraceMonkey started tracing the loop on line 10, but failed each time because of an eval.
An important note about this debug output is that you will typically see some messages referring to inner trees growing, stabilizing, and so on. These really aren’t problems: they usually just indicate a delay in finishing tracing a loop because of the way TraceMonkey links inner and outer loops. And in fact, if you look further down the output after such aborts, you will usually see that the loops eventually do trace.
Otherwise, aborts are mainly caused by JavaScript constructs that are not yet supported by tracing. The trace recording process is easier to implement for a basic operation like + than it is for an advanced feature like arguments. We didn’t have time to do robust, secure tracing of every last JavaScript feature in time for the FF3.5 release, so some of the more advanced ones, like arguments, aren’t traced in FF3.5.0. Other advanced features that are not traced include getters and setters, with, and eval. There is partial support for closures, depending on exactly how they are used. Refactoring to avoid these constructs can help performance.
Two particularly important JavaScript features that are not traced are:
Recursion. TraceMonkey doesn’t see repetition that occurs through recursion as a loop, so it doesn’t try to trace it. Refactoring to use explicit for or while loops will generally give better performance.
Getting or setting a DOM property. (DOM method calls are fine.) Avoiding these constructs is generally impossible, but refactoring the code to move DOM property access out of hot loops and performance-critical segments should help.
We are actively working on tracing all the features named above. For example, support for tracing arguments is already available in nightly builds.
Here is the slow example program refactored to avoid eval. Of course, I could have simply done the multiplication inline. Instead, I used a function created by eval because that’s a more general way of refactoring an eval. Note that the eval still can’t be traced, but it only runs once so it doesn’t matter.
function runExample3(){var t0 =new Date;var sum =0;for(var i =0; i <100000;++i){
sum += i;}var prod =1;var mul =eval("(function(i) { return prod * i; })");for(var i =1; i <100000;++i){
prod = mul(i);}var dt =new Date - t0;
document.getElementById('example3_time').innerHTML= dt +' ms';}
Run time:
There are a few more esoteric situations that can also hurt tracing performance. One of them is trace explosion, which happens when a loop has many paths through it. Consider a loop with 10 if statements in a row: the loop has 1024 paths, potentially causing 1024 traces to be recorded. That would use up too much memory, so TraceMonkey caps each loop at 32 traces. If the loop has fewer than 32 hot traces, it will perform well. But if each path occurs with equal frequency, then only 3% of the paths are traced, and performance will suffer.
This kind of problem is best analyzed with TraceVis, which creates visualizations of TraceMonkey performance. Currently, the build system only supports enabling TraceVis for shell builds, but the basic system can also run in the browser, and there is ongoing work to enable TraceVis in a convenient form in the browser.
The blog post on TraceVis is currently the most detailed explanation of what the diagrams mean and how to use them to diagnose performance problems. The post also contains a detailed analysis of a diagram that is helpful in understanding how TraceMonkey works in general.
Comparative JITerature
Here I will give a few comparisons to other JavaScript JIT designs. I’ll focus more on hypothetical designs than competing engines, because I don’t know details about them — I’ve read the release information and skimmed a few bits of code. Another big caveat is that real-world performance depends at least as much on engineering details as it does on engine architecture.
One design option could be a called a per-method non-specializing JIT. By this, I mean a JIT compiler that compiles a method at a time and generates generic code, just like what the interpreter does. Thus, the brown boxes from our diagrams are cut out. This kind of JIT doesn’t need to take time to record and compile traces, but it also does not type-specialize, so the orange boxes remain. Such an engine can still be made pretty fast by carefully designing and optimizing the orange box code. But the orange box can’t be completely eliminated in this design, so the maximum performance on numeric programs won’t be as good as a type-specializing engine.
As far as I can tell, as of this writing Nitro and V8 are both lightweight non-specializing JITs. (I’m told that V8 does try to guess a few types by looking at the source code (such as guessing that a is an integer in a >> 2) in order to do a bit of type specialization.) It seem that TraceMonkey is generally faster on numeric benchmarks, as predicted above. But TraceMonkey suffers a bit on benchmarks that use more objects, because our object operations and memory management haven’t been optimized as heavily.
A further development of the basic JIT is the per-method type-specializing JIT. This kind of a JIT tries to type-specialize a method based on the argument types the method is called with. Like TraceMonkey, this requires some runtime observation: the basic design checks the argument types each time a method is called, and if those types have not been seen before, compiles a new version of the method. Also like TraceMonkey, this design can heavily specialize code and remove both the brown and orange boxes.
I’m not aware that anyone has deployed a per-method type-specializing JIT for JavaScript, but I wouldn’t be surprised if people are working on it.
The main disadvantage of a per-method type-specializing JIT compared to a tracing JIT is that the basic per-method JIT only directly observes the input types to a method. It must try to infer types for variables inside the method algorithmically, which is difficult for JavaScript, especially if the method reads object properties. Thus, I would expect that a per-method type-specializing JIT would have to use generic operations for some parts of the method. The main advantage of the per-method design is that the method needs to be compiled exactly once per set of input types, so it’s not vulnerable to trace explosion. In turn, I think a per-method JIT would tend to be faster on methods that have many paths, and a tracing JIT would tend to be faster on highly type-specializable methods, especially if the method also reads a lot of values from properties.
Conclusion
By now, hopefully you have a good idea of what makes JavaScript engines fast, how TraceMonkey works, and how to analyze and fix some performance issues that may occur running JavaScript under TraceMonkey. Please report bugs if you run into any significant performance problems. Bug reports are also a good place for us to give additional tuning advice. Finally, we’re trying to improve constantly, so check out nightly TraceMonkey builds if you’re into the bleeding edge.
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, and mouseup made 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?
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 false or calling preventDefault() 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.
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.
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.
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.
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.
In a nutshell, the stars of this show are the setData() and getData() methods of the dataTransfer property exposed by the Event object.
The setData() method is typically called in the dragstart listener, loading dataTransfer up 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/html followed by text/plain). This, in turn, allows you to decide on acceptable content types at the time of the drop event or even during dragover to 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 dataTransfer really 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 populate dataTransfer with 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/plain content.
You can also select content in another browser window, and expect to see text/html appear 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 dataTransfer property of the original Event object exposes the method setDragImage() 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:
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 #logo image is 64×64, the parameters in the second setDragImage() 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 effectAllowed and dropEffect properties exposed by the Event object.
The basic idea is that the dragstart event listener can set a value for effectAllowed like 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 dragover event listener can set the value of the dropEffect property to indicate the expected effect invoked on a successful drop. If the value does not match up with effectAllowed, 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.
Today’s demo comes from Lim Chee Aun, the creator of the Phoenity icons and themes, and web developer in Malaysia.
The border-radius property is probably one of the most interesting parts of the CSS3 specification, where it allows you to create rounded corners on elements. For example:
div {
border-radius:10px;
-moz-border-radius:10px;
-webkit-border-radius:10px;}
In Firefox 3.5, -moz-border-radius has been updated to match the latest revision to the CSS3 specification. Thus now, we can create elliptical borders.
So, what does that mean? According to the specification, the syntax is:
It shows the set of values accepted for this property, plus an optional slash with another set of values. This is where the magic comes from. If two sets of values are given, values before the slash set the horizontal radius and values after it set the vertical radius.
This opens a few interesting possibilities. This demo shows some experiments on certain types of shapes that you can play with.
In the demo, it’s not only possible to modify the border width, style and radius, but also the style as well, such as ridge, double and groove. As of now, dotted and dashed doesn’t work yet and being rendered as solid. For more information, see Mozilla bug 431176 .