Activate your free membership today | Log-in

Friday, July 3rd, 2009

It’s Friday. Play some drums…. HTML5 style

Category: Examples, Sound

Brian Arnold created a fun sample drum machine simulator using HTML5 <audio>.

JAVASCRIPT:
  1.  
  2. function playBeat() {
  3.         if (isPlaying) {
  4.                 var nextBeat = 60000 / curTempo / 4;
  5.                 // Turn off all lights on the tracker's row
  6.                 $("#tracker li.pip").removeClass("active");
  7.                 // Stop all audio
  8.                 stopAllAudio();
  9.                 // Light up the tracker on the current pip
  10.                 $("#tracker li.pip.col_" + curBeat).addClass("active");
  11.                 // Find each active beat, play it
  12.                 $(".soundrow[id^=control] li.pip.active.col_" + curBeat).each(function(i){
  13.                         document.getElementById($(this).data('sound_id')).play();
  14.                 });
  15.                 // Move the pip forward
  16.                 curBeat = (curBeat + 1) % 16;
  17.                 // Schedule the next one
  18.                 setTimeout(playBeat, nextBeat);
  19.         }
  20. }
  21.  

That's not all Brian is working on:

I'm also working on something like the ToneMatrix or Tenori-on (Flash and actual devices, respectively) in pure HTML/JS. It works too, but the sounds aren't exactly designed to be great together (it's currently working on a C scale) and so if you're careful, you can get some decent sound but otherwise, it'll hurt your ears.

Posted by Dion Almaer at 8:11 am
3 Comments

++++-
4.1 rating from 9 votes

Friday, June 26th, 2009

Fun with text-shadow

Category: CSS, Examples

Zach Johnson is at it again, this time giving us a fun Friday treat with CSS text shadow, all via:

JAVASCRIPT:
  1.  
  2. document.getElementById('text-shadow-box').onmousemove = function(e) {
  3.     var xm = e.clientX - 300;
  4.     var ym = e.clientY - 175;
  5.     var d = Math.sqrt(xm*xm + ym*ym);
  6.     text.style.textShadow = -xm + 'px ' + -ym + 'px ' + (d / 5 + 10) + 'px black';
  7.    
  8.     xm = e.clientX - 600;
  9.     ym = e.clientY - 450;
  10.     spot.style.backgroundPosition = xm + 'px ' + ym + 'px';
  11. }
  12.  

Posted by Dion Almaer at 11:26 am
13 Comments

++++-
4.1 rating from 30 votes

Thursday, June 11th, 2009

Animating SVG with Canvas and Burst

Category: Examples

Christopher Blizzard and his team are doing great write-ups on hacks.mozilla.org as they celebrate 35 days of Open Web goodness.

They just posted on the work of Alistair MacDonald who used his Burst engine to demonstrate taking SVG and having Burst load it and convert it all to JavaScript objects that are rendered inside of a canvas.

To get a feel for the code, view source:

JAVASCRIPT:
  1.  
  2.     Burst.defaults.debug=false;
  3.    
  4.     Burst.defaults.ease="easeOutQuad";
  5.     Burst.timeline("chassis", 0, 100, 1, true)
  6.       .shape("car", "car.svg", "svg", 0, 0, 1, 0)
  7.         .cut("wheel1;wheel2")       
  8.         .group("chassis")
  9.           .track("top").key(0,0).key(50,-20).key(70,0)       
  10.     ;
  11.    
  12.     Burst.defaults.ease="linear";
  13.     Burst.timeline("wheels", 0, 100, 1, true)
  14.       .shape("car", "car.svg", "svg", 0, 0, 1, 0)
  15.         .cut("chassis")         
  16.         .group("wheel1").track("centerX").key(0,230).track("centerY").key(0,350)
  17.           .track("rot").key(0,0).key(100,-360)
  18.         .group("wheel2").track("centerX").key(0,430).track("centerY").key(0,350)
  19.           .track("rot").key(0,0).key(100,-360)
  20.     ;
  21.    
  22.     Burst.timeline("carObject", 0, 300, 3, false)     
  23.       .track("scl").key(0,.5)
  24.       .track("left").key(0,400).key(300,-195)
  25.       .inherit("wheels")
  26.       .inherit("chassis")
  27.     ;
  28.    
  29.  
  30.     Burst.timeline("boom", 0, 10, 1, true)
  31.       .shape("boom", "boom.svg", "svg", 0, 0, 1, 0)
  32.     ;
  33.    
  34.     Burst.start("carObject", function(){
  35.         Burst.timeline("chassis").paused=true;
  36.         Burst.timeline("wheels").paused=true;
  37.         Burst.start("boom");
  38.     });
  39.  

and then watch the tutorial that shows how you can take Inkscape and quickly get animating.

View the Ogg or mp4 versions.

Also check out the other items:

Posted by Dion Almaer at 7:54 am
5 Comments

++++-
4.4 rating from 17 votes

Wednesday, June 10th, 2009

Styling buttons as links allowing you to POST away

Category: CSS, Examples

Have you ever wanted to just <a href="path" action="post">? Remember the hub-ub with the old Google Web Accelerator and how it started to crawl links to delete actions that were mistakenly using GET?

Natalie Downe has written up a piece on styling HTML buttons as links which means that you can somewhat get the same effect. It shows how you can use an inline span to get the hover effect, taking:

HTML:
  1.  
  2. <button type="submit" class="link"><span>Hello there I am a button</span></button>
  3.  

and styling it with:

CSS:
  1.  
  2. button {
  3.         overflow: visible;
  4.         width: auto;
  5. }
  6. button.link {
  7.         font-family: "Verdana" sans-serif;
  8.         font-size: 1em;
  9.         text-align: left;
  10.         color: blue;
  11.         background: none;
  12.         margin: 0;
  13.         padding: 0;
  14.         border: none;
  15.         cursor: pointer;
  16.        
  17.         -moz-user-select: text;
  18.  
  19.         /* override all your button styles here if there are any others */
  20. }
  21. button.link span {
  22.         text-decoration: underline;
  23. }
  24. button.link:hover span,
  25. button.link:focus span {
  26.         color: black;
  27. }
  28.  

You can see the simple example in action.

Posted by Dion Almaer at 3:15 am
16 Comments

+++--
3.9 rating from 13 votes

Thursday, June 4th, 2009

Image thumbnail crop tool with Processing.js and PHP GD

Category: Examples

Matt Schoen has written a nice image thumbnail tool that lets you zoom in on the part of the image that you want to crop for a thumbnail.

You can check out the Processing code that does the work:

JAVASCRIPT:
  1.  
  2. void setup(){
  3.     size(600, 600);     //for internal size variables
  4.     frameRate(30);      //set our draw rate
  5.     stroke(0);    //all lines will be black
  6.     color c = color(128,128,128,128);
  7.     fill(c);        //all filled rects will be 50% gray with 50% transparency
  8. }
  9.  
  10. int xoffset = 0;        //temp variable while we're dragging the mouse
  11. int yoffset = 0;
  12. int xpos = 0;      //where to draw the image
  13. int ypos = 0;
  14. int startx, starty;
  15. int prsd = 0;
  16. PImage a;  // Declare variable "a" of type PImage 
  17. a = loadImage("img.jpg"); // Load the images into the program
  18. double s = 1.0;
  19. double r = 1.0;
  20.  
  21. void draw(){    //this method is called by processingjs at the above frame rate
  22.         background(0)//draw a black background
  23.                 //for testing purposes, to figure out the scaling
  24.         scale(s);
  25.         image(a, xpos - xoffset, ypos - yoffset);       //draw scaled image
  26.         scale(1/s);     
  27.         //for whatever reason, using "scale(1.0);" didn't reset the scale here.  I have no idea why...
  28.        
  29.         //draw ghosting frame
  30.         noStroke();     //we don't want borders on these rects
  31.         rect(0,0,600,100);
  32.         rect(0,500,600,100);
  33.         rect(0,100,100,400);
  34.         rect(500,100,100,400);
  35.         line(0,560,600,560);
  36.        
  37.         //draw the "button" regions
  38.         line(300,560,300,600);
  39.         line(570, 0, 570, 30);
  40.         line(570, 30, 600, 30);
  41. }
  42. void mousePressed(){
  43.         if(mouseY> 560){
  44.                 if(mouseX> 300){
  45.                         s += .1;
  46.                 }
  47.                 else{
  48.                          s -= .1;
  49.                 }
  50.         }
  51.         if(mouseY <30 && mouseX> 570)
  52.                 link("http://www.pagefoundry.net/matt/jquery/renderthumb.php?x=" + xpos + "&y=" + ypos + "&s=" + s);
  53.         prsd = 1;
  54.         startx = mouseX;
  55.         starty = mouseY;
  56. }
  57. void mouseDragged(){
  58.         if(prsd> 0){
  59.         xoffset = startx - mouseX;
  60.         yoffset = starty - mouseY;
  61.         }
  62. }
  63. void mouseReleased(){
  64.         prsd = 0;
  65.         xpos -= xoffset;
  66.         ypos -= yoffset;
  67.         xoffset = 0;
  68.         yoffset = 0;   
  69. }
  70.  

Posted by Dion Almaer at 7:08 am
8 Comments

++---
2.5 rating from 33 votes

Friday, May 22nd, 2009

JSPlacemaker - Geo data extraction in pure JavaScript

Category: Examples, JavaScript

Content extraction is still a hot topic on the web. We have lots of great text content but not much clue as to what those texts are. To make it more obvious we do term extraction for tagging but also geo location extraction for giving the text some spacial reference.

A fairly new web service that does this for us is Yahoo's Placemaker. What it does is analyze a text (or the document defined by an HTML or feed URL) and give you back all the geographical locations that are mentioned in it. Pretty awesome, but the problem is that the API only allows for POST values and has either XML or RSS output. This means you can't do it in simple XHR because of the cross-domain problem and you can't use generated script nodes as there is no JSON output. You'd have to use a server-side proxy service. This is pretty easy with PHP and cURL as explained in this blog post but can be annoying, too.

That's why I wrote a small wrapper in JavaScript that allows JS access to the Placemaker service called JS-Placemaker. I am not hosting a proxy for you, all you need to do is get your own application ID for Placemaker and use the JavaScript which you can host yourself if you wanted to.

Analyzing a text using JS-Placemaker is as easy as this:

JAVASCRIPT:
  1. Placemaker.config.appID='YOUR-APP-ID';
  2. Placemaker.getPlaces('Hi I am Chris, I live in London. Originally I am from Germany',
  3.  function(o){
  4.    console.log(o);
  5.  },
  6.  'en-uk');

The console output is an object or an array of places the service returned from the text:

JS-Placemaker - geolocate texts in JavaScript by you.

The first parameter is the text you want to analyze (this could be a pointer to the innerHTML of a DOM element, for example), the second is the callback function and the third the locale of the text - the demo page shows that Placemaker groks several languages.

Under the hood, JS-Placemaker uses YQL to work around the issue of proxying the request. YQL allows you to define your own data tables and even allows for doing JavaScript conversion of data on the server-side before sending it on. YQL has JSON output, so I was able to use that to access Placemaker in JavaScript.

The geo.placemaker Open Table was built by Balaji Narayanan and Tom Hughes-Croucher and can be used in YQL directly. Say you want to get the geo location data from the Slashdot Homepage in JavaScript, you can do this with the following statement in YQL.

CODE:
  1. select * from geo.placemaker where documentURL="http://slashdot.org" and documentType="text/html" and appid="...the app id..."

You can choose JSON as the output and you get the data a a nice object.. Define a callback method and you could use it directly in a script node.

Have a Play with the YQL console using the Open Table, but better get your own AppID, before this one exceeds the daily limits.

Posted by Chris Heilmann at 9:28 am
3 Comments

+++--
3.7 rating from 23 votes

Thursday, April 30th, 2009

YQL execute now allows you to convert scraped data with server side JavaScript

Category: Examples, JavaScript, Yahoo!

I am a big fan of YQL, a terribly easy and fuss-free way to access APIs and mix data retrieved from them in a simple, SQL style language. Say for example you want photos of Paris,France from Flickr that are licensed with Creative Commons attribution, you can do this with a single command:

CODE:
  1. select * from flickr.photos.info where photo_id in (select id from flickr.photos.search where woe_id in (select woeid from geo.places where text='paris,france') and license=4)

Try it out here and you see what I mean.

The next step of this interface was to open it out to the public. You can define an "Open Table" as a simple XML schema and bring your own API into this interface with that.

One thing that's been burning on my tongue to tell the world about has been finally released now: YQL execute. Instead of making the YQL language itself much more complex (and thus running in circles) we now allow you to embed JavaScript in the Open Table XML that will run on the YQL server and allow you to access other web services, authenticate and scrape HTML with JavaScript and E4X. As Simon Willison put it:

This is nuts (in a good way). Yahoo!’s intriguing universal SQL-style XML/JSONP web service interface now supports JavaScript as a kind of stored procedure language, meaning you can use JavaScript and E4X to screen-scrape web pages, then query the results with YQL.

Using this, you can augment the original functionality of YQL to whatever you need. For example, you can scrape HTML with YQL using XPATH, but there was no way to use CSS selectors. Using an open table that invokes James Padolsey's css2xpath JavaScript on the server side, this is now possible.

CODE:
  1. use 'http://yqlblog.net/samples/data.html.cssselect.xml' as data.html.cssselect; select * from data.html.cssselect where url="www.yahoo.com" and css="#news a"

Run this query in YQL

The data table is pretty easy:

XML:
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <table xmlns="http://query.yahooapis.com/v1/schema/table.xsd">
  3.   <meta>
  4.     <samplequery>select * from {table} where url="www.yahoo.com" and css="#news a"</samplequery>
  5.   </meta>
  6.   <bindings>
  7.   <select itemPath="" produces="XML">
  8.     <urls>
  9.       <url></url>
  10.  
  11.     </urls>
  12.     <inputs>
  13.       <key id="url" type="xs:string" paramType="variable" required="true" />
  14.       <key id="css" type="xs:string" paramType="variable" />
  15.     </inputs>
  16.       <execute><![CDATA[
  17.    //include css to xpath convert function
  18.    y.include("http://james.padolsey.com/scripts/javascript/css2xpath.js");
  19.    var query = null;
  20.    if (css) {
  21.       var xpath = CSS2XPATH(css);
  22.       y.log("xpath "+xpath);
  23.       query = y.query("select * from html where url=@url and xpath=\""+xpath+"\"",{url:url});
  24.    } else {
  25.       query = y.query("select * from html where url=@url",{url:url});
  26.    }
  27.    response.object = query.results;
  28.       ]]></execute>
  29.     </select>
  30.   </bindings>
  31. </table>

Check the official Yahoo Developer Network blog post on YQL execute for more examples, including authentication examples for flickr and netflix.

Posted by Chris Heilmann at 9:28 am
14 Comments

+++--
3.9 rating from 25 votes

Wednesday, April 22nd, 2009

Dynamic Content Injection with HTML5 Canvas and Video

Category: Canvas, Examples, Firefox, Video

Paul Rouget and Tristan Nitot are having a lot of obvious fun with Canvas and <video> these days. The latest goodness is a demo by Paul that shows real-time dynamic content injection.

Notice the Firefox logo in between the two phones with bright screens? That is injected into the world thanks to Canvas.

How did Paul do this? He told us:

Obviously, I use the <video/> tag.
But what you see is not the video element (display: none;), but a
<canvas/> tag.
All the patterns you see on the top right are regular <img/>,
<video/> and <canvas/> elements. The play/pause button is
a <svg/> element
(position: absolute;) on the top of the main <canvas/> element.

A canvas element provides a method named drawImage which let you
inject the content of a DOM element in the canvas (like a screenshot). It works
with three kinds of elements: <img/>, <canvas/> and
<video/>.

When you click on the <svg/> button, the Javascript code launches the
main video. Then, the main javascript loop is executed each 10
milliseconds.

Here are key things that occur during the main loop:

  • first, the content of the video is injected in the main canvas. That's why
    the canvas element looks like a video element;
  • second, the position of the 2 brighter areas of the canvas are computed
    (you have access to all pixels values);
  • third, the required transformation is computed (rotation, scale,
    translation);
  • fourth, the content of the selected pattern is injected in the main canvas
    following the transformation.

A little drawing:

DCI

Check out the demo in a bleeding edge Firefox browser, or watch the video:

Crazy cool stuff :)

Posted by Dion Almaer at 8:50 am
5 Comments

++++-
4.7 rating from 36 votes

Friday, April 3rd, 2009

Auto Scrolling Parallax Effect in CSS

Category: CSS, Examples

Paul Hayes is off doing more fun things, this time creating an auto scrolling parallax effect with CSS, specifically using multiple background images on a single element and the -webkit-transition property to provide the auto-scrolling.

All with a bit o' CSS:

CSS:
  1.  
  2. #background {
  3.         background: url('../images/foreground.png') 5% 5%,
  4.                 url('../images/midground.png') 20% 20%,
  5.                 url('../images/background.png') 90% 110%;
  6.         top: 218px;
  7.         left: 0;
  8.         right: 0;
  9.         bottom: 0;
  10.         position: fixed;
  11.         -webkit-transition: left 300s linear;
  12. }
  13.  
  14. #experiment:target #background {
  15.         left: -5000px;
  16. }
  17.  
  18. #experiment:hover #background {
  19.         left: -9999px;
  20. }
  21.  

Posted by Dion Almaer at 1:01 am
5 Comments

+----
1.4 rating from 252 votes

Thursday, March 26th, 2009

Creating a clock in CSS

Category: CSS, Examples

If you ever go to the BBC website you will see the working clock in the top right:

It thus seems appropriate that Paul Hayes of London has created an interesting experiment that shows how you can create an analogue clock using just CSS and JavaScript is only used to get the current time.

The real magic is in CSS transitions such as:

CSS:
  1.  
  2. /* -webkit-transition: property duration timing-function */
  3.  
  4. #clock img[src*='hour'] {
  5. -webkit-transform: rotate(90deg);
  6. }
  7.  
  8. #clock img[src*='second'] {
  9. -webkit-transition: -webkit-transform 60s linear;
  10. }
  11.  
  12. #clock:target img[src*='second'] {
  13. -webkit-transform: rotate(360deg);
  14. }
  15.  

Nice work Paul!

Posted by Dion Almaer at 7:11 am
16 Comments

+++--
3.8 rating from 28 votes

Thursday, March 19th, 2009

Vizeddit: Visualization of Reddit

Category: Examples, MooTools

Yvo Schaap has created a reasonably interesting real-time front-end to Reddit data over at erqqvg.com. It's actually a really cool visualization of how Reddit's stories change over time, with "yellow-fade" indications when new comments and votes and registered. Super-nice.

(The screenshot doesn't do it justice; go check it out.)

But that's only half the story. They've got a much-better-named service called "Vizeddit" that displays the same data with much richer graphics and animations:

Once again, you really need to go see it. The numbers along the bottom represent stories, and they grow and swap places over time, new ones are inserted, and so forth. You see the votes drop down in real-time, and new comments float in as well.

All brought to you with the Moo.

Posted by Ben Galbraith at 6:00 am
Comment here

++++-
4.2 rating from 13 votes

Wednesday, March 18th, 2009

Super fast client side searches - the Flickr way

Category: Examples, JSON, Performance, XmlHttpRequest, Yahoo!

Over at the Flickr development blog, Ross Harmes, one of those lesser sung JavaScript heroes explains in detail how Flickr creates really fast client side searches and one of the implementations of these findings is the newly released find people faster feature:

find people faster feature on flickr

The main findings of the team were that eval() is not only evil but also very slow whereas dynamic script nodes are fast but insecure. The solution was to do a custom evaluation of string data rather than using JSON:

Having set the performance bar pretty high with the last approach, we dove into custom data formats. The challenge would be to create a format that we could parse ourselves, using JavaScript’s String and RegExp methods, that would also match the speed of JSON executed natively. This would allow us to use Ajax again, but keep the data restricted to our domain.

Since we had already discovered that some methods of string manipulation didn’t perform well on large strings, we restricted ourselves to a method that we knew to be fast: split(). We used control characters to delimit each contact, and a different control character to delimit the fields within each contact. This allowed us to parse the string into contact objects with one split, then loop through that array and split again on each string.

JAVASCRIPT:
  1. that.contacts = o.responseText.split("\\c");
  2.  
  3. for (var n = 0, len = that.contacts.length, contactSplit; n &lt;len; n++) {
  4.  
  5.         contactSplit = that.contacts[n].split("\\a");
  6.  
  7.         that.contacts[n] = {};
  8.         that.contacts[n].n = contactSplit[0];
  9.         that.contacts[n].e = contactSplit[1];
  10.         that.contacts[n].u = contactSplit[2];
  11.         that.contacts[n].r = contactSplit[3];
  12.         that.contacts[n].s = contactSplit[4];
  13.         that.contacts[n].f = contactSplit[5];
  14.         that.contacts[n].a = contactSplit[6];
  15.         that.contacts[n].d = contactSplit[7];
  16.         that.contacts[n].y = contactSplit[8];
  17. }

Once this had been speeded up, all they needed to use was the YUI AutoComplete control and voilà - fast client side searches even with massive datasets.

Posted by Chris Heilmann at 2:38 pm
8 Comments

+++--
3.6 rating from 16 votes

Thursday, February 26th, 2009

Aurora: Class types and invariants in JavaScript

Category: Examples, JavaScript

Ryan Morr has created a really fun experiment that shows the versatility of JavaScript. He just announced Aurora, fun times with class types and invariants in JavaScript.

He introduces the project:

The goal of the project is to provide a means of defining instance variables bound to a specific data type as you would find in Java or a variety of other languages. All subsequent calls to the methods of the instance result in automatic variable validation after the method call is complete. If a type violation is found, a descriptive exception is thrown and that specific variables value will be reverted back to what it was before the method call took place.

In addition to that, there is added support for class invariants, inspired by Eiffel, each class can declare a set of invariants for each instance variable which outlines the rules of that variable. For example, a variable called month in the class calendar must be an integer between the values of 1 and 12, if that rule is broken an exception is thrown.

The main pieces are:

  • Data Types: E.g., items: [Object], collection: Array(Element). You can create your own via define: Aurora.define('Hash', Aurora.isHash);
  • Invariants: E.g. a calendar class set:
    JAVASCRIPT:
    1.  
    2. invariant: { 
    3.      day: function(){ 
    4.          return this.day>= 1 && this.day <= 31
    5.      }
    6.      month: function(){ 
    7.          return this.month>= 1 && this.month <= 12
    8.      }
    9.      year: function(){ 
    10.          return this.year>= 1 && this.year <= new Date().getFullYear()
    11.      } 
    12. }
    13.  
  • Classes: The declare method is used to define a new class and has a similar syntax to the dojo.declare method. The method can accept either two or three arguments depending on whether the new class will inherit from a superclass.
  • Inheritance: Classical inheritance is also supported and maintains the same behaviour and functionality that you would expect. However, in additon to the inherited methods and the ability to call superclass methods, both declared types and invariants of a superclass will also be inherited by all subclasses.
  • Exceptions: Whenever a violation occurs, whether it be a type or invariant violtation, an exception is thrown that indicates the type of violation, the class and method in which the violation occurred, the variable that failed the validation, and in the case of type violations the expected type.

Nice stuff Ryan!

Posted by Dion Almaer at 2:21 pm
Comment here

+++--
3.7 rating from 31 votes

Wednesday, February 25th, 2009

3D Canvas Demos in 2D Canvas

Category: Canvas, Examples

Paul Baukus pointed us to an English translation of some of the amazing stuff he saw in his recent trip to Japan. In this case, it's an emulation of 3D rendering in canvas' 2D context:

This stuff is CPU intensive, but is a pretty nice example of what's possible.

Posted by Ben Galbraith at 4:00 am
7 Comments

++++-
4.3 rating from 27 votes

Monday, February 23rd, 2009

HTML5 Canvas Cheat Sheet

Category: Canvas, Examples, Tip

Jacob is back, and this time he has a cheat sheet with him. It is nice to see the Canvas API fitting on one sheet here, and I really like the images showing how things look like and work.

Posted by Dion Almaer at 12:56 am
1 Comment

++++-
4.2 rating from 19 votes

Sunday, February 22nd, 2009

Cross-Browser Inline-Block

Category: Articles, Examples, Showcase

Ryan Doherty has one of those really nice articles that walk you through how to do something that should be easy in CSS but isn't, until you know how: Cross Browser Inline Block.

By the end of it all you will have this:

HTML:
  1.  
  2.     li {
  3.         width: 200px;
  4.         min-height: 250px;
  5.         border: 1px solid #000;
  6.         display: -moz-inline-stack;
  7.         display: inline-block;
  8.         vertical-align: top;
  9.         margin: 5px;
  10.         zoom: 1;
  11.         *display: inline;
  12.         _height: 250px;
  13.     }
  14. </style>
  15.  
  16.         <div>
  17.             <h4>This is awesome</h4>
  18.             <img src="http://farm4.static.flickr.com/3623/3279671785_d1f2e665b6_s.jpg"
  19.             alt="lobster" width="75" height="75"/>
  20.         </div>
  21. </li>
  22.  

which gets you the following reliably in many browsers:

Along the way you will learn about more IE star hacks, fun with hasLayout, use XUL stacks to help Firefox 2 along, and more. Very nice job Ryan.

Posted by Dion Almaer at 5:04 am
11 Comments

++++-
4.2 rating from 42 votes

Next Page »