Archive for the 'browsers' Category

3PO#fail

Saturday, June 16th, 2012

So I was flipping through recent slides from Steve Souders and came across a reference to a nice post from Pat Meenan explaining how he setup blackhole.webpagetest.org and how you can edit your hosts file to send third party scripts to the black hole simulating a firewall-blocked or down third party and the effect on your site. (whew, long sentence)

I thought to would be nice to make that easier and have people see (and demonstrate to bosses and clients) how damaging frontend SPOF (Single Point Of Failure) can be. A browser extension maybe. A Chrome extension, because I've never made one. The idea marinated undisturbed for a few days and last night all of a sudden I got to work.

May I present you...

Now available at the Chrome web store.

3PO?

3PO = 3rd Party Optimization

I find it amusing, hope you do too

#fail?

Well, yeah. What happens to your site when a 3rd party goes down? Does it still work?

Is it true that your site is only down when it's down? Or it's down when:

It's down
or
Facbeook is down
or
Google is down
or
Twitter is blocked in your office
or
code.jquery.com is down
...and so on and so on.

This extension helps you check what happens with a click of button.

What 3PO#fail does

Very simple: it's looking for scripts from a list of suspects (api.google.com, platform.twitter.com, etc) and redirects them to blackhole.webpagetest.com

The current list of 3rd parties:

var urls = [
  '*://ajax.googleapis.com/*',
  '*://apis.google.com/*',
  '*://*.google-analytics.com/*',
  '*://connect.facebook.net/*',
  '*://platform.twitter.com/*',
  '*://code.jquery.com/*',
  '*://platform.linkedin.com/*',
  '*://*.disqus.com/*'
];

How?

Install the extension. Load your page. Or mashable.com for example. Then this happens:

It's a button with # on it. Click it. It turns red.

The extension now listens to script requests made to one of the suspect domains.

Now shift-reload the page. If a 3rd party script is found, it's redirected to the black hole and then a counter appears.

Observe whether or not the page is usable when a third party is down. Enjoy and demo to your boss. Tell them: sites do go down, companies ban social networking sites, and btw what do you think will happen when you visit China and load our site?

If you're looking for a page to try, go to mashable or business insider or, ironically, test the extension's page in Chrome web store. Turns out they include Google+'s button synchronously.

Dupe

Here come the LOLz. I blasted this extension out to Steve Souders and back he came with: doh, Pat Meenan also did a Chrome extension to do just this.

Bwahaha. What? You snooze, you miss a whole new tool by Pat Meenan himself.

Here's Pat's extension: SPOF-O-Matic. Try it, use it. It looks more thought out than mine definitely. And there's more code. Maybe Pat spend more time than a night on it. Or maybe he didn't, he's an amazing hacker and half! I mean, uh, webpagetest, hello!

I'll definitely "borrow" his list of 3rd parties which has more entries than mine.

Oh well, you live, you learn (to write Chrome extensions)

Chrome extensions

Creating a Chrome extension was a first for me and was mostly frictionless. Well documented, plenty of samples (try to browse the samples in the repository, because downloading ZIP files is too many clicks). Debugging the extension in the same web inspector is a big plus! Overall I think it's easier to write a Chrome extension than a FF one. Although the last I checked, FF has improved a lot.

Now for the nitpicks.

The API is sometimes irritating. I mean things like

setTitle({title: "My title"});

or

setBadgeText({text: "My text"});

Doplicating title, title, title is annoying. Sometimes it's title, sometimes text, or path or name. Method name appears short but in fact you have to remember one more thing - a property name in a config object. Sounds more like setTitleWithTitle(title) which is just as ridiculous (and popular in Obj-C it seems). Anyway.

The web store asks you for 5 bucks to register and submit an extension. Credit card and all. I didn't like that.

My extension was held for a review which doesn't always happen. The help section says 2-3 business days, but it turned out to be only hours for me. Got a nice email saying the extension is approved and also an explanation why it was held for review. Nice touch.

Code

The code is here: https://github.com/stoyan/3PO-fail. There's not a lot of it. A manifest file and an script that listens to specific URLs and request types in a onBeforeRequest event.

Stripping away UI stuff here's all there is to it.

Callback function which redirects the request:

function failer(info) {
  console.log(info.url); // test
  return {
    redirectUrl: 'https://blackhole.webpagetest.org'
  };
}

There's no logic here because the API allows you to let the browser do request inspection and filtering for you. Here all you do is return an object with a redirectUrl property.

And how do you setup your callback to be invoked?

chrome.webRequest.onBeforeRequest.addListener(
  failer,
  {
    urls: urls,
    types: ['script']
  },
  ["blocking"]
);

You specify your callback to be invoked only for script requests and only those that match a URL in the url array (see above)

The end to the SPOF

All you have to do is load third party scripts synchronously. See here the BFF function for an example. Yet, so many sites are not doing it. There's a need for people to understand this problem. Let's call it demand for advocacy. And now there's supply of 2 brand new tools that make it in-you-face obvious what the damaging effects are.

Random

I went over some of the pages that Steve has listed in his calendar blog post: Business Insider and O'Reilly. O'Reilly is better now and it uses my BFF script (nice, 'scuse me there's something in my eye). Business Insider is almost there. The social stuff is async now, but code.jquery.com is still a SPOF. Funny enough they have a blocking script tag pointing to twitter, but it has a class "post-load". So a script kicks in before this tag and replaces it with async loading. I wonder: why the trouble and not just go async to begin with?

 

getUserMedia in Opera 12

Thursday, June 14th, 2012

Opera 12 wins - the first stable desktop browser to ship getUserMedia(). I believe they had shipped it already in a mobile version of the browser.

(I'll need to fix my example from last night because Chrome Canary uses webkitGetUserMedia and also uses a stream URL to assign to video element's src)

You can start playing with it right now in Opera, no need to install nightly versions and enable secret flags. Here's how:

1. Install Opera
2. Load any page
3. Press Command + Alt + I (on a Mac) to bring the Dragonfly console (like Firebug)
4. Type:

document.body.innerHTML = '<video autoplay>';

and then

navigator.getUserMedia(
  {video:true}, 
  function(stream){document.getElementsByTagName('video')[0].src = stream}
)

This is it, enjoy :)

Ever so slightly disappointed that {audio: true} gives a "not supported" error

 

When is a stylesheet really loaded?

Thursday, March 17th, 2011

Often we want to load a CSS file on-demand by inserting a link node. And we want to know when the file finished loading in order to call a callback function for example.

Long story short: turns out this is harder than it should be and really unnecessary hard in Firefox. I hereby beg on behalf of many frustrated developers: please, Firefox 4, please fire a load event when a stylesheet loads.

Update:
If you think this is "a change we can believe in", tell the browser vendors to fire a load event on link elements as the HTML5 standard mandates. Here's the list of bugs for each browser to comment on and point people to:

Here's what the standard says:

"Once the attempts to obtain the resource and its critical subresources are complete, the user agent must, if the loads were successful, queue a task to fire a simple event named load at the link element"

With that out of the way, let's see what we have here.

// my callback function
// which relies on CSS being loaded
function CSSDone() {
  alert('zOMG, CSS is done');
}
  
 
// load me some stylesheet
var url = "http://tools.w3clubs.com/pagr/1.sleep-1.css",
    head = document.getElementsByTagName('head')[0];
    link = document.createElement('link');
 
link.type = "text/css";
link.rel = "stylesheet"
link.href = url;
 
// MAGIC
// call CSSDone() when CSS arrives
 
head.appendChild(link);

Options for the magic part, sorted from nice-and-easy to ridiculous

  1. listen to link.onload
  2. listen to link.addEventListener('load')
  3. listen to link.onreadystatechange
  4. setTimeout and check for changes in document.styleSheets
  5. setTimeout and check for changes in the styling of a specific element you create but style with the new CSS

5th option is too crazy and assumes you have control over the content of the CSS, so forget it. Plus it checks for current styles in a timeout meaning it will flush the reflow queue and can be potentially slow. The slower the CSS to arrive, the more reflows. So, really, forget it.

So how about implementing the magic?

  // MAGIC
  // #1
  link.onload = function () {
    CSSDone('onload listener');
  }
  // #2
  if (link.addEventListener) {
    link.addEventListener('load', function() {
      CSSDone("DOM's load event");
    }, false);
  }
  // #3
  link.onreadystatechange = function() {
    var state = link.readyState;
    if (state === 'loaded' || state === 'complete') {
      link.onreadystatechange = null;
      CSSDone("onreadystatechange");
    }
  };
  
  // #4
  var cssnum = document.styleSheets.length;
  var ti = setInterval(function() {
    if (document.styleSheets.length > cssnum) {
      // needs more work when you load a bunch of CSS files quickly
      // e.g. loop from cssnum to the new length, looking
      // for the document.styleSheets[n].href === url
      // ...
      
      // FF changes the length prematurely :( )
      CSSDone('listening to styleSheets.length change');
      clearInterval(ti);
      
    }
  }, 10);
  
  // MAGIC ends

Test

Test page - riiiight here. I'm loading a CSS file delayed two seconds on the server. Attaching all those event listeners and timeouts above. Adding another timeout that just says "... and two seconds later ... " after (you guessed it!) two seconds. Now observing los resultados...

Results

  • IE fires readystatechange and load (tested years ago, too lazy to test now again). Now with IE9 maybe addEventListener will work too?
  • Firefox (like before) fires nothing. It updates the length of document.styleSheets immediately, not waiting for the file to actually arrive. So the outcome in my test log is:
    zOMG, CSS #1 is done: listening to styleSheets.length change
    ... and two seconds later ...
    
  • Opera fires load via onload and via addEventListener too. Like FF it also increments document.styleSheets.length immédiatement. The outcome:
    zOMG, CSS #1 is done: listening to styleSheets.length change
    ... and two seconds later ...
    zOMG, CSS #1 is done: onload listener
    zOMG, CSS #1 is done: DOM's load event
  • Chrome and Safari will not fire events but will update document.styleSheets only when the file arrives, yey!
    ... and two seconds later ...
    zOMG, CSS #1 is done: listening to styleSheets.length change

All in all, there's at least one way to tell when the stylesheet is loaded in each browser, except Firefox. Now that's embarrassing.

Is there really no hope for Firefox?

If you go really crazy than yes - implement magic #5 but it has serious drawbacks.

Otherwise the object trick should do - all browsers seem to fire load and/or readystatechange event consistently. Obviously it's a little more complicated. Although probably not as complicated as monitoring the document.styleSheets collection

I also tried MozAfterPaint - it might work but didn't for me, because my CSS didn't change anything on the page that required repaint. Obviously not a fit-all solution.

Another thing that failed was checking document.styleSheets[n].cssRules. Although document.styleSheets.length is updated immediately, I was thinking FF cannot update document.styleSheets[n].cssRules (document.styleSheets[n].sheet.cssRules) until the CSS actually arrives. However since FF 3.5 (or thereabouts) you don't have access to cssRules collection when the file is hosted on a different domain (CDN say). Security thing, you see. Even cssRules.length would've been enough, but nah.

Takeaways

  1. Any ideas about a clever (or not so clever) workaround that lets us figure out when CSS is loaded in FF? Please comment.
  2. Libraries can benefit from document.styleSheets.length trick to support Chrome and Safari. I know at least that YUI3 doesn't support callbacks on Y.Get.css() in Safari (nor FF)
  3. Firefox 4 must implement load on stylesheets :) No doubt about it. IMO all browsers should fire load on everything related to external resources.

UPDATE: FF solved!

Thanks to Ryan's comment below, Zach and Oleg, turns out there is something that works. And that is:

  1. you create a style element, not a link
  2. add @import "URL"
  3. poll for access to that style node's cssRules collection

It just happens so that Firefox will not populate this collection until the file arrives!

var style = document.createElement('style');
style.textContent = '@import "' + url + '"';
 
var fi = setInterval(function() {
  try {
    style.sheet.cssRules; // <--- MAGIC: only populated when file is loaded
    CSSDone('listening to @import-ed cssRules');
    clearInterval(fi);
  } catch (e){}
}, 10);  
 
head.appendChild(style);

Updated my test and seems to work just fine.

Whew!

I still maintain that this is unnecessarily complex and all browsers should simply fire load event. Seems to me this FF behavior might change at any time, as well as Safari's not populating document.styleSheet

One thing to note: this access to cssRules is not a failure of the same security check that doesn't allow cssRules access with outside domains. In this case we're accessing cssRules of the inline style and that's fine. We still cant access the styles of the @import-ed file from a different domain.

Side note: makes me wonder - this could be a technique to preload JS without executing too :)

 

Browser sniffing with conditional comments

Thursday, May 13th, 2010

Browser sniffing is bad. But sometimes unavoidable. But doing it on the server is bad, because UA string is unreliable. The solution is to use conditional comments and let IE do the work. Because you're targeting IE most of the times anyway.

In fact IE8 is a decent browser for the most practical purposes and often you're just targeting IE before 8.

Conditional comments in practice use the following pattern:

  1. Load the decent browsers CSS
  2. Conditionally load IE6,7 overrides

The drawback is that IE6,7 get two HTTP requests. That's not good. Another drawback is that having a separate IE-overrides stylesheet is an excuse to get lazy and instead of solving a problem in a creative way, you (and the team) will just keep adding to it.

We can avoid the extra HTTP request by creating our CSS bundles on the server side and having two browser-specific but complete stylesheet files:

  1. The decent browsers CSS
  2. The complete CSS for IE6,7 not only the overrides

Then the question is loading one of the two conditionally without server-side UA sniffing. The trick (courtesy of duris.ru) is to use conditional comments to comment out the decent CSS so it's not loaded at all:

<!--[if lte IE 7]>
  <link href="IE67.css" rel="stylesheet" type="text/css" />
<![endif]-->
<!--[if gt IE 7]><!-->
  <link href="decent-browsers.css" rel="stylesheet" type="text/css" />
<!--<![endif]-->

The highlighting suggests what the decent browsers see.

IE6,7 see something like this after the conditional comments are processed:

  <link href="IE67.css" rel="stylesheet" type="text/css" />
<!--
  <link href="decent-browsers.css" rel="stylesheet" type="text/css" />
-->
 

Free-falling waterfalls

Monday, December 14th, 2009

2010 update:
Lo, the Web Performance Advent Calendar hath moved

Dec 14 This post is part of the 2009 performance advent calendar experiment. Stay tuned for the articles to come.

In this serias of performance posts, so far we've looked at having fewer components in the waterfall (meaning less HTTP requests) and also making the components as small as possible. The next task is to make sure that the waterfall is as short as possible - meaning let it fall freely, without interruptions and have the browser download as many components as possible in parallel.

Some ways to make the waterfall fall free include having:

  • fewer DNS lookups
  • parallel downloads using several domains (this point contradicts the previous, ain't performance fun)
  • fewer redirects (ideally no redirects)
  • non-blocking scripts and styles
  • smaller request/reponse headers, which includes using fewer cookies

Reducing DNS lookups

When you request a component, the browser needs to resolve the hosthame of the component to an IP address. This is known as a DNS lookup and you can see those lookups in the waterfall charts. The DNS time may look negligible (plus DNS lookups get cached by browsers and operating systems), but they sometimes take ridiculous amount of time. It depends on many factors, often beyond your control, so the best thing to do is change what you do control and that is - require fewer DNS lookups. Carlos Bueno has an excellent writeup on DNS lookups here.

You should limit the number of DNS lookups the browser needs to perform, ideally to no more than 2 to 4, according to Yahoo's studies.

Parallel downloads

Browsers have limits on how many components they download from the same domain at the same time. In older browsers, including IE6 and IE7 this limit is 2. This can definitely slow down your waterfall significantly, when you have a greater number of components to download.

Newer browsers have increased that limit to 4 (Safari, Opera 10) or 6 (FF3, IE8), so this should be less of an issue. But at the end it depends on your page - how many components and how many people on IE6,7.

Below is an image of how IE7 loads a page with 8 images, where each image is artificially delayed to take 2 seconds. Downloading two components at a time, IE spends at least 8 seconds on the images (2-4-6-8) or a total time of over 9 seconds.

Loading the same page in IE8 is shown below. IE8 loads 6 components at a time, so the 8 images are loaded in two batches (1st - 6 images, 2nd - 2 images), for a total time of 4 seconds spend on images. Overall, the whole page loads in 5 seconds.

Now, to work around this limitation in older browsers a common technique is to create dummy subdomains, like img1.example.org, img2.example.org and so on, so that more components can be downloaded in parallel (the limitation is per domain). If you're going to do this, remember to balance this optimization with the fewer DNS lookups recommendation, don't spread on too many domains. Look carefully at your waterfalls to find the balanced point. Again, the general recommendation is that a page should require up to 2-4 domains tops.

Quick sidetrack: two URLs for your toolbox.

  1. For the up-to-the-moment insight into different browser limits and capabilities, bookmark Google's BrowserScope project. Check the "Network" tab for example to see the parallel downloads limitations across browsers
  2. Cuzillion (by Steve Souders) lets you quickly create test pages. The waterfalls above are actually coming from a page created with Cuzillion and tested in AOL's WebPageTest

No redirects

Redirects are bad for your waterfall. They do nothing for the user but just slow down the experience. Think about what happens: the browser makes a request, waits for the response, the response says "no, no, go get your component from way over there", so the poor old browser starts again - makes a new request, waits for the response.

So, avoid redirects, be they server-side redirects or client-side (JavaScript or meta-tag redirects).

As an illustration how bad redirects can be - consider the waterfall below. It's from a real page, not a made up case. So it all looks like the page could finish loading after about 1.1 seconds, but then a redirect occurs at 0.9s, takes half a second and then points to a 1x1 blank GIF. Obviously it's some sort of stats tracking image. But the bad parts are that: a/ it's an IMG tag, therefore delays onload and b/ there's a redirect. At the end, the page loads in 1.7 seconds instead of 1.1 seconds. The user experience suffers for no reason. The way to fix this is simply remove the image from the IMG tag and load it with new Image().src = "1x1.gif" this way taking it out of the onload flow. Then remove that redirect. For such stats tracking cases a 204 No Content response is the appropriate way to go.

Blocking script and styles

Scripts block downloads, hence slow down your free-falling waterfall. This is an important topic which deserves an article of its own, so stay tuned.

And what about stylesheets, do they block other downloads in the waterfall? Turns out stylesheets are mostly fine, but they could also block in these cases:

  • in Firefox before version 3 (probably no need to worry about it)
  • in all browsers, if followed by an inline script

The second one is interesting as much as it's surprising. It's probably not a good idea to have inline script tags scattered all around the HTML to begin with. And since this can cause the stylesheets to block the downloads of the other components, it should be avoided at all costs.

Be sure to check Steve Souders' blog post for more information. Credit to Steve - I believe he was the first to take note and report this issue.

So check your waterfall if you see a stylesheet that blocks, look around it in the markup for any inline script tags that can be moved further down.

Cookies and other HTTP headers

We talked about making the responses smaller. But we can also optimize the requests by making the HTTP headers smaller. You can take a look at your request and response headers and see if you're not sending too much.

Looking at my blog I see this Server header:

Server: Apache/2.2.11 (Unix) mod_ssl/2.2.11 OpenSSL/0.9.7a Phusion_Passenger/2.2.4 mod_auth_passthrough/2.1 mod_bwlimited/1.4 FrontPage/5.0.2.2635

Seems to me like too much information.

There's also an ETag:
Etag: "9f38013-2af0-453b354a20bc0"

While ETags may be help with caching, when you have far-future Expires header, they are not necessary. And if you have a multi-machine setup, ETags can actually be bad (YSlow will warn you for this issue)

While you have some control over the response headers, you have very little control over the request headers. It's your users' browsers that control them. But you do have control over the Cookie header and this is where the bigger savings will actually come from because Cookie is often the biggest part of the HTTP headers

Reduce cookies

You should aim at sending the least possible amount of cookies. Be careful when you write cookies. Make them smaller and write them to the appropriate sub-domain name. If you have a blog at blog.example.org and a main site at www.example.org, then don't write the blog.example.org cookies at *.example.org level.

Hotmail have talked about how they compress their cookies before writing them. That's also an idea if you have big Cookie headers.

Cookie-less domains for components

Better yet, for static components that don't have any use for these cookies, just don't send them. Setup static.example.org and don't write cookies for this domain. Then put your static components there.

A curious piece of stats here - Philip Dixon reported (slides) that after Shopzilla.com moved their static components to a cookie-free domain they made more money.

Images to non-cookie domain resulted in
0.5% top line revenue increase!

"top line" means revenue (maybe you know that but I had to check :) ). This is a fascinating idea - that you can make more money by improving something as simple as cookie-less components. So, every little bit helps. Keep making your site faster and ... you never know.

www or no-www

This point also adds to the good old www vs. no-www flamewar. If you opt for no www, then in IE you cannot write cookies to example.org, but you'll write to *.example.org. This means your static.example.org will see all the cookies too. This post has some more info on the topic.

If you've already polluted your top-level domain with long-term cookies, the remedy would be to just buy a new "clean" domain, like examplestatic.org, never ever write cookies to it and use it for your static components.

Thanks!

And that's it for today's post, thank you for reading and may HTTP be with you ;)

 

Browser’s implied globals

Tuesday, October 20th, 2009

Like it's not bad enough that JavaScript has implied globals (forget var and you create a global), but the browsers have decided it's a good idea to add more pollution to the global namespace.

This has been a source of frustration before with IE, it's really hard to understand the logic behind it, but it's also happening in other browsers.

Consider this:

<meta name="description" content="test me" />

A normal META tag, right? But in IE this will create in a global variable called "description" pointing to that DOM node. Yep.

alert(description.content); // "test me"

That's pretty annoying. Even more annoying is that getElementById('description') will also point to the DOM node, although it doesn't even have an ID.

A test is born

Anyway, I wanted to test the effect of other name and id attributes in different tags and different browsers. With the exception of Firefox which doesn't create any globals, all other did to some degree. Rather disappointing. I tested IE6, 8 (plus compat view), FF 3.5, Safari 4 and Opera 10.

Here's the test page

And below are the results. The yellow x means that testing for the presence of this global returned "undefined", the white o means that the global variable points to an object. So for example continuing with the meta example above, typeof window.description will return undefined in FF (yellow x) and object in IE (white o).

global description IE FF Saf O
description <meta name="description"... o x x o
robots <meta name="robots"... o x x o
paragraph-id <p id="paragraph-id"... o x o o
paragraph-name <p name="paragraph-name"... x x x o
form-name <form name="form-name"... o x o o
form-id <form id="form-id"... o x o o
input-name <input name="input-name"... x x x x
input-id <input id="input-id"... x x o x
link-name <a name="link-name"... o x x o
link-id <a id="link-id"... o x o o
div-name <div name="div-name"... x x x o
div-id <div id="div-id"... o x o o

So...?

So this is a useless feature if you ask me. Not reliable, not cross-browser, maybe considered convenient back when rollover buttons and animated gifs were all the rage (and animated window.status, remember?), but today can only cause troubles where you least expect it. Should be removed in future browser versions.

For the time being we just have to remember to always declare and initialize our local variables because it looks like someone else might also decide to do so for us. Which can lead to errors if we assume too much.

 

Replace the Home button with a script

Tuesday, March 3rd, 2009

Robert Ames commented on my previous post suggesting replacing the Home button with my little site search bookmarklet. I didn't even know this was possible, but I found it pretty cool, so I just had to try.

x-browser

I tried this in Firefox/Mac and IE/Windows and it works just fine.

ok, what is it?

Well, the idea is simple: when setting the homepage in your browser, instead of providing an http://... URL, you can use the javascript:... pseudo-protocol. So for example you can type:

javascript:alert(document.title)

And then every time you hit the Home button in the browser, an alert with the page title will pop up. How cool is that!

Naturally, you can replace the simple javascript with any sophisticated bookmarklet of your choosing (hint, hint)

how-to

In Firefox on the Mac you go Firefox / Preferences. On Windows: Tools / Options. Then in the Main tab, you type/paste the bookmarklet code.

Setting the Home to a javascript bookmarklet in Firefox

For IE/Windows go Tools / Internet Options and select the General tab

Internet Explorer - setting the Home button to a javascript bookmarklet

 

Javascript console in IE

Saturday, October 18th, 2008

I'm a sucker for consoles. The ability to quickly type some code and see it executed right then and there... priceless. That's why I'm a huge fan of Firebug's JavaScript console. But what about IE?

option 1 - Firebug lite

Firebug lite is a lighter version of the proper Firebug which runs in browsers other than Firefox. You "install" the bookmarklet and voila - JavaScript console and other goodies are available on any page.

You visit any page and click the "Firebug lite" bookmarklet...
firebug lite bookmarklet

... then you hack away in the shiny console!
firebug lite bookmaklet in action

option 2 - built-in MS script editor/debugger

If you do any javascript in IE, it's a good idea to have this debugger guy enabled. There's actually at least three different debuggers, but one of them is already installed without you lifting a finger. If you enable it, you can debug any time there's a JavaScript error on the page. It also features a console! You can't get to the console unless there's a JS error, so you may need to cause the error yourself. Here's the step-by-step scenario.

Go to menu Tools / Internet Options... / Advanced tab. Under the "Browsing" category uncheck the box that says "Disable Script Debugging"
set the IE options

Go to any page and cause an error, by typing in the address bar some non-existing property or some non-existing object for example. Like javascript:alert(a.a.a)
Go to anypage and cause an error

Non-surprisingly, you get an error, but now you have the option of debugging the error:
A error and a debug option

You're given a list of debuggers, in case you've installed more debuggers from MS. Select your debugger or just hit Yes:
pick a debugger

Just say OK here...
step into procedure

Click "Break"...
Break

Finally - a console! We're in! The console is the so-called "Immediate" window, which is not displayed by default. To see it go to menu Debug / Windows / Immediate. Then just start fiddling with the page. Type anything and hit enter to see it evaluated. You also list the properties of an object by typing its name, like document.images[0] or just document.
Fiddle with document.images[0]

Once you've had your fun, stop debugging:

Enjoy the results of your hard work, a.k.a. replacing some logo with a shot of your favorite book ;)
your fav book

 

a DOCTYPE is all it takes

Friday, July 25th, 2008

At a lunch conversation about quirks vs standards mode, the question was raised: but what if you don't have those kinda important tags such as <html> or <body>? is this going to be quirks mode?

Well, the following test case shows that a DOCTYPE is all it takes in order to have standards mode rendering. Was this a Cinderella song: "We all need a little DOCTYPE, just a little helper, ooh and it will be alright" :)

» Run test example

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 
 
hello
 
<script type="text/javascript">
alert("Quirks? " + document.compatMode);
</script>
 

Browser Wars 2.0?

Monday, December 17th, 2007

browserwars.png

 

Smart browsers don’t download unneeded images

Saturday, June 23rd, 2007

We ofter complain about browsers, browser inconsistencies, quirks, hickups, the list goes on. Let's say something nice to them, and hope for good karma :)

It happens as your web app grows in size and team members that some parts of the stylesheets become obsolete, no one remembers why they were there in the first place, but no one has cojones to remove them, because of fear that removing them might break something you forgot to take into consideration. Same case when you have the same stylesheet for 50 different pages and on some pages only some of the styles are actually used. I was wondering, do you pay performance penalty when you have style rules that refer to images, but the rules are never needed to render the page? Will the browser download all those unneeded images? The answer is No. The browser will try to avoid downloading the image assets as much as possible. And this behavior is pleasantly consistent across IE, FF, Safari (for Windows), Opera.

Test #1

I tried this stylesheet:

body {
    background: url(bg.png);
}
#some-id {
    background: url(ie.png);
}
.some-class-name {
    background: url(o.png);
}
p {
    background: url(image.php);
}

And applied it to a blank page that doesn't have any paragraphs, or elements with class name some-class-name, or ID some-id. The result was that only bg.png was downloaded, which is great.

> test #1

Test #2

I added a button which, when clicked, creates a new P element and sets a 3 seconds timeout before appending the new element to the body. (The background for the P element is the script image.php which only sleeps for 3 seconds for demonstration purposes, before redirecting to ff.png.) The result is that the image is requested only after the element is added to the document flow, not when it was created. Which is smart. It ain't added 'till it's added.

> test #2 (same file as test #1, btw)

Test #3

OK, modified test #2. A P element is created, its visibility is set to hidden and it's added to the document. It's officially in the document flow, although not currently displayed. My guess was that the browsers will download the image at this point. True for FF, IE7, Safari, Opera.

> test #3

Test #4

Here's the only inconsistence in the behavior of the browsers. A P element is created, but with display: none, then added to the document. So it's part of the DOM tree, but not really part of the document flow. Then another button changes display to block.

FF will wait for the display: block before downloading the image background
Opera will also wait until display: block
IE7 begins to crack under the pressure and downloads the image even with display: none
So is Safari

> test #4

So what?

Well, it's nice to know that the browsers won't download the images referred to from our stylesheets unless they are needed for rendering. There is a side effect though. If you happen to create dynamic elements that have some styles with background and no other similar elements already exists on the page, the user experience might suffer slightly as the image will generally be requested after the new element is added. In cases when this is important to you, you might need to take some extra care to make sure the image is downloaded before you add the new element. You might need to do for example a new Image() after initial page load. (reminds me of the good old days of image rollover buttons, those were the days, eh? Seems like no one does image rollover buttons and menus anymore. It's all css this, css that... ;) )

 

Dynamic SCRIPT and STYLE elements in IE

Friday, January 26th, 2007

So you know how to add external scripts and styles, using the DOM, after the page is loaded. And what if you don't have external files, but have some style definitions and some JS code as text and you want it inserted and executed into a page.

The DOM way

"Ha! An easy one", you'd say and then go like:

var ss = document.createElement('script');
var scr = 'alert("bah");';
var tt = document.createTextNode(scr);
ss.appendChild(tt);
var hh = document.getElementsByTagName('head')[0];
hh.appendChild(ss);

"Ha!" in turn says IE, "No way!"

The IE way for SCRIPT

The above won't work in IE, but you can use the text property instead of creating a text node. Interestingly enough, this also works in Firefox.

var ss = document.createElement('script');
var scr = 'alert("bah");';
ss.text = scr;
var hh = document.getElementsByTagName('head')[0];
hh.appendChild(ss);

The IE way for STYLE

STYLE, SCRIPT, what's the difference, they are merely elements of the DOM tree. For the normal browsers, yes, so creating a text node with the stylesheet body will work in Firefox. For IE, you need a workaround.

var ss1 = document.createElement('style');
var def = 'body {color: red;}';
ss1.setAttribute("type", "text/css");
ss1.styleSheet.cssText = def;
var hh1 = document.getElementsByTagName('head')[0];
hh1.appendChild(ss1);

Note that while in the SCRIPT case I took the liberty of skipping the type attribute, it's absolutely required here.

So with a bit of object sniffing, we can get a cross-browser solution:

var ss1 = document.createElement('style');
var def = 'body {color: red;}';
ss1.setAttribute("type", "text/css");
var hh1 = document.getElementsByTagName('head')[0];
hh1.appendChild(ss1);
if (ss1.styleSheet) {   // IE
    ss1.styleSheet.cssText = def;
} else {                // the world
    var tt1 = document.createTextNode(def);
    ss1.appendChild(tt1);
}

Update: note that it's important for IE that you append the style to the head *before* setting its content. Otherwise IE678 will *crash* is the css string contains an @import. Go figure!

 

Firebug console for IE

Wednesday, December 6th, 2006

Update: A better version of what I was trying to do is here. It works around the cross-domain permission problems in IE by not loading a page in the frame, but putting there the actual content.

Firebug - no words to describe how cool it is, really. After the recent new release (1.0. beta) the number of features is overwhelming. I for one can't live anymore without it, seriously.

One of the things I noticed on the website is the ability to use the Firebug console in other browsers than Firefox. I don't know if this existed before version 1.0 but if it did, it was the best kept secret. I am so addicted to the console in Firefox, I use it all the time to tweak a few things here and there when I'm working on a page. Recently I was looking for something similar for IE, but couldn't find it. Lo and behold, it was right under my nose.

So, here's the page that describes how to use Firebug in IE (and others). Basically you unzip the Firebug Lite files somewhere on your server and then you include firebug.js in your pages. But why stop there? And isn't it possible to avoid including this script on every page (and forgetting to remove once you're done, or removing it prematurelly since a page, just like a painting, is never really finished). Bookmarklets to the rescue!

I wanted to host the Firebug files on my hard-drive and then use a javascript dynamic include to load firebug.js via a bookmarklet. This way I would be able to load the firebug console every time I want it, on any page. Unfortunatelly IE's security policy won't allow it. Then?

Solution

The solution I came up with is:

  1. you copy the Firebug Lite files somewhere on your server
  2. you call a bookmarklet that will load firebug.js
  3. you hit F12 and you have a console!

This procedure has to be repeated for every domain you're working on, because of the security policy that won't allow cross-domain frame scripting. You can have one copy for your http://localhost and one for every domain. To ease the creation of bookmarklets that load firebug.js, I came up with a Firebug bookmarklet generator.

In action

  1. I copied Firebug Lite files (get the .zip) on this server (phpied.com), they are here.
  2. I (and you can try the same) generate a bookmarklet, using the bookmarklet tool
  3. Add the generated bookmarklet to the favorites
  4. Go to any page on phpied.com
  5. Click the new favorite
  6. Hit F12 to show/hide the console

Here's how (a readable version of) the generated code looks like:

javascript:(function(){
  var firebug_js = document.createElement('script');
  firebug_js.setAttribute('type', 'text/javascript');
  firebug_js.src = 'http%3A//www.phpied.com/files/firebug/firebug.js';
  document.getElementsByTagName('head')[0].appendChild(firebug_js);
  firebug_js.onreadystatechange = function () {
    if (firebug_js.readyState == 'complete') {
      console.open()
    }
  }
})()

Minor improvement to the console

The Firebug Lite console executes the code you type, but doesn't show it again when you use the up/down arrows, the way it does in Firefox. So I added this feature (copying from myself), you can replace the firebug.js you download with my version.

Not sold yet?

Here's a screenshot of the console in action, I used it to change my photo on the homepage.

firebug-ie-console.png

Go ahead, please

I strongly encourage everyone to try this out. Firebug is a beautiful thing and using even a bit of it in IE is great.

 

Rendering styles

Wednesday, October 4th, 2006

The question is - what will a browser do, given a page with several stylesheets, each of them probably overwriting definitions from the previous ones? Will the browser render the page using the first received css file, while downloading the other ones and after that partially re-rendering where required? The answer is: no, the browser will wait until all CSS files are downloaded, (then probably merge all definitions, just a wild guess) and will render once.

Test

I did this test - one page with two stylesheets that contain pretty much the same selectors for different table styles (thanks to this gallery). Each of the CSS files is actually a PHP script and has a call to sleep(), one sleeps for 5 seconds, the other one for 10.

Result

The browser sits there and waits for the both styles, rendering nothing (except for the page title, but that's not really rendering, is it?). So nothing happens for 10 seconds, then the second style is used for the final rendering. This happens in both FF and IE.

Misc

I also tried sleeping in the actual page, and flushing the output after each row. In my home environment FF renders each row as it's received, but in my hosted environment, it waits for the whole table. IE alsways waits for the complete table.

If I put the page to too much sleep so that the php script dies before the second stylesheet is dowloaded, the browser uses whatever is at hand (css1) to render the page.

Demo/download

 

quirks or no quirks bookmarklet

Wednesday, September 20th, 2006

Here's a bookmarklet that will tell you whether or not the browser renders your page in Standards Complaiance Mode or Quirks Mode. The bookmarklet will figure this out for the page as well as for all the frames (and their frames) recursively. Enjoy!

Install

Drag this to your bookmarklets/favorites or right click and add to favorites:

quirks or !quirks

Sample output

Here's a sample output, produced when used in my WordPress backend when writing this post:
quirks.png

As you can see the page has two frames (probably iframes, doesn't matter), one of them is rendered in Standards Compliant Mode (CSS1Compat) the other one is in Quirks Mode (BackCompat). The overall document is CSS1Compat as well. For the frames, if they were named, you would see the name of the frame before the URL brackets.

And this is GMail, wow, lotsa frames, none compliant ;)
quircks-gmail.png

The code

The code is pretty simple, just accessing the compatMode of the the document object. Here it is in more human readable form (not one long line like bookmarket code).

var response = 'Document mode: ' + document.compatMode;
function checkFrames(w) {
  if(w.frames && w.frames.length>0){
    response+='\\n\\n';
    for(var i=0;i<w.frames.length;i++){
      var fr=w.frames[i];
      try {
        response+=fr.name +
              ' ('+fr.document.location+') - '+
              fr.document.compatMode+'\\n';
      } catch (e) {
        response+='Could not access this frame\\n';
      }
      checkFrames(fr);
    }
  }
}
checkFrames(window);
alert(response);