thingsinjars

  • 1 Apr 2011

    Investigating IE's innerHTML

    Be warned, this is a long and quite techy post that rambles a bit. It's about JS, jQuery, IE and XML namespaces. Be careful.

    I'm currently working on a tool which uses JS to parse an XML document and output it as JSON. Straightforward enough, you'd think. The issue I'm fighting against crops up when the XML tags have an arbitrary namespace. True enough, some of the files I'll be processing have this namespace defined at the top of the document but the tool has to be robust enough to cope when they don't.

    To cut a long story short, IE6, IE7 and IE8 have an interesting attitude to innerHTML when the tags you are trying to insert have a namespace. IE9 seems to do the job as you'd expect. I've created some jsFiddle pages which try and identify the issues. They both use QUnit and the test code is included below.

    createElement nodeName vs get(0).nodeName

    View this on jsFiddle.net

    I started off using jQuery to help identify this as the tool uses jQuery for the XML heavy-lifting. The two tests in this demo create elements in two different ways. First, we create the element using document.createElement and grab the nodeName then we use jQuery's constructor and use get(0) to grab the bare DOM element's nodeName. Also, in this first set of tests, we're creating non-standard elements.

    
    test("Compare elements without namespace", function() {
     var element1, element2;
    
     element1 = document.createElement('spud').nodeName;
     element2 = $('<spud/>').get(0).nodeName;
    
     equals(element1, element2, "We expect these to match");
    });
    

    The code above runs fine everywhere – IE, FireFox, Opera, Chrome, etc. etc. Good.

    
    test("Compare elements with namespace", function() {
     var element1, element2;
    
     element1 = document.createElement('a:spud').nodeName;
     element2 = $('<a:spud/>').get(0).nodeName;
    
     equals(element1, element2, "We expect these to match");
    });
    

    This runs fine in non-IE browsers, they all report the nodeName as 'a:spud'. IE now reports the nodeName as 'spud'. Ah. I dug through the jQuery source, tracking down the bare roots of the constructor and eventually figured out that just looking at the element itself isn't going to provide any clues. The bit that does the actual string-to-elements work (somewhere around line 5619 in jQuery 1.5.2) creates a container div then injects the (slightly modified) code as innerHTML. The issue must be in IE's interpretation of innerHTML, I thought to myself. And then to you by writing it here.

    .innerHTML aside

    or ‘jQuery is clever’

    Before we continue with this long and, ultimately, unnecessary investigation into namespaces, I have to take a small diversion to cover some smart stuff jQuery does. One thing in particular, in fact. Around that line I mentioned earlier (5619-ish), an extra bit of text is inserted into the innerHTML to cope with IE's oddity. If you are trying to create a non-standard element using innerHTML, IE will not complain but also just do pretty much nothing at all:

            var div = document.createElement('div');
            div.innerHTML = '<spud></spud>';
            alert(div.innerHTML);
    

    The above code will alert '<spud></spud>' in most browsers but '' in IE. What jQuery does is firstly wrap your element in an extra <div></div> (producing '<DIV></DIV>') then prepends the word 'div' to that. The innerHTML reported by IE is now 'div<DIV><SPUD></SPUD></DIV>'! There it is! Next, the extra gubbins is removed by calling .lastChild and you're left with innerHTML = '<SPUD></SPUD>'. That's pretty darned clever.

    .innerHTML vs document.appendChild

    View this on jsFiddle.net

    Back on track. Armed with this little trick, we can reliably test innerHTML in IE using non-standard elements.

    
    module("Known elements (span)");
     test("Compare elements without namespace", function() {
      var div1, div2;
    
      div1 = document.createElement('div');
      div1.innerHTML = '<span></span>';
    
      div2 = document.createElement('div');
      div2.appendChild(document.createElement('span'));
    
      equals(div1.innerHTML.toLowerCase(), div2.innerHTML.toLowerCase(),
         "We expect these to match");
     });
    
     test("Compare elements with namespace", function() {
      var div1, div2;
    
      div1 = document.createElement('div');
      div1.innerHTML = '<u:span></u:span>';
    
      div2 = document.createElement('div');
      div2.appendChild(document.createElement('u:span'));
    
      equals(div1.innerHTML.toLowerCase(), div2.innerHTML.toLowerCase(),
         "We expect these to match");
     });
    

    The first test in this pair runs fine everywhere exactly as we'd hope and expect. The second fails miserably in IE. Let us quickly run the same test with unknown elements just to make sure we're identifying the right problem:

    module("Unknown elements (spud)");
     test("Compare elements without namespace", function() {
      var div1, div2;
    
      div1 = document.createElement('div');
      div1.innerHTML = 'div<div>' + '<spud></spud>' + '</div>';
      div1 = div1.lastChild;
    
      div2 = document.createElement('div');
      div2.appendChild(document.createElement('spud'));
    
      equals(div1.innerHTML.toLowerCase(), div2.innerHTML.toLowerCase(),
         "We expect these to match");
     });
     test("Compare elements with namespace", function() {
      var div1, div2;
    
      div1 = document.createElement('div');
      div1.innerHTML = 'div<div>' + '<u:spud></u:spud>' + '</div>';
      div1 = div1.lastChild;
    
      div2 = document.createElement('div');
      div2.appendChild(document.createElement('u:spud'));
    
      equals(div1.innerHTML.toLowerCase(), div2.innerHTML.toLowerCase(),
         "We expect these to match");
     });
    

    As before, the first test in this pair works fine, the second fails. Cool. Or not. Either way, you can now see that it doesn't really matter whether the elements are standard or custom and that little diversion we took earlier really was unnecessary. Still, you know more now about some of the cleverness in jQuery than you did before.

    It turns out the reason IE reports the nodeNames as the non-namespaced ones is because it has been busy behind the scenes and added an extra XML namespace prefix into our current context. The innerHTML of the div we filled up using innerHTML has been modified to:

      <?xml:namespace prefix = u />
      <u:span></u:span>
    

    Where'd that namespace declaration come from?! Goshdarnit, IE. From its point of view, within that little context, u:span is equivalent to span

    The most stripped-down example

    View this on jsFiddle

    Seriously, it does not get more fundamental than this.

    element = document.createElement('div');
    testHTML = '<div></div>';
    element.innerHTML = testHTML;
    element.innerHTML.toLowerCase() == testHTML.toLowerCase() 
    

    The last line there is true for all browsers.

    element = document.createElement('div');
    testHTML = '<a:div></a:div>';
    element.innerHTML = testHTML;
    element.innerHTML.toLowerCase() == testHTML.toLowerCase() 
    

    The last line there is true for all browsers except IE 6, 7 and 8!

    In conclusion?

    Ultimately, there are no winners here. Identifying the problem is quite different from fixing it. I've added a note to the relevant jQuery bug in the tracker but it's not so much a bug in jQuery as a humorous IE quirk. There's some talk of refactoring the .find() method to handle more complicated tagnames so this might get picked up then. The fix will probably be something along the lines of checking the outcome of the innerHTML doesn't have an unexpected namespace declaration when the selector has a colon in it:

    div.replace( /<\?[^>]*>/g, '' )

    I'd submit the patch myself but I'm having difficulty getting unmodified jQuery to build on any of my machines without failing most of the QUnit tests. I've probably typed something wrong.

    Javascript, Geek, Development

  • 1 Apr 2011

    Autogenerated Everything

    After seeing this collection of the 892 different ways you can partition a 3 x 4 grid1, I was struck by a thought. If these were generated as HTML templates, they could be combined with a couple of other useful websites and become a nice, API-driven site builder2.

    The process

    • On the site-building webpage, you'd enter a few keywords describing the site you want and drag a slider along between 1 and 12 to specify how many content areas you want. The value from the slider would be used to pick a template randomly from the number available for that combination of panels.
    • This template would be dropped into the middle of an HTML 5 boilerplate (possibly generated via Initializr)
    • The keywords would be passed to ColorAPI to generate an asthetically pleasing colour-scheme
    • The keywords would then be passed to FlickHoldr along with the dimensions of some of the areas from the template to get relevant imagery
    • Grab some lorem ipsum of the right length from LoremIpscream to fill out the content areas of the site
    • Done. Your asthetically pleasing, nicely designed site is ready to download within a few seconds.

    Once this service has been created, I'm fairly sure me and the rest of the industry will be out of a job.

    1 Technically, there are 3,164 ways you can partition the grid but most of them are vertical or horizontal mirrors of the 892 mentioned.

    2 There are a few references to 3x4grid.com which is apparently going to be an API-accessible collection of these layouts but there doesn't seem to be anything there yet.

    Ideas, Geek, Development, Opinion

  • 18 Mar 2011

    Shelvist

    I've been a bit quiet for the last couple of weeks. Now I can reveal why!

    All my spare time has been taken up building Shelvist (well, that and looking after a 4-month old). It's a way of keeping track of which books you have read, which ones you are currently reading and which ones you want to read. That's it.

    Just over a year ago, I started listening to a lot of audiobooks and, 12 months after I subscribed to Audible, I decided I wanted to see just how many books I'd read*. All the existing book tracking sites (e.g. Shelfari, Goodreads) focus on recommending, rating, reviewing which just seemed like hard work.

    *Listening to an audio book counts as reading. It just sounds weird if you tell people you listened to a book.

    Building this was also a chance to learn some new tech. I've been wanting to play with CakePHP for a while so this seemed like the best opportunity. It's been a while since I used any kind of framework and I've never used a PHP MVC framework (although I did build one last year as an intellectual exercise).

    I got over 90% of the site built in spare time over about 5 days and most of that was spent just reading the CakePHP docs. The reason for the lengthy delay between initial build and launch is down to that last 10%. As often happens with frameworks, nearly everything you'd ever need to do is bundled into some nice, easy to access functionality. That is, after all, kinda the point of a framework. It's the stuff that makes your product or website unique that proves trickier. I won't go into any details just now although I might come back to it in a later post.

    More later, first of all, go shelve some books: Shelvist.

    Geek, Development, Design, Toys

  • 30 Jan 2011

    Scoped Style

    I couldn't let it lie. The nifty JavaScript from the previous post was all well and good but I had to have a go at jQuery plugin-ifying it. It has been Enpluginated.

    Your options now are simple:

  • Have a look at a demo

  • Check out the source on GitHub

  • Download it and start scoping some styles.

  • If you still have no idea what I'm talking about, you can read about the attribute. There are still a couple of bugs when the scoped blocks are deeply nested within other scoped areas so I'm hoping someone with a keen sense of how to straighten out Webkit oddness can help. When browsers don't implement functionality, it's a bit tricky to guess what they'd mean.

    Aside from that, it's cross-browser (IE7+) compatible and ready to use. I'm interested to know if anyone finds it useful or finds any odd combinations of styles that don't scope nicely.

    Development, Javascript, CSS, Geek, Toys

  • newer posts
  • older posts

Categories

Toys, Guides, Opinion, Geek, Non-geek, Development, Design, CSS, JS, Open-source Ideas, Cartoons, Photos

Shop

Colourful clothes for colourful kids

I'm currently reading

Projects

  • Awsm Street – Kid's clothing
  • Stickture
  • Explanating
  • Open Source Snacks
  • My life in sans-serif
  • My life in monospace
Simon Madine (thingsinjars)

@thingsinjars.com

Hi, I’m Simon Madine and I make music, write books and code.

I’m the Engineering Lead for komment.

© 2025 Simon Madine