Why does jQuery fail to hide certain HTML? - javascript

I've been running my head into a wall trying to figure this out. Take the following HTML body:
<body>
<div id="project">
<h1>Hi</h1>
<h2>Hello</h2>
</div>
</body>
And the following jQuery code:
$(function(){
var h = $('#project').html();
$('#project').remove();
$(h).hide().appendTo('body');
alert("Created HTML, hide, and appended!");
});
The $(h).hide() portion causes jQuery to throw an exception in Safari 4 and Firefox 3.5.
Safari: TypeError: Result of expression 'this[a].style' [undefined] is not an object.
Firefox: uncaught exception: [Exception... "Could not convert JavaScript argument arg 0" nsresult: ...]
When I change the HTML to contain just one of the two headings (if you remove the <h1> or <h2> from the HTML, the script runs successfully. Why is this?
To try for yourself, see http://jsbin.com/avisi/edit
Edit: I'm not actually trying to remove and element from the DOM and re-insert it by copying the HTML. This is just a test case for an error I'm having in more complex code, and I'm trying to understand why this error occurs. I agree that, if I wanted to accomplish just what is shown here, I would use something like $('#project').remove().children().appendTo('body')

I cannot duplicate your error in Firefox. However, you might want to try cleaning it up with the following:
$('#project').remove().children().appendTo('body').hide();
Broken down, this is what's happening
// Get the `project` container
$('#project')
// Remove it from the page
.remove()
// Get its children (the h1, h2, etc)
.children()
// Append those nodes to the body
.appendTo('body')
// Hide those nodes
.hide();
Others are proposing that .hide() is causing problems since the node that it is being applied to is not part of the main document; however, this is just not the case. As long as you maintain a reference to any node, you can affect its style property (via hide, show, etc).
One things you might want to check is to make sure that $('#project') is actually returning the (if any) expected node. Problems may arise otherwise.
So I poked around in Safari and found your problem. Here's a dump from the developer console.
> var h = $('#project').html();
undefined
> var t = $(h);
undefined
So far, so good. undefined here simply means that the statement (the var statement) has no return value (which it doesn't)
> t.hide()
ajax.googleapis.com/ajax/libs/jquery/1.4.0/jquery.min.js:131TypeError: Result of expression 'this[a].style' [undefined] is not an object.
Here's the error that you described. Inspecting each item in jQuery object will reveal the error below
> t[0]
<h1 style=​"display:​ none;​ ">​Hi​</h1>
Good...
> t[1]
(whitespace)
Dammit. Really? Here's the problem. whitespace nodes have no style attribute, which is what's causing the problem.
> t[2]
<h2>​Hello​</h2>
This is why copying the HTML of one node to another just to move those nodes is a bad technique. I suggest you use the snippet that I provided above.

There's a text node being selected in the $(h). We can filter that out using the filter function though.
This should work (I've only tested in FF though):
$(function(){
var h = $('#project').html();
$('#project').remove();
$(h).filter("*").hide().appendTo('body');
alert("Created HTML, hide, and appended!");
});
Pretty wierd behaviour IMO.

You removed the contents from the DOM before, so there is nothing to hide. If you would do
$(h).appendTo('body').hide();
it should work

Related

IE10 inserting blank text DOM entries

I have a piece of Javascript code that locates the single table in the DOM then tries to manipulate its first child, the thead (actually, it iterates though the children of that child, the tr entries but that's not important to the question). The code to do this is:
var tableNode = document.getElementById("table").firstChild;
This works fine in Firefox ESR (10/17/24) and IE9 but fails in IE10, and the reason appears to be because IE10 is inserting weird DOM entries and it's one of those I'm picking up with firstChild instead of the desired thead. I base this on the DOM dump below along with the fact that tableNode.data is set to the string type.
The DOM in IE10 compatibility mode (where it also works) looks like this:
and you can see that the DOM indeed looks sensible. However, examining the DOM in normal IE10 mode shows this:
For what it's worth, Chrome gives me:
and FF17esr gives me:
neither of which seem to have the empty text elements.
Now, I can run the site in compatibility mode but that's an annoying thing to have to tell all our customers. I can also apparently add the hideous:
<meta http-equiv="X-UA-Compatible" content="IE=9">
to my output but I'm not entirely certain what other effects that may have.
I'd like to understand first why IE10 is adding these nodes whereas IE9/FF/IE10compat aren't. There are some discussions I've found stating that whitespace in the HTML may be causing it but it seems to me that this shouldn't result in random nodes being created and, in any case, I don't think I have any superfluous white space. Although I should mention that the value of tableNode.data mentioned above as type string is actually \n, meaning that the newline at the end of the line may be creating this DOM entry.
But, to be honest, that seems ludicrous. HTML is supposed to ignore whitespace outside of tags as far as I'm aware, or at least fold it into a single element. I find it hard to believe that something like:
<tag>line 1</tag>
<tag>line 2</tag>
would result in three DOM entries, tag, empty node and tag just because there's a newline between them.
Any ideas on how to best solve this? Am I going to have to modify my Javascript code to skip these DOM entries?
You can never know where a browser might insert text nodes so you have to make sure you're getting the first child "element"in case the browser put a text node there.
Here's a simple function that will do that:
getFirstChildElement(parent) {
var node = parent.firstChild;
// advance until we get to an element node (skipping text and comment nodes)
while (node && node.nodeType !== 1) {
node = node.nextSibling;
}
return node;
}
Or, if you just want to get the <thead> element, you can simply use this:
table.getElementsByTagName("thead")[0]
Are you absolutely sure Firefox doesn't show those empty text nodes? I'm asking because it should, if it doesn't then it's a bug in Firefox.
Previously only IE behaved the way you expected. All other browsers including Firefox, Chrome, Safari and Opera followed W3C DOM standards which requires them to retain those whitespace. IE10 now join the ranks of other web browsers and behave in a standards compliant way.
You'd be right to complain that this doesn't make sense but it's what the standards require.
As such, the correct way to get the element is to check it's tagName:
var table = document.getElementById("table");
var child = table.firstChild;
while (child && child.tagName != 'thread') {
child = child.nextSibling;
}
// remember to check child after this point because it may be undefined
Additional explanation
Firebug and Chrome's DOM explorer hides those text elements as a matter of convenience, but it's still there. You can try this out:
<html>
<body>
<div id="foo">
<div id="bar">
</div>
</div>
<script>
var f = document.getElementById('foo');
document.body.innerHTML += f.firstChild.id + <br>;
document.body.innerHTML += f.firstChild.nextSibling.id + <br>;
</script>
</body>
</html>
In all browsers except older versions of IE the above page would output:
undefined
bar
That's because the firstChild is the empty text node. You can console.log it if you like to check out that firstChild.

DOCTYPE breaks style.display

I have a (legacy) JS function, that shows or hides child nodes of argument element. It is used in mouseover and mouseout event handlers to show-hide img tags.
The function looks like this:
function displayElem(elem, value, handlerRoot){
try{
var display = 'inline';
if(!value)
display = 'none';
if(handlerRoot)
elem.style.display = display;
var childs = elem.childNodes;
for (i = 0; i < childs.length; i++){
if(childs[i].nodeType == Node.ELEMENT_NODE){
childs[i].style.display = display;
alert("Node "+childs[i].tagName+" style set to " +childs[i].style.display);
}
}
}catch(e){
alert('displayElem: ' + e);
}
}
Here, value and handlerRoot are boolean flags.
This function works perfectly, if target html page has no doctype. Adding any doctype (strict or transitional) breaks this. Alert shows style has been set to the right value, but child elements are not displayed.
Would be good, if this function could work with any DOCTYPE.
Image (a child node of elem) is initialized like this (perhaps something is wrong here?):
var img = new Image();
img.style.cssText =
'background: transparent url("chrome://{appname}/content/dbutton.png") right top no-repeat;' +
'position: relative;' +
'height:18px;'+
'width:18px;'+
'display:none;';
JavaScript doesn't really work over plain HTML but on the DOM tree generated by the browser. Thus the DOCTYPE does not have a direct influence on JavaScript but on the way the browser handles invalid HTML and CSS.
I think the first step is to clean-up the HTML and make sure it's valid, esp. that tags are used in allowed places and properly nested. That will guarantee that the generated node tree is the same no matter the rendering mode.
You can also use your favourite browser tool (such as Firebug) the inspect the real tree and make sure nodes are placed where you think they are.
Update:
I wonder if when dealing with a document in standards mode (the document has a DOCTYPE), Firefox is inserting an implied element that it doesn't insert in backward-compat mode (no DOCTYPE), and so the image isn't an immediate child of elem but instead a child of this implied element that's then a child of elem; so you won't see the image in elem.childNodes. Walking through the code in a debugger is the best way to tell, but failing that, alert the tagName of each of the child nodes you're iterating through in the loop.
For example, with this markup:
<table id='theTable'>
<tr><td>Hi there</td></tr>
</table>
...Firefox will insert a tbody element, so the DOM looks like this:
<table id='theTable'>
<tbody>
<tr><td>Hi there</td></tr>
</tbody>
</table>
...but it won't be that specific example unless the DOCTYPE is a red herring, because I just tested and Firefox does that even in backward-compat mode. But perhaps you were testing two slightly different documents? Or perhaps it does it with some elements only in standards mode.
Original:
Not immediately seeing the problem, but I do see two issues:
i isn't declared in the function, and so you're falling prey to the Horror of Implicit Globals. Since your alert is showing the correct value, I can't see why that would be the problem.
url(..) in CSS doesn't use quotes. Yes they can, optionally.
Thanks to Álvaro G. Vicario. Though he didn't gave an exact answer, the direction was right.
I've checked the page with w3c validator, and found that my Image objects were missing src attribute. Thus, adding img.src = "chrome://{appname}/content/dbutton.png"; helped.
Still, I'm not sure, why the original code author used background style instead of src... Perhaps, that would remain a mystery. :)

Javascript getElementById - reading works, altering doesn't

So, I have this pretty complex ajax thing going.
It loads new html (including div tags and all) to show up on the page.
I included a 'more' link to load additional data.
This more link links to my javascript function. The 'more' link is located in a div, which I gave a unique id. The next time the load function is called, I use document.getElementById(the id).style.display="none"; to "remove" this div from the look of the page.
I set error traps for this, the div with that id is found without problems, but javascript fails to change my style property.
I tested alert(document.getElementById(the id).innerHTML); and that worked without problems - hence the title of the question.
So, does anyone have any ideas/do I need to offer more information? The main problem is that it doesn't throw any errors anywhere, yet it fails to complete the task I asked...
Here's a bit of code to go with it -
try
{
var myidthing = "morelink" + ContentStart.toString(); //the id is correct
var div = document.getElementById(myidthing);
if (!div)
{
}
else
{
div.style.display="none"; //this doesn't work, but doesn't raise an error
alert(div.innerHTML); //this works without problem
}
}
catch(theerr)
{
alert(theerr);
}
------------------------->EDIT<-------------------------
I'm incredibly sorry if I upset any people.
I'm also angry at myself, for it was a stupid thing in my code. Basically, I had a variable that stored the contents of a parent div. Then I (succesfully) removed the div using the removeChild() method. Then my code pasted the contents of that vaiable (including the div I wanted gone) back into the parent div.
I switched around the order and it works fine now.
Again, excuse me for this.
Throwing out a few ideas of things to look for:
You said the div is generated by javascript. Is it possible the div you are targeting is not the one you think you are? It could be you are targeting another div, which is already hidden, or obstructed... or maybe the innerHTML you are displaying goes with a different element than the one you intend to target. Put an alert or script breakpoint in the if(!div) case, also, and see if it's going down that path.
If the above code is only a stripped-down version of your actual code, check your actual code for typos (for example: style.display = "none;";)
Using the FireBug plugin for FireFox, inspect the target element after the operation completes, and make sure that the display: none appears in the style information. If not, use FireBug's debugger to walk through your javascript, and see if you can figure out why.
Use FireBug to break on all script errors, in case there is another error causing this behavior.
Try empty quotes instead of 'none' and see if that works?:
document.getElementById('element_id').style.display="";
Failing that, don't change the style, just add a class which hides the element.

What exactly can cause an "HIERARCHY_REQUEST_ERR: DOM Exception 3"-Error?

How exactly does it relate to jQuery? I know the library uses native javascript functions internally, but what exactly is it trying to do whenever such a problem appears?
It means you've tried to insert a DOM node into a place in the DOM tree where it cannot go. The most common place I see this is on Safari which doesn't allow the following:
document.appendChild(document.createElement('div'));
Generally, this is just a mistake where this was actually intended:
document.body.appendChild(document.createElement('div'));
Other causes seen in the wild (summarized from comments):
You are attempting to append a node to itself
You are attempting to append null to a node
You are attempting to append a node to a text node.
Your HTML is invalid (e.g. failing to close your target node)
The browser thinks the HTML you are attempting to append is XML (fix by adding <!doctype html> to your injected HTML, or specifying the content type when fetching via XHR)
If you are getting this error due to a jquery ajax call $.ajax
Then you may need to specify what the dataType is coming back from the server. I have fixed the response a lot using this simple property.
$.ajax({
url: "URL_HERE",
dataType: "html",
success: function(response) {
$('#ELEMENT').html(response);
}
});
Specifically with jQuery you can run into this issue if forget the carets around the html tag when creating elements:
$("#target").append($("div").text("Test"));
Will raise this error because what you meant was
$("#target").append($("<div>").text("Test"));
This error can occur when you try to insert a node into the DOM which is invalid HTML, which can be something as subtle as an incorrect attribute, for example:
// <input> can have a 'type' attribute
var $input = $('<input/>').attr('type', 'text');
$holder.append($input); // OK
// <div> CANNOT have a 'type' attribute
var $div = $('<div></div>').attr('type', 'text');
$holder.append($div); // Error: HIERARCHY_REQUEST_ERR: DOM Exception 3
#Kelly Norton is right in his answer that The browser thinks the HTML you are attempting to append is XML and suggests specifying the content type when fetching via XHR.
It's true however you sometimes use third party libraries that you are not going to modify. It's JQuery UI in my case. Then you should provide the right Content-Type in the response instead of overriding the response type on JavaScript side. Set your Content-Type to text/html and you are fine.
In my case, it was as easy as renaming the file.xhtml to file.html - application server had some extension to MIME types mappings out of the box. When content is dynamic, you can set the content type of response somehow (e.g. res.setContentType("text/html") in Servlet API).
You can see these questions
Getting HIERARCHY_REQUEST_ERR when using Javascript to recursively generate a nested list
or
jQuery UI Dialog with ASP.NET button postback
The conclusion is
when you try to use function append, you should use new variable, like this example
jQuery(function() {
var dlg = jQuery("#dialog").dialog({
draggable: true,
resizable: true,
show: 'Transfer',
hide: 'Transfer',
width: 320,
autoOpen: false,
minHeight: 10,
minwidth: 10
});
dlg.parent().appendTo(jQuery("form:first"));
});
In the example above, uses the var "dlg" to run the function appendTo.
Then error “HIERARCHY_REQUEST_ERR" will not come out again.
I encountered this error when using the Google Chrome extension Sidewiki. Disabling it resolved the issue for me.
I'm going to add one more specific answer here because it was a 2 hour search for the answer...
I was trying to inject a tag into a document. The html was like this:
<map id='imageMap' name='imageMap'>
<area shape='circle' coords='55,28,5' href='#' title='1687.01 - 0 percentile' />
</map>
If you notice, the tag is closed in the preceding example (<area/>). This was not accepted in Chrome browsers. w3schools seems to think it should be closed, and I could not find the official spec on this tag, but it sure doesn't work in Chrome. Firefox will not accept it with <area/> or <area></area> or <area>. Chrome must have <area>. IE accepts anything.
Anyway, this error can be because your HTML is not correct.
I know this thread is old, but I've encountered another cause of the problem which others might find helpful. I was getting the error with Google Analytics trying to append itself to an HTML comment. The offending code:
document.documentElement.firstChild.appendChild(ga);
This was causing the error because my first element was an HTML comment (namely a Dreamweaver template code).
<!-- #BeginTemplate "/Templates/default.dwt.php" -->
I modified the offending code to something admittedly not bulletproof, but better:
document.documentElement.firstChild.nodeType===1 ? document.documentElement.firstChild.appendChild(ga) : document.documentElement.lastChild.appendChild(ga);
If you run into this problem while trying to append a node into another window in Internet Explorer, try using the HTML inside the node instead of the node itself.
myElement.appendChild(myNode.html());
IE doesn't support appending nodes to another window.
This ERROR happened to me in IE9 when I tried to appendChild an dynamically to a which already existed in a window A. Window A would create a child window B. In window B after some user action a function would run and do an appendChild on the form element in window A using window.opener.document.getElementById('formElement').appendChild(input);
This would throw an error. Same with creating the input element using document.createElement('input'); in the child window, passing it as a parameter to the window.opener window A, and there do the append. Only if I created the input element in the same window where I was going to append it, it would succeed without errors.
Thus my conclusion (please verify): no element can be dynamically created (using document.createElement) in one window, and then appended (using .appendChild) to an element in another window (without taking maybe a particular extra step I missed to ensure it is not considered XML or something). This fails in IE9 and throws the error, in FF this works fine though.
PS. I don't use jQuery.
Another reason this can come up is that you are appending before the element is ready e.g.
<body>
<script>
document.body.appendChild(foo);
</script>
</body>
</html>
In this case, you'll need to move the script after the . Not entirely sure if that's kosher, but moving the script after the body doesn't seem to help :/
Instead of moving the script, you can also do the appending in an event handler.
I got that error because I forgot to clone my element.
// creates an error
clone = $("#thing");
clone.appendTo("#somediv");
// does not
clone = $("#thing").clone();
clone.appendTo("#somediv");
Just for reference.
IE will block appending any element created in a different window context from the window context that the element is being appending to.
e.g
var childWindow = window.open('somepage.html');
//will throw the exception in IE
childWindow.document.body.appendChild(document.createElement('div'));
//will not throw exception in IE
childWindow.document.body.appendChild(childWindow.document.createElement('div'));
I haven't figured out how to create a dom element with jQuery using a different window context yet.
I get this error in IE9 if I had disabled script debugging (Internet Explorer) option. If I enable script debugging I don't see the error and the page works fine. This seems odd what is the DOM exception got to do with debugging either enabled or disabled.

Debugging IE8 Javascript Replace innerHTML Runtime Error

function DeleteData(ID)
{
var ctrlId=ID.id;
var divcontents=document.getElementById(ctrlId).innerHTML;
var tabid=ctrlId.replace(/div/,'tab');
var tabcontents=document.getElementById(tabid).innerHTML;
alert(document.getElementById(tabid).innerHTML);
document.getElementById(tabid).innerHTML="<TBody><tr><td></td></tr><tr><td></td></tr><tr><td></td></tr></TBody>";
document.getElementById(ctrlId).innerHTML='';
}
I am trying to replace the Table with empty table but
document.getElementById(tabid).innerHTML="<TBody><tr><td></td></tr><tr><td></td></tr><tr><td></td></tr></TBody>";
this line is causing Unknown Runtime Error
You can't set value to a table's innerHTML, you should access to child cells or rows and change them like that :
document.getElementById(tabid).rows[0].cells.innerHTML = 'blah blah';
For more info/example : Table, TableHeader, TableRow, TableData Objects
In IE8, you cannot change the innerHTML of an object when the code that attempts that is fired from within the object.
For example:
<span id='n1'>
<input type=button value='test' onclick='DoSomething(this);'>
</span>
with the following code tucked in some remote corner of your document:
<script type='text/javascript'>
function DoSomething(element)
{
document.getElementById("n1").innerHTML = "This is a test"
}
</script>
This will fail with the error unknown runtime error. I believe it has to do with the fact that you're trying to replace the HTML code which fired the current line of execution. The onclick event is part of the code being replaced by the DoSomething function. IE8 doesn't like that.
I resolved my issue by setting a timer event for 250 milliseconds, such that the function called at the end of the 250 milliseconds replaces the span's innerHTML.
I find out that in IE8 some elements are in readonly: COL, COLGROUP, FRAMESET, HEAD, HTML, STYLE, TABLE, TBODY, TFOOT, THEAD, TITLE, TR.
Therefore if you try to set innerHTML for these elements IE8 notify alter with Unknown Runtime Error.
More details here: http://msdn.microsoft.com/en-us/library/ms533897%28VS.85%29.aspx.
The simplest way is to convert read-only elements to div element.
I had the same issue but my error was because I was inserting a p tag directly underneath a p element as in:
document.getElementById('p-element').innerHTML = '<p>some description</p>';
I don't really see how this is HTML format error; seems more like another IE8 bug.
Great, I had the same situation with setting the innerHTML. When I read this I realised that one of the elements was a TR and one was a TD and the TD was working.
Changing the code so that they're both TDs fixes it, which means that it is a rather non-obvious problem caused by the structure of tables.
Presumably it throws the DOM awry when I start changing table rows since it can't store the table as an array any more.
I'm sure IE could give a more informative error message since it can't be that uncommon for the DOM to change on the fly and it seems strange that this would only throw an error for tables.
Ohh yes - remember that the HTML structure has to be valid.
I tried loading an entire page into a <p> tag - it failed (obviously), changing it to a <div> tag solved my problem!
So remember the HTML structure!
why it happend in IE...
case scenario:
Javascript unknown runtime error in IE while accessing Div inside Div to add ajax contents.
solution:
dont ever place nested divs in between the form tags to play with there contents .. :)
When using YUI try to use
Y.one('#'+data.id).setContent(html);
instead of:
Y.one('#'+data.id).set('innerHTML' , html);
If you need to set innerHTML to "" (empty string) then you can use removeChild instead
This is not a Big issue, since i faced same problem few days ago and the reason this error
occurs in ie is that - There exists an error in our html format or you are using an element other than <td> to replace innerHTML ie use only, dont use tables,divs to replace innerHTML.
SwapnilK

Categories

Resources