Skip navigation.

Claws, fangs, fur...

...the bear essentials

Posts tagged with "technology"

Unobtrusive Javascript

, ,

There are scripts we wish to use only in certain browsers... mostly because other browsers don't need them. And when we do need those scripts, they shouldn't obtruse on the other browsers. The methods used to supply scripts this way are called "unobtrusive".

Why?

Because we do want to separate behaviour from mark-up and we don't want browsers yelling at us about stuff they don't need anyway.

Like what?

  • Like scripts that simulate CSS-defined hover behaviours so they work in MSIE6 and 7, too. Or like scripts that simulate Web Forms 2.0 behaviour in MSIE6 and 7. (Both of these are supported natively by Opera. Firefox only supports the first, but the second is expected soon.)
  • But also like a braille-reader that doesn't know about Javascript at all, or a search spider crawling and indexing our site.


How?

First we remove any and all in-line javascript handlers from the (x)html source. If the other browsers aren't going to use it, they shouldn't see it.

Then we specify that only the addressed browser is going to read the script. Since in most cases MSIE is the only odd one out, MS has been helpful enough to provide Conditional Comments, that are used by MSIE only. They work like this:

<!--[if lte IE 7]
<script type="text/javascript" src="MSIEScript.js"></script>
[endif]-->


Of course this works with in-page script as well:

<!--[if lte IE 7]
<script type="text/javascript">
/* <![CDATA[ */
alert('MSIE-only XHTML-javascript');
/* ]]> */
</script>
[endif]-->


or, for good-ole html:

<!--[if lte IE 7]
<script type="text/javascript">
alert('MSIE-only HTML-javascript');
</script>
[endif]-->


Thirdly, we reattach the events we removed in the first step. All modern DOM2 browsers use the W3's addEventListener for that, except MSIE, whose programmers in all their wisdom decided to create their own implementation known as attachEvent. And no, they didn't add addEventListener as a courtesy method either.

So we figure out which method is used by the browser. A nice way for doing this was described by someone else on the net: JavaScript Tip #1: Speed Up Object Detection. I'll just take it one step further:

<script type="text/javascript">
var addEvent;
if (document.addEventListener) {
 addEvent = function(element, type, handler) {
  element.addEventListener(type, handler, false);
 };
} else if (document.attachEvent) {
 addEvent = function(element, type, handler) {
  element.attachEvent("on" + type, handler);
 };
} else {
 addEvent = function(element, type, handler) {
  element["on" + type] = handler;
 };
}</script>


Will this work? Not all the time. There's a problem with parameters: the above-mentioned methods don't allow the immediate passing-through of parameters. So we have to find a way to read them from the window.event object. Especially when we're hovering or clicking and we want to know which element was hovered or clicked. Unfortunately, MSIE doesn't follow the DOM2 recommendation, so once more we need an exception.

<script type="text/javascript">
function readEventTarget(e){
 if(!e){
  if((window)&&(window.event)){
   e=window.event
  }else{
   return false
  }
 }
 if(e.target){return e.target}
 if(e.srcElement){return e.srcElement}
 return false;
}</script>


Will this work? Again, not all the time. In MSIE and Firefox, in case of an LI-element with only block-level elements inside it, the hover and click will only reach the block-level elements and not the LI-element. In Opera however, the interaction will reach the LI-element. So once more we need to open our bag-o-tricks:

Instead of adding static events and figuring out who called it, we can add "live" events. This is done by creating functions on the fly. Disadvantage: memory footprint.

<script type="text/javascript">
function init(){
  if((window)&&(document)&&(document.childNodes)){
    var c,d=document.childNodes;
    for(var i=0; i<d.length; i++){
      c=d[i];
      if((c)&&(c.tagName)&&(c.tagName=='UL')){
        c.onmouseover=new function(){
          window.status=('Number of items in list: '+this.length);
        };
      }
      c=null;
    }
  }
}</script>


(Of course we can combine this with a static event by calling it from inside the new function.)

See that 'this' thing in there? Why can't we simply ask for c.length, since it's supposedly the same object? Because that 'c' is a variable that holds a value only inside that loop. Once the new function gets called, the loop no longer exists, thus the 'c' variable no longer points to anything. Querying a length of nothing results in an error.

Instead we query the length of the 'this' object. At the moment of function execution, 'this' refers to the executioner, which happens to be the UL.

This by the way can lead to a whole bunch of confusion, especially if it isn't apparent which object is the executioner.




There's a lot more to say about unobtrusive javascript. The main idea is not to bog down a browser with functions it either can't use anyway, or supports natively.

I hate MSIE

, , ,

Again.

Why?

First of all, because even the latest version (7) of this browser thinks it is impossible to hover a cursor over anything other than a hyperlink. No, it says, you cannot hover your cursor over an image. No, it isn’t possible to hover over a list item. Nope, hovering over a button does not exist.

Sure, you can define these behaviours in the html code… but that’s SOOOOO 90s! Ef off! We put these behaviours into our CSS. Right?

Yes, and that’s exactly where MSIE4, 5, 5.5, 6 and yes, even 7 go wrong.

(Edited to add the following: my latest tests show that MSIE 7 does perform hover actions on elements other than hyperlinks. Previous versions refused.)

Does the competition allow CSS-specified hover behaviours over other things than a hyperlink? Sure they do! Anything we could possibly desire and code into a web page is hoverable, according to Opera, Firefox, Safari, Camino, K-Meleon, Konqueror, Netscape, Mozilla.

Thank you, Microsoft, for refusing to implement standard behaviour.

Can we solve this stupidity?

Say, “yes Junior”! (”Yes, Junior!”)

We can add a bit of javascript to our web page, specifically for MSIE. We can hide it from decent browsers by using Conditional Comments (another MS invention, this time it cames in handy).

And here comes the second reason for my renewed hatred of MS’s poor excuse of a web browser:

Because none of the other browsers require Javascript for the hover behaviour, we want to inject it completely unobstrusively. Meaning: the decent browsers don’t get to see any of the javascript. Meaning: any behaviour that needs javascript will have to receive it… by javascript.

Can we do that? Yes, we can!

Of course, MS implements its own method. Instead of following the W3 recommendation of the DOM-2 object model for connecting events to HTML (element.addEventListener), it uses its proprietary method (element.attachEvent), and it refuses to implement the standard method. Thank you once more, MS, for making my life harder.

However, we can work with that kind of arrogance. Since the script only needs to work in MS anyway, we shouldn’t really be bothered. Annoying, yes.

First we attach our own custom initialisation to the loading of the page. Here we find hate-reason 2.1: we can’t attach it to the document, no, MSIE wants us to attach it to the bloody window. Otherwise, the initialisation simply won’t be executed. Why is this a problem? Because the window isn’t part of the page! It’s part of the browser and the page author should never be allowed to change the user’s browsing environment! So thank you, MS, for impeding on the user’s independence!

Second, we write our custom initialisation. We tell it to find the elements we wish to hover and attach the same javascript function to their hover event. There we find hate-reason 2.2: we can’t use a predefined function. Instead we have to create the function for every element. Bloody waste of memory.

Reason? Parameters. When using a predefined javascript function to hover an element, we have to pass the element itself to the function, so it can figure out which element was hovered. The Javascript language definition (a.k.a. Ecmascript) however prevents the author from supplying this information during the attachment process… which is exactly where we want to supply it!

So the hover function doesn’t know what to hover. Can we get around that? Sure we can! We just call up the window event and figure out what element was hovered! Even MSIE can do that! Right?

Kind-of.

MSIE’s implementation of the event object differs greatly from the W3 DOM2 standard. First of all, it has no target property. It does have a proprietary srcElement property however, that fills this omission. (Now the nice thing to do, MS, is to add a target property anyway, and keep the srcElement for backwards compatibility.) But what does this propery say? Does it say what element was hovered?

Kind-of.

In MSIE, the hovering doesn’t bubble up to its parent. So for very simple, flat web pages this might do. But it won’t even work when hovering a list item with a block inside of it… because it will indicate the block was hovered instead of the list item.

Well, isn’t that enough? No! We don’t want to attach this hovering behaviour to every possible element inside a list item… We want the hovering on the list item itself! Exactly the same way it works when we specify an onmouseover event on the list item. Please don’t tell me you can do the one but not the other, MSIE!

Helas.

So strike the predefined function. Instead, we use an in-line, newly created function for each list item. Great.

Thank you, Microsoft, for increasing the amount of memory used by a web page. Thank you, for forcing us to increase the complexity of our scripts. Thank you, for impeding on the user’s independence. Thank you, for assuming only hyperlinks can be hovered.

Thank you, for making decent, well-willing web authors waste 3 days trying to find ways around your crummy, short-sighted omissions.

xhtml and in-page scripts / styles

, , , ...

So you’ve decided to write XHtml instead of Html. And all of a sudden you realise: my in-page scripts and styles stopped working! (Or at least they stopped validating.)

Why, you ask?

Mostly because scripts and styles use characters that Html doesn’t really like, for instance the &. This will break validation and in some browsers it will prevent scripts and styles from being interpreted at all.

To solve this, the XHtml 1.0 recommendation describes three methods:
  1. omit any usage of html entity references inside your scripts
  2. move your scripts from in-line to a separate file
  3. escape the entire script by marking it as CDATA


The first method changes the scripts and makes them unusable.

The second method is nice and best practice, but isn’t always useful.

The third method doesn’t work.

What?

The third method doesn’t work. At least not in any current browser I’ve tested.

Why?

Let’s see how the recommendation sees this escaping:

<script type="text/javascript">
<![CDATA[
alert('hello, world!');
]]>
</script>

Guess what? Most current browsers don’t accept the code for specifying a CDATA block inside a javascript block. So they won’t recognise the script, won’t execute it, or may generate lots of errors.

Is there a solution? Yes, there is! Remember from scripts in Html, that we used to add Html comment tags to hide the scripts from unscripted browsers? We can do the same here, with the same effect!

<script type="text/javascript">
/* <![CDATA[ */
alert("hello, world!");
/* ]]> */
</script>

Easy, no?

And we can do the exact same with our style block, too:

<style type="text/css">
/* <![CDATA[ */
html { background: black; }
/* ]]> */
</style>

Now the blocks validate and will get executed.

This method was tested in:
  • Opera 9.2
  • Firefox 2.0
  • K-Meleon 1.1
  • MSIE 6
  • MSIE 7

Google Maps Sucks

, , , ...

For community websites I’m building a member locator using Google Maps to allow members to locate themselves and others around them. Pretty neat.

What isn’t neat is the following: the Google example code shows to use an XHTML doctype. Yay! Erm, no. See, in order for Microsoft’s excuse for a web browser to understand XHTML, it has to treat it as HTML, rendering all the advantages of using XHTML completely useless. The server admins know that, and instead of sending XHTML as XHTML, they send it as HTML. So the document claims to be XHTML, while both the server and the browser assume it to be HTML.

Big difference? Big difference!

Why?

Because XHTML is much, much more strict than HTML, and much, much more extensible. It’s a web author’s godsend, and a browser’s nightmare at the same time.

As said, Google Maps examples show that authors should be using XHTML files. Since I’m testing my scripts in decent browsers (Firefox, Opera), I assumed that it would be no problem to have my server send them as XHTML. Yes, I had to be the wise guy who actually tried to adhere to the standards as recommended. Took me 4 hours to recognise Google’s mistake, and 2 seconds to correct it.

The symptom: my browser wouldn’t load some of Google’s scripts, indicated by its complaining that my code was calling objects that didn’t exist.

The problem: Google’s Map scripts use a function that overwrites the running application with new code. (For insiders: check out the GScript function.)

Why is this a problem? Because the running application is being erased and overwritten by itself. XHTML is protected from such malpractice. Thus Google’s code won’t work.

HTML, as said, is far less strict. HTML browsers assume that the new code is going to be appended to the existing application, instead of overwriting it. So HTML browsers are mis-executing code which then results in a desired behaviour, while XHTML browsers are correctly exucting the same code, which then results in undesired behaviour… nice catch-22.

Google Maps documentation doesn’t talk about this catch. They probably assume everyone sends their XHTML documents as HTML, just like Microsoft. Their examples are 2 years old, and I’ve seen complaints about this same topic as far back as 18 months.

By advocating mis-use and malpractice, Google promotes the confusion about web standards, and suports Microsoft’s wish to dominate the web. I wonder whether Google has stock in Microsoft.

I haven’t found a strict XHTML solution yet, but if I did, it wouldn’t work in Microsoft’s poor excuse of a browser anyway.

Browser-incompatibility: lazy author?

, , , ...

My day job involves creating web applications (and related stuff like business consultancy, information analysis, and education). One of our solutions is a CMS manufactured by a third company. This CMS is based upon Microsoft Windows technology, so guess with how many browsers its Control Panels are compatible?

Yes, a rhetorical question.

To enter a new article, an Editor needs to open the Editor Control Panel. It features a wysiwig content editor, not unlike the one I'm using right now to add this post. With one major difference: it's based on the MS HTML Editor object. When the system's architect or initial implementor made that choice, they severely limited the potential of their editor to whatever functionality Microsoft decides to offer.

Then people like me start using the CMS and ask: why doesn't this work in my browser of choice? Why do you force me to use Microsoft's browser? Inevitably, the incompatibility issue is raised, but when we dig deeper, at the heart of the matter we find a different issue.

No, the CMS programmers aren't lazy. They also aren't stupid: mostly, they know what they're doing. They know their system is limited. But their hands are tied by their managers, who decided to invest in Microsoft technology.

Why is this important? Because of the steep learning curve most Microsoft technology imposes. After spending thousands of hours and buckloads of money on creating something that finally works kind-of appropriately in a Microsoft realm, management fears that supporting a different realm requires a similar investment. That steers the problem away from technology and usability, and into commerce.

We can recognise a similar process in the minds of web authors. After spending hours, days, weeks, to get their web site to work in the world's most used browser, they fear that supporting any other browser requires an equal investment, thus they cop out and label their site "best viewed with..."

This isn't laziness. Instead, it's an unbalanced r.o.i. They might even recognise the problem, and still choose to continue, because they know no other way. They're stuck.

How can we overcome this problem? A highway allows cars of any brand to use it, then why limit the brands on the information super-highway? Can we convince the people who make the investment decisions that leaning on any technology vendor this much, results in loss of potential clients, and thus loss of profit?
Download Opera, the fastest and most secure browser