I have come across a weird situation which looks like a security bug in IE. This concerns DOM manipulation by creating new objects using innerHTML.
Some background on the topic: In a certain project, I am using a 3rd party WYSIWYG Html rich editor. As all of these editors, they create new DOM elements (mainly fancy toolbars with buttons). After development, the application was installed on some QA machines and servers. Much to my surprise, when browsing to the application from the actual server itself (i.e. localhost), in IE all the scripting worked well,
except
for the rich editor. The editor’s html
was
built successfully, but the toolbar buttons were inactive.
I spent some time investigating this. If no scripting was allowed what so ever, the entire website wouldn’t have functioned at all, and the editor wouldn’t have shown up – this wasn’t the case. JavaScript was clearly working everywhere except for those toolbar buttons. Testing with NOSCRIPT tag also failed (i.e. JavaScript is enabled). I checked the Security zones under IE and found the following: In
Local Intranet
the Active Scripting settings was set to Enabled, but in the
Internet
Security zone it was set to Disabled. I tried to set it to Enabled, although I was absolutely certain that there was nothing in the web application nor in the rich editor that was using the
Internet
zone, and behold – the buttons started to work after refreshing the page!
Turns out that something the rich editor was doing was considered by IE to be in the
Internet
zone, or so it seems, and therefore the button events did not work. I debugged this more and more till I found the actual innerHTML JavaScript command which the editor’s code used in order to build the editor’s DOM elements, and nothing seemed wrong. Debugging that same code also provided no errors in the Console window, and nothing hinted at this problem. But when I reproduced this by disabling the Active Scripting settings in the
Internet
zone again, the buttons were inactive again.
So, I created my own sample and added DOM elements using innerHTML, in a similar fashion to how the rich editor added it’s own buttons, in order to try to emulate and understand what was going on here. I enabled Active Scripting settings both in the
Internet
zone and in the
Local Intranet
zone. The following works as expected:
Note (below) the Security zone of the page (in the IE browser window, right click and select Properties for this dialog to come up). As you can clearly see in the screenshot, IE recognizes that the browsed page is in the
Local Intranet
zone (note the ‘localhost’ in the address bar). This means that local JavaScript code runs great, where as non-local JavaScript code is disabled (for example, if we switch to jQuery on the CDN instead of my local jQuery js file, jQuery won’t work. That is why I had to download jQuery and use it locally in this example).
In order to reproduce this bug, we’ll disable Active Scripting on the
Internet
zone, and leave it as enabled for the
Local Intranet
zone. Here’s the
Internet
zone:
The
Local Intranet
remains as Enabled:
Now we’ll refresh the page. While the ‘go’ is rendered properly on the browser, it is now inactive, despite the fact that the entire page is running only local JavaScript code. Although I can clearly see the “onclick” event in the Debugger Tools on the newly created anchor – it is not working. No error message was thrown, and no message was shown in the Console windows of the Debugging Tools. I could not find any documentation about this behavior in Microsoft’s different sites (but it does not mean that there isn’t any).
Workaround
At this stage I was puzzled about my next move. I had to try and think of a workaround for this, or I’ll risk having an HTML editor which is non functional in this scenario, with no way to warn the user and explain how to change this settings (NOSCRIPT isn’t working here so there really isn’t a way to tell using JavaScript). This situation is not necessarily uncommon: on some Windows server installations it looks like by default IE is configured to disable Active Scripting on the
Internet
Security zone. So potentially DOM elements created by local JavaScripts may be malfunctioning. I attempted several workarounds using jQuery to “re-attach” events. This worked, but I had trouble to figure out when to perform the workaround or risk the event occurring
twice
, in case Active Scripting is enabled. After experimenting some more, I noticed that there are times in which innerHTML code which included events
does work
. Finally I understood the following “rule”:
if innerHTML is set on an element which is already a part of the DOM tree, the events work properly. If the innerHTML is set on an element which is not yet a part of the DOM (i.e. new element which was just created), and only then the element is added to the DOM – the event does not work!
Note the following change in the code:
As you can see, by switching lines 12 and 13, I added the wrapping div to the DOM tree first, and only then I set the innerHTML property to an anchor with an “onclick” event. I refreshed the page and now the event worked as expected! It’s as if IE creates the elements well but the events are not triggered, if the newly created element is not yet attached to the DOM. Appending the element afterwards is simply “too late”. Note, that if I had set the innerHTML of the #container div directly (i.e. without creating a second div), this would have worked as #container div is already a part of the DOM.
The same behavior goes for jQuery Insertion – the following ‘append’ creates the elements successfully, but the ‘onclick’ does not work (jQuery 1.7.1):
I assume that jQuery first creates an element and sets it’s innerHTML, prior to attaching it to the DOM. So the same behavior applies. The workaround is very easy:
As you can see, jQuery’s ‘html’ works on an existing DOM element (‘#container’) and that is why the event works, whereas ‘append’ seems to create a new DOM element and set it’s innerHTML before the element is attached to the DOM, and that is why it fails.
“Armed with this knowledge”, the workaround on how to solve the non-working buttons of the Html editor is now very simple, and applies to both enabled and disabled Active Scripting scenarios; all that is required, is to “re-apply” the innerHTML property of the newly created elements. Because those new elements are now a part of the DOM, setting the innerHTML recreates the child elements and their events correctly this time. I used jQuery for setting the innerHTML. For example:
Lines 11-14 simulate code which creates the html in a buggy manner.
Line 17 applies this workaround for IE only.
Line 18 takes the DOM’s html from the newly created div element.
Line 19 “re-applies” the html, which basically means that the anchor tag and the ‘onclick’ are recreated.
This time IE handles the ‘onclick’ well:
Summary
I believe that the described behavior is an IE bug. If MS wanted to block element creation using innerHTML when
Internet
zone Active Scripting is disabled, it would have blocked it entirely disallowing the above workaround. Fortunately, this bug is easy to workaround once you realize what it is. Naturally, this workaround could prove to be expensive, so I would limit using it only to specific cases where you have no alternative, and only for IE (like I had when using the 3rd party editor).
Enter your email address to subscribe to this blog and receive notifications of new posts by email.
Email Address:
Privacy & Cookies: This site uses cookies. By continuing to use this website, you agree to their use.
To find out more, including how to control cookies, see here:
Cookie Policy