This is how a tweet from @SubtleGradient, re-tweeted by @jdalton, has been able to steal my rest tonight ... and this post is the consequence ...
What's new in a nutshell
There is a W3C Recommendation about addEventListener behavior, which clearly specify the second argument as an EventListener.
The new part is that no library I know has ever used a proper EventListener interface, preferring the classic attached callback instead.
The most common case that may disappear is well explained in this MDC addEventListener page.
Rather than bind inline or add anonymous functions to make our object call context preserved, we can simply add an handleEvent method to whatever object and pass it as EventListener.
Moreover, being close to full ES5 support and "use strict" directive where arguments.callee disappears, it may be more than handy to be able to perform such operation:
document.addEventListener("click", {
handleEvent: function(evt){ // 1 shot callback event example switch(evt.target.nodeType){ case1: case9:
evt.target.removeEventListener(
evt.type, this, // here we are! false ); break; } } }, false);
An opened door for custom listeners
As I have recently posted, custom listeners implementation can be truly handy when we are dealing with events driven applications, but as soon as I have read the tweet, I had to rewrite a fresh new way to create a listener. Please note that following code is assuming that the browser supports both DOM Level 2 and Array extras, which is true for all modern browsers, mobile oriented included.
// a function declaration reused internally function notifyEvent(callback, i, stack){ // use DOM Level 0 events strategy // to stop the loop if necessary // checking if the result is exactly false if(callback.call( // the curent object as context this, // the classic event as first argument
event, // the called callback (life easier)
callback, // again the current context // if the callback has been bound this ) === false){ // if false, reassign the current stack ...
eventListener["@"+event.type] = stack.slice(); // ... and break the current forEach loop // (or, for the record, whatever Array.extras)
stack.length = 0; } }
var // local scoped object, reachable internally // usable as mixin so instances won't be polluted // with all possible event types // the type is prefixed in any case // so that name clashes should be // really rare however we use the object
eventListener = {
// we attach to a proper stack
addEvent: function(type, callback){ var // try to retrieve the stack ...
stack = ( // if already there ...
eventListener["@" + type] || // otherwise we create it once (eventListener["@" + type] = []) ), // as addEventListener, don't attach // the same event twice
i = stack.indexOf(callback)
; // so if it was not there ... if(-1 === i){ // FIFO order via stack
stack.push(callback); } },
// called via addEventListener // the "this" reference will be // the eventListener object, // or the current instance // if used as "class" mixin // or via Object.create / clone / merge
handleEvent: function(e){ // retrieve the stack var stack = eventListener["@" + e.type]; // and if present ... if(stack){ // set temporarily the local event var
event = e; // notify all registered callbacks // using current this reference // as forEach context
stack.forEach(notifyEvent, this); // let the GC handle the memory later
event = null; } },
// how we remove the event, if any ...
removeEvent: function(type, callback){ var // try to retrieve the stack ...
stack = eventListener["@" + type], // find the index ...
i
; // if the stack is present if(stack && ~(
i = stack.indexOf(callback) )){ // remove it
stack.splice(i, 1); } } },
// I could have called this variable tmp // but it's actually the current event // once assigned ... so ...
event
;
// test that furthermore this // callback won't be fired again this.removeEvent("click", callback);
// add delayed a callback // without any valid reason :-)
setTimeout(function(self){ // test addEvent again
self.addEvent("click", function(){ alert(2); }); }, 0, this);
// block the current notification returnfalse; });
// the event fired only the second click
lst.addEvent("click", function(){ alert(1); });
/** de-comment the mixin example to test
// that no @click is attached ;-)
for (var key in lst) {
alert(key);
}
// */
Advantages
both evt.stopPropagation() and evt.preventDefault() are not able to break the current notification of all attached listeners, if added to the same node, and while the FIFO order gives to the node "owner" or creator the ability to pollute the event object with some flag such evt.pleaseDontDoAnyOtherActionHere = true, not every library, script, or framework, may respect or understand this flag. With custom events we can adopt better strategies to actually avoid any other operation if this is what we meant, because we arrived before over the node and we may like to be that privileged
being custom, we can also decide which argument should be passed for each callback, simplifying most common problems we may have when dealing with listeners
we can better decouple DOM and listeners, being able to remove whatever amount of callbacks simply calling once node.removeEventListener(evt.type, this, false); inside any kind of notification
being based on standard and modern browsers, we can use native power, in this case provided by forEach and indexOf operations, so that performances will be best possible
thanks to automatic context injection, we can still reuse callbacks for different listeners, through bind, or simply considering the current context once called (or in this case the third argument by reference, if the context is different)
Last but not least, if we would like to fire an event we can bypass DOM initialization using handleEvent directly, e.g.
Apparently both W3C behavior and provided examples are compatible with every modern browser with DOM Level 2 support, and I believe this is great.
The only one behind here is IE9 pre 3, but again @jdalton has acted at speed light, thanks!
The main concept works on using CSS3 for the radius, making a round circle view point. This contains an iframe which is the exact same content. Using CSS3 again I scale the content in the iframe so that it is x2 larger, allowing smaller content to be read. The magnifying glass is also controlled by simple keyboard combinations which allow for show / hide and zooming in and out. Future plans will include Chrome and Firefox add-ons meaning that an iframe will no longer be used and can then use canvas to get the page content, which can then be updated as the page content updates.
The environment is mapped using cube mapping. I store all the values of the cubemap as floats. I increase the definition range by multiplying all values bigger than 0.95 with 2. This makes sure that the bright parts of the image are also very bright in the reflections. You can think of this as faking hdr. I do not calculate any lighting, it is all coming from the environment map. To make the animation look a bit more fluid and hide the aliasing I apply some fake motion blur by blending the current frame with the previous one.
You can check out the code to see how it all works. What frame rates are you getting?
Jacob Waller created an addictive word came in Golingo. What sets it apart?
Not a single line of Objective-C written, courtesy of Titanium Mobile
Only one (!) image ingame - the rest is CSS3 magic
Fluid gameplay thanks to CSS Transitions and Animations
All logic using pure, beautiful JavaScript
Multitouch draggables using iPhone Touch API
Logic encapsulated using Low Pro - meaning split screen mode was easy pie
jQuery 1.4.2 for development speed (and sanity of developer)
CouchDB as highscore storage, with storage logic in JavaScript
Predictable randomness means replayable games, all courtesy of excellent seedrandom
Jacob dives into deeper into the full tool chain that he used in his post about open sourcing the code behind the game. That's right, he wants you to fork it and do something amazing with the game. Pretty awesome if you ask me.
He then goes on to talk about the initial construction of the game:
Most of the actual game was made in a few weeks time, but from scratch to published app it took almost six weeks of part time work. A lot of this time was spent banging my head into various walls. Again, Titanium is great, but it's a young framework with all the kinks that follows. There has been quite a few bugs, and the documentation hasn't always been up to date - but this is much better nowadays. What more - since most other developers are as clueless as yourself, It's been hard finding good resources and getting help. I did however get a trial for Appcelerator's Premium Subscription, with 48-hours guaranteed response, and I must say it's really good stuff. The developers themselves answers all your stupid questions and relieves most of the wall banging. If you can afford it, go for it!
By releasing the code for Golingo, we hope to relieve some of the headaches surrounding developing packaged HTML5 apps. I do not say that it's not full of faults, because it is, but at least it is a working example full of faults. Please don't hesitate to dig through the code to see what is going on. We believe we've solved some common problems that you too will run in to when using Titanium, for example transparently calling native functions from a webview (and vice versa) using callbacks and trickery. Here's a quick recap of that:
Connecting Titanium Contexts
Low Pro like a low pro: $('<div/>').attachAndReturn(Letter, this, letter, specialLetter);
The JavaScript slim shady himself.... Dustin Diaz (formerly of YUI - Google, now of Twitter) has taken some time out for his busy coding at Twitter, and photography awesomeness to get back to some JS blogging.
One technique you don’t often see is queueing up a chain of methods, asynchronously, by which functions can be linked together independent of a callback.
Let's start with the end result. What if you could do this?
Raphael is pure goodness. Dmitry gave a fantastic talk at JSConf, and his library never fails to impress.
There have been a few interesting posts recently. Trotter Cashion kicks things into gear as he declares his love and then goes on to show how you can unit test Raphael with some mocks.
This is a guest post from the folks at Nextpoint. We've previously mentioned their pioneering use of Ajax in the legal industry and open sourcing of Growl4Rails. Here they bring us some details on their scrollable-document interface -- make sure to make it to the end of the post for a link to the sample code.
If your application involves reading large documents, books, or articles there's a good chance you've had to put some serious effort into building a nice reader. At Nextpoint, we build web-based litigation applications for discovery and evidence management and our users often find themselves reading very long documents. We decided to build a nice scrollable reader because we believe it's the most natural way to navigate through large documents in a browser and because we needed to have other meta-data and functionality remain in static locations surrounding the document. You may have seen something similar at Google Books or Bing image search. With potentially thousands of pages and images in each document there are some very interesting technical challenges that arise so we'll walk through how we tackled them.
Challenges
Documents can be thousands of pages long and consist of imaged pages making document sizes quite large. They vary in resolution and range in quality from poorly scanned images to rich graphical diagrams. Because many of the documents contain poor quality images we also implemented a zoom feature which leverages our Theater document presentation tool, previously covered here, to expand images on the fly. And finally, as always, it needs to be fast when scanning or following search hits throughout the documents.
Our approach
Our basic reader structure is a container DIV styled with "overflow:auto". The initial markup is very lightweight with JavaScript doing all of the heavy lifting. The basic metadata for each page (page number, image size, and URL) is loaded in sets of 20 on the initial request and then using ajax while navigating the document. The IMAGE elements are created on the fly as pages get near the visible area. Those are the basics. Here's a brief demo and then we'll hash out some of the details.
Preparing the scroll area
There are two main methods for incrementally loading -- expand the scrollable container as you load more data (such as Bing image search), or pre-set the height of the container for the entire content (such as Google Books uses). We chose to pre-set the height by drawing very simple placeholders for each page initially. These consist of a "Page 123" label and a div with the actual or estimated page height (depending if we've loaded that page's metadata yet), which will eventually act as a container for the page.
This allows the user to scroll through the document immediately, without causing confusion as the content jumps around due to the scrollable content getting "taller." We keep the placeholders as simple as possible so the initial setup and rendering of the page is quick. After drawing the placeholders, we cache the offsetTop of each placeholder in an array so we can quickly determine the "current visible page" by comparing the scrollTop of the container DIV to this array. Finally, we set up event handlers to watch the scroll position. We trap the onScroll event and use it to update the current page number in our navigation bar, and then separately a setInterval callback that checks if the user has "landed" on a particular page for long enough (~200ms) that we should load its images. (This allows for quick scrolling through the document without unnecessarily loading in-between images.)
Loading content as you go
When the user lands on a particular page, we first try to load all the currently-visible pages, then we pre-load a few of the surrounding pages, so those will be ready when they scroll down further. Loading the pages consists of checking whether we've already loaded the page metadata for it -- and if not, making an ajax call to load that plus 20 or so nearby pages' metadata. Once we have metadata for the page, we update its dimensions and create DOM content for the labels, a checkered "loading..." type background, and the page image itself. We use a bit of a trick, to help make sure the images for pages in the visible area load first, by setting all those up, and preloading the surrounding pages in a separate method on a 1ms setTimeout callback.
This lets the browser start loading the visible pages before adding the surrounding ones to the DOM. Also, if we update the dimensions of the page (because an initial estimate of height was a bit off), we re-cache the list of placeholder offsetTop values. Beyond that, the main UI piece is navigation. Next- and previous-page buttons are easy, along with a box where the user can type a page to jump to. Since the offsetTop of each page is already in an array, you can just scroll there and let the onScroll handler update as usual.
Gotchas
As usual, the Web has some quirks that corrupt this simple concept. One is that the browser doesn't render the DOM changes until Javascript events finish. So we end up using that setTimeout trick more often -- in particular, right before updating the cache of offsetTop values. It's always like this: setTimeout(updatePlaceholderCache, 1). Another is that, because we load the images from Amazon S3, there's a possibility of the URLs expiring if the user is reading a particularly long document, so we have to add an expire time to the page metadata, and check that before loading the page image. If it's expired, we have to re-request the page metadata before displaying the image. Also, we want a page to be considered "current" when it's scrolled most of the way into the visible area (when just the tail of the previous page is visible at the top), so whenever we check what the current page is, we add a slop amount to the container DIV's scrollTop value. Finally, since the container DIV may sometimes be larger than a single page of a document, it could be impossible to scroll down far enough that the last page is considered "current." So we add a spacer at the bottom of the container, just tall enough to let the last page scroll into the current position. (While not strictly necessary, extra features such as a "link to this page" option wouldn't work for the last page if it never was seen as the current page.)
A Line of Code is worth a Thousand Words?
For those interested in more of the details, we've put together a more generic version of the code, available here. No setup necessary and you'll be able to explore the basic concept without the Nextpoint dependencies.
for(var i = 0; i <lamps; i++){ var from = i * w * block_width;
result.push( calcMidColor(img_data.data, i * pxl, Math.min((i + 1) * pxl, total_pixels - 1))); }
return result; }
Then, two canvas objects are placed, one on each side, of the video itself.
As part of an upcoming article on geo location I am putting together a few Geo Toys for myself and here is the first one. Addmap.js is a JavaScript that analyses an elements text content, finds geographical locations and links them to Google Maps. It also adds a map preview and a list of the found locations to the element.
See addmap.js in action below - all the content in the green box is generated from the paragraph of text above it. You can try it out for yourself by clicking the screenshot.
Using addmap.js is easy - sign up for a Google Maps Key and provide it as a configuration parameter. Then call the analyse function with the ID of the element to analyse as the parameter:
When I talked about some snow related CSS3 experiment, I could not imagine @Natbat was already preparing something like snowflakes, an almost fully CSS3 featured snow FX created for clearleft, specially suited for Chrome and Safari.
And what about @zacharyjohnson? He put snow all over the network via its Winternetizer, the first snow proxy I have ever seen.
Am I missing anybody? ... sure, me!
Above FX is dedicated to all Ajaxian readers and created via some CSS3 rule handled via JavaScript for a partial cross browser implementation. WebKit based browsers, included Android and iPhone, plus Firefox 3.6, should render properly, while the most interesting thing, snow a part, is that for the first time rather than browser sniffing, I have implemented a sort of "screen resolution to power computation" sniff:
Probably not perfect, the aim is to avoid same number of flakes in mobile devices, netbooks, or desktop PCs.
I guess one day we'll have exposed CPU model and RAM amount as read only userAgent properties, so that all new effects could avoid stress for web surfers.
Something like System namespace in ActionScript, with capabilities for audio and video and extra info about the current navigator ... maybe just an AS to JS bridge 'till that day? We'll see, today the important thing is simply one: Have Fun!
There are a lot of CSS transitions experiments going on right now. Yesterday I discovered another HTML and CSS experiment which went "far far away", compared with my simple CSS gallery.
Guillermo Esteves presented a piece of history translated for tomorrows browsers: the Star Wars Episode IV opening crawl in HTML and CSS:
Unfortunately, the live experiment is not suitable for all browsers and the ideal target seems to be only OSX 10.6 and its latest Safari browser but it partially worked via Google Chrome as well.
Something To Learn About
We are moving complex computations into our favorite "decoration layer": CSS
We also want as much control as possible, and the above concept is brilliant to understand how to tame CSS transitions.
This example includes new and different techniques. Here is what I found interesting:
@font-face
OK, this is not new at all, but in this case I could not find a single valid reason to avoid the original font: a must! The only point here is that the author could have saved a bit of bandwidth via pre-deflated or gzipped fonts rather than serving them without any apparent optimization.
The stage is the block element where the magic happens. The perspective property is able to give us a "deep space" 3D feeling making closer objects look larger than further ones. Via origin modification we can decide where things should disappear, in this case a bit higher point than a central 400px, to create a similar atmosphere respecting the movie choice (and I guess to make content readable as well).
Far far away is the initial text. As we can see with other browsers as well this appears and disappear in 6 seconds.
This fade-in-out happens just once, so at the end of the effect, unless we won't modify the node class, the element won't be displayed anymore. This is what the animation-iteration-count property does while next snippet is the fade-in-out customization:
Via keyframes <transition_name> we can blend FX linearity deciding the amount of opacity, or other properties, at certain moments.
A generic normal linear fade-in-out would be visible 100% only in the middle of the transition while in this case it is forced to be visible for 68% of the time, making fade in and out still homogeneous but controlling the full opacity for longer.
We could have used an ease-in-out effect over opacity property to obtain a similar result but I find definitively more interesting this kind of approach.
Warp Speed: Action!
Thanks to Z axis transitions the initial STAR WARS image can appear and disappear using again a customized FX:
/* few lines after, img is the only logo image */
img {
opacity:0; position:absolute; top:100px; width:1000px;
-webkit-transform-origin:centercenter; /* above custom logo animation */
-webkit-animation:logo 25s linear;
-webkit-animation-iteration-count: 1; /* suspance before the logo ... */
-webkit-animation-delay: 12s; /* logo appears slower and fly away faster */
-webkit-animation-timing-function: ease-in; }
Above mix of webkit properties suggests me that new JavaScript libraries will use run-time actions to CSS transformations soon, rather than hard and manual JS computations over computed styles or similar expensive operations.
We can control delays, we can stop FXs removing classes or simply overwriting existent directives and, moreover, we can split the CSS itself into logical parts as the same @gesteves did, putting custom animations all together: good hint!
The Crawl
Last piece to check out is the text plus its title.
#crawl { color:rgb(252,223,43); font-family:FGD, sans-serif; text-align:center; font-size:36px;
opacity:0; /* long animation */
-webkit-animation:crawl 120s linear; /* again just once */
-webkit-animation-iteration-count: 1; /* starting while the logo is still there */
-webkit-animation-delay:16s; /* preserving 3D aspect for the entire animation */
-webkit-transform-style:preserve-3d; }
#crawl p.title { font-family:FGDC, sans-serif; /* it's a title */ text-transform:uppercase; /* it's massive */ font-size:96px; /* but scaled to fit inside margins */
-webkit-transform:scaleX(0.6); }
#crawl p { /* preserve spaces */ white-space:pre; }
The Mythical Song
Well, this demo could not miss a proper audio element:
If we have a good broadband connection and we are sure in 12 seconds we have buffered enough audio, the synchronization between JavaScript and the transition delay is almost perfect and the demo experience unique.
It is great to see this work, and I look forward to seeing what he comes up with next. It feels that with HTML5, CSS3, and the Web.Next we will be able to be much more creative. Finally, it's December, I can already imagine a CSS3 based snow effect for our pages ... no?
Tutorialzine is a nice blog but I think sometimes it should should re-dimension chosen titles.
I have discovered only yesterday and thanks to my good old favorite Web related italian blog, a nice (or if you prefer another) jQuery lightbox style experiment.
The post is complete with examples and explanation over PHP, CSS, jQuery, and finally jQuery UI.
So what is the problem? The title: An Awesome CSS3 Lightbox Gallery With jQuery
At the end of the day, the total size of the demo is massive, compared with what it offers, plus the only piece of CSS3 in the stylesheet is a box-shadow and a rotation via -webkit-transform.
Is That It?
If we can define awesome a basic usage of CSS3 requiring both jQuery and jQuery UI to create a Gallery, included a server side language, how can we define my latest experiment realized in half an hour and without using JavaScript, PHP, or whatever programming language at all?
The answer is simple: CSS3 and we can read how I did it via the not-minified and hopefully well commented css file.
OK, agreed my page is dynamically fake and a proof of concept, but honestly, which title would consider appropriate for above example?
Thanks in any case to Tutorialzine for the interesting step by step explanation and to let me try above experiment which works with latest WebKit, Chrome, Safari, and somehow with Firefox, I've not tried the nightly, and Opera as well but in latter cases without transitions.
P.S. for those with poor computation performances like me, here there is a fluid concept variation ;-)