Ajax Bestiary: A Javascript Field Guide
 
Ajax Bestiary: A Javascript Field Guide
 
 

Entries Tagged as 'Internet Explorer'

Page Visibility API

Posted by Dave Mahon

To date, it has not been easy to tell, using JavaScript, whether the user can see your page or not. Sure, we have load, focus, blur and unload, but these have proven quirky and inconsistent in the past. Certain environments give us the ability to jump between windows and tabs, and even scroll pages without giving focus to the browser window. Automatic lock screens leave your web page active, but inaccessible. Further, Google is developing a feature for Chrome wherein a page can load and render before it is made visible to the end user.

If you’ve ever received a message on Facebook when it was the selected tab, but not necessarily the selected window and application, and it hasn’t made a noise, you’ve experienced some of this quirkiness.

So what can you do with this new API? You can slow down your application, suspend timers and clocks, and generally make your page behave better in a cooperative multitasking environment. In short, still more of what used to require Flash to implement no longer does.

Chrome introduced this new, simple API in 13 and the W3C has a group standardizing it. Firefox and IE will both see it added in their respective version 10 browsers.

To use it, you really only need to know two properties and one event.

The document.visibilityState property can be any one of four values, but the two most common (and only required) ones are hidden and visible. Now, when the page loads, you can know whether your content is actually exposed to the user or not.

The document.hidden provides a simple boolean.

Happily, the standards do partially specify how other applications can interact with these properties:

As examples, the attribute returns true when:

  • The User Agent is minimized.
  • The User Agent is not minimized, but the page is on a background tab.
  • The Operating System lock screen is shown.
  • The User Agent is minimized and a preview is shown.

The visibilityChange event will fire when this state changes after the initial page load, so you needn’t check it obsessively.

Note that Chrome uses the webkit prefix for this API, its properties and its event. Firefox and IE are expected to use the W3C version, without prefixes.

Bring HTML5 and ECMAScript 5 to a browser near you

Posted by Dave Mahon

Unless you develop in-house sites for companies that have standardized on a very modern browser, you’re stuck developing sites for IE8 and even IE7. (Heaven help you if you’re still developing for IE6).

How then do you access some of the powerful features of ECMAScript or the contextual tags of HTML5 without having to write layer upon layer of fallbacks?

Your fellow JavaScript developers have produced shims to emulate most of this functionality, even in environments where Chrome Frame isn’t viable.

The html5shiv project is still under active development. It not only adds support for HTML5 contextual tags, it sets their default styling and makes them print in IE8 and below. Naturally, their canvas element is not going to have the behaviors you see in other browsers, but at least you can get away with a single set of markup and fewer CSS rules.

The es5-shim project is also under active development. It alters prototypes of native objects, so I wouldn’t combine it with prototype.js. This library also has the benefit of being well documented – including what does and does not differ from the specification. Heed the cautions from Kris Kowal, its author, though:

“As closely as possible to ES5″ is not very close. Many of these shims are intended only to allow code to be written to ES5 without causing run-time errors in older engines. In many cases, this means that these shims cause many ES5 methods to silently fail. Decide carefully whether this is what you want.

Unexpected call to method or property access

Posted by Dave Mahon

If you’ve ever used jQuery’s append or prepend methods in IE8 with complex HTML fragments, you’ll probably recognize this message. I ran into it very recently when I was loading relatively simple HTML containing a relatively complex script into a modal window.

Naturally, the bug only occurred in Internet Explorer and the failure happened at line 4 of this block:
append: function() {
return this.domManip(arguments, true, function( elem ) {
if ( this.nodeType === 1 ) {
this.appendChild( elem );
}
});}

This seems like the least likely point of failure imaginable, but there was the error preventing the rest of the script from executing.

I found plenty of theories. There were some recurring themes though.

First, it is critical that you append well-formed HTML or the browser may justifiably fail. I tested mine fragment by wrapping it in some very basic HTML, HEAD and BODY tags with a DOCTYPE declaration and feeding it to the W3C Validator. That, unfortunately, did not work.

I disregarded the theories about buttons, because I had other scripts that referenced buttons and they worked fine.

One commenter (zmonteca) in the jQuery forums mentioned, “DispHTMLCommentElement,” which got me thinking. What exactly were this and elem and the moment of failure? After adding this to the Watch variables, I saw the problem. At some point in the loop, this became the SCRIPT element and elem became the DIV. Infuriatingly, at earlier iterations, the same nodes were in the correct order!

Now it all made sense. Both are valid HTML DOM nodes of type 1 (element), but it makes no sense to the browser to embed a DIV inside of a SCRIPT, just as you wouldn’t embed a BUTTON inside of an IMG. It would be downright… unexpected!

So the morals of the story are

  1. Always write clean, valid markup
  2. Be comfortable performing stack traces
  3. Really understand not just JavaScript, but HTML as well

Oh, and are you’re wondering how I dealt with the situation? I’ll own up to introducing a hack-ish patch to my copy of jQuery 1.7. I simply checked the tagName property of this and if it is SCRIPT, I reverse this and elem. This particular project is unlikely to have dynamically loaded OBJECT tags, so this seemed the fastest, cleanest way to deal with a bad issue when the markup itself validated.

Getting Started With Chrome Frame

Posted by Don Albrecht

As a follow up to my last post, I thought I’d put out an instruction on actually creating a Google Chrome retreat.

Step 1: Add a Chrome Frame header to your site.

Add this to the head of the Chrome Frame dependent pages:

<meta http-equiv="X-UA-Compatible" content="chrome=1">.

If this isn’t practical, You can also configure your server to send an HTTP header of

X-UA-Compatible: chrome=1

I advise against this approach, however because server configs won’t be immediately available to other users on a site.  By its nature, a Chrome Frame retreat should be considered a reasonably unique event.  It’s good to clearly document this dependency for anyone else who may need to debug things later.  Once you’ve started to retreat, make sure you’re applying the header to every page of the site.  The two engines render things differently and you may create an inconsistent experience for your users if they are transitioning between Chrome Frame and Trident environments frequently.  In my experience, border-radius and drop shadow toggling on and off is a pretty strong “It’s Broken” signal for end users.

Step 2: Identify your Dependency Wall.

  1. Identify the range of pages / screens in your web app that you can’t support in IE.
  2. Identify the navigation routes to those pages.
  3. Leverage the routes to find natural locations for gatekeepers.
      1. Login Screens (the perfect location)
      2. Tutorial Screens (A natural context fit for users)
      3. An Advanced Mode (Similar to the “enriched mode” Microsoft uses for ActiveX requiring cloud apps).
    • If you’re lucky, this will be a screen somewhere beyond your conversion pipeline.
  4. Create an interception event for IE users.  You can bake this into your app, but the easy way is with conditional comments.
    1. Identify the Dom Nodes in your layout that must be presented to the user for them to progress.
    2. <div id=’loginbox’>…</div>
    3. Create a conditional comment to hide the nodes and prompt the user to install Chrome Frame.
    1. Be careful of z-index.  The chrome install iframe doesn’t work well if IE is floating some other content node on top of the install button.
    2. UN-INSTALL chrome frame once you’ve tested your retreat perimeter.  This is a big deal for ongoing development as you won’t be testing in IE otherwise.
    3. Becareful about your conversion pipeline.  Your key selling points may be behind the perimeter, but the act of demanding Chrome Frame is a pretty severe barrier to forward movement.
  5. <html>
    <body> <div id='prompt'></div>
    <!--[if IE]>
    <script type="text/javascript"
    src="http://ajax.googleapis.com/ajax/libs/chrome-frame/1/CFInstall.min.js"></script>
    <style>
    #loginBox{
    display:none;
    }
    </style>

    <script>

    window.attachEvent("onload", function() {
    CFInstall.check({
    mode: "inline",
    }); });
    </script> <![endif]-->
    </body>
    </html>

    Other Lessons Learned

    Here’s a few lessons learned from deploying this.