Wed 7 Mar 2007
Yesterday I had to look into a problem that was occuring in Safari with some code that I had written that was using the innerHTML property. This code works by creating some HTML using DOM functions, then adding some 3rd party HTML to one of the nodes. Though in most cases the 3rd party content is likely to be just a number, I wanted to allow for more interesting content, such as HTML entities, so I decide the simplest method would be to use innerHTML rather than parsing the content and generating the DOM nodes myself.
The code has been in use for several months without issue and has been working fine all this time in all the browsers I’ve tried it in. For some reason in this one particular scenario though, Safari was completely ignoring the attempts to set the innerHTML of the node. Setting the innerHTML and then on the following line attempting to read it was also giving an empty response. For example:
text = "foo";
node.innerHTML = text;
alert( "html="+node.innerHTML ); // Pops up message saying "html="
I tried numerous methods to fix this problem, including setting the property before and/or after adding the DOM node to the document. I also googled it which flagged up a number of related posts but these generally referred to pages that were served as XHTML (ie. pages with an .xhtml extension and MIME type application/xhtml+xml) and were not offering any solution.
One thing I did notice, though, was that if I typed into the address bar of Safari something along these lines:
javascript:node.innerHTML = "foo"
Then my change would occur, so it seemed that Safari was happy enough to set the innerHTML of the node, just not at the time I wanted it to, which led me onto my solution:
setTimeout( function() { node.innerHTML = text; }, 50 );
Yes, setting a timeout to occur in 50ms at which point the innerHTML would be set seemed to work. No idea why, and it is a bit nasty, but it was the only solution I could think of, and more importantly – it worked!
So hopefully if you see a similar problem and you need a solution, you’ll find this page and be able to make use of my dirty hack above to fix it.
Update
Firstly – something I should have mentioned. This problem has only been occurring since we started modifying the URL of the page from within JavaScript, i.e. window.location.hash = '#foo'. We knew this didn’t work exactly as we’d hope, as the spinning status indicator stays spinning as soon as we modify the location, but we really really wanted to modify the location. Disabling the URL modifying does fix the .innerHTML problem though.
Secondly – I recently noticed further problems. I was using code exactly like that above, setting the .innerHTML property, then setting it again 50ms later. Unfortunately this can have problems too because the first attempt might work, but the second one fail, leaving you with no content again! Also I have seen it fail twice in succession, only setting the content on the third time. The horrible solution I’ve ended up with is a recursive method that sets the .innerHTML, checks to see if it was set correctly (it will be empty if not) and sets a timeout to call itself after a short pause if it didn’t work. I have put a limiting factor of 5 loops on to make sure it doesn’t run forever, and I do check whether the value you’re trying to give to the content is empty as well. The complete code for the function is as follows then:
function setInnerHTML( element, html, count ) {
element.innerHTML = html;
if( ! count )
count = 1;
if( html != '' && element.innerHTML == '' && count < 5 ) {
++count;
setTimeout( function() {
setInnerHTML( element, html, count );
}, 50 );
}
}
Technorati tags: javascript, safari, bug, browser
Thank you for this. I was pulling my hair out working remotely from Argentina to a site that is published in Canada and no MAC to test here. You made me look good.
That’s interesting. I’d add a 4th parameter to allow a callback function. Someone might be want to do something, only after the content is surely loaded.
Hello,
Thank you very much for detailed description of this problem. It saved a lot of time for us.
We had almost the same problem and successfully fixed it.
The most important thing was that the problem appeared only after modification of window.location.
Best regards,
Siarhei Barysiuk
interesting… I’ve been suffering from the same issue in safari. I have a couple of place on my page where the results of an ajax call are inserted in the page, the first one always fails, the second is fine.
Your solution works for me(and is also mentioned here http://www.webdeveloper.com/forum/showthread.php?t=150552) but..eh…it’s a ‘bit nasty’ as you say. Does anyone know the root cause of this issue? Why does safari behaves this way?
Now that google chrome is running with Webkit, it will only become more important to get things working 100% in this engine…
Thank you for this!!!! I am working on an iPhone application and found that this came up intermittently.
This worked like a charm. We were having problems with a navigation pane not appearing on an IPAD. Great tip!!!
Thanks for detailed explanation and the code.
Your solution really helps us with our iPhone web application.
Great!! Thanks!!!
It really works.
We solved this by overriding the PhoneGap.run_command function.
http://stackoverflow.com/questions/3644474/calls-to-update-dom-are-ignored-in-phonegap-app-with-iphone-3g-running-ios-3-0/5273001#5273001
I had trouble with the jquery .html(‘foo’) function not working consistently while the .text(‘foo’) worked in the iphone safari. Perhaps your finding explains this.
I can definitely verify it still happens in iOS 4.3.1 on a 3GS, as well as on several Android devices running 2.1/2.2/2.3 as well as the emulator for all of those versions.
BTW, has anyone ever filed a bug with WebKit? I’ve been digging in their Bugzilla database and code and found nothing concrete so far. If nobody else has I am going to file a bug with them.
Someone did file a bug, but with Apple directly: rdar://8893869
Apple forum posting: https://devforums.apple.com/message/366602
[...] PhoneGap. It looks like it is fixed in the Webkit browser on the same device. This article from John mc Kerrell in 2007 points out its a really old bug. He suggests adding a timer and try to set the innerHTML a [...]
I’ve found a pretty nice solution to this problem, by automatically translating the innerHTML operation into DOM manipulations.
Details here – http://martinkou.blogspot.com/2011/05/alternative-workaround-for-mobile.html
Pretty sure an alternative to the “nasty” solution above could be just to use the below code:
node.appendChild(document.createTextNode(“text”));