Replacing DOM Nodes with new nodes. - javascript

I have a collection of DOM nodes I would like to replace with new DOM nodes.
Due to the way I pass the information into the function I only have a variable named collection which is an object full of HtmlTableRowElements and I would like to replace it with a new variable called Rows.
Here is what I have
var parent = collection[0].parentNode;
parent.innerHTML = "";
parent.appendChild(rows);
however this gives me a long strange error.
uncaught exception: [Exception... "Could not convert JavaScript argument arg 0 [nsIDOMHTMLTableSectionElement.appendChild]" nsresult: "0x80570009 (NS_ERROR_XPC_BAD_CONVERT_JS)" location: "JS frame :: http://127.0.0.1/test.php :: :: line 103" data: no]
So I have also tried
collection[0].parentNode.innerHTML = "";
collection[0].parentNode.appendChild(rows);
which returns
collection[0].parentNode is null
I do understand why the second option is returning an error. I would imagine the first option is returning an error because I have removed the element the variable is referencing.
I'm beginning to think that looking for the parent and replacing it's contents is not the way to go about this.
any help?

What is in rows? I can't tell from your question
If it’s an array, put the contents into a document fragment and append that:
var fragment = document.createDocumentFragment();
for (var i = 0, l = rows.length; i < l, i++) {
fragment.appendChild(rows[i]);
}
parent.appendChild(fragment);
Putting a bunch of elements in a document fragment is faster than appending them to something in the document one by one, and is a good way to carry around a collection of elements (instead of an array).
If it’s a string, use innerHTML:
parent.innerHTML = rows;
When you call collection[0].parentNode.innerHTML = "", collection gets removed from the document. It still exists only because you’re holding onto it in a JavaScript variable, but no longer has a parent node. You should still be able to grab parent in advance (like you do in the first example), and append things to it:
var parent = collection[0].parentNode;
parent.innerHTML = "";
parent.appendChild(rows);

parent.innerHTML = ""; is one way to clear the content. You then have to iterate over the other elements and append them:
for(var i = 0, l = rows.length; i < l; i++) {
parent.appendChild(row[i]);
}
But afaik it might be problematic to append new rows this way. It might be better to use table.insertRow [MDN].

Related

Why can't I make "onclick" event handler in my javascript code? [duplicate]

I am using getElementById when I need to clone the div element.
Code:
printHTML( document.getElementById("div_name").cloneNode(true));
Now I need to use getElementsByClassName
CloneNode is not working when using getElementsByClassName.
How can I put class name in here?
Thank's
EDIT:
When I try to use this:
printHTML( $('.dataTables_scroll').clone(true) );
You can see my function:
function printHTML(clonedDive){
var iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.contentWindow.onunload = function(){
$(".DTTT_container").show("fast");
$("#header_outer").show("fast");
$(".ColVis.TableTools").show("fast");
$("#footer").show("fast");
};
iframe.contentWindow.document.body.appendChild(clonedDive);
iframe.contentWindow.print();
document.body.removeChild(iframe);
}
I am getting an error in this line:
iframe.contentWindow.document.body.appendChild(clonedDive);
This is an error description:
Uncaught Error: NOT_FOUND_ERR: DOM Exception 8
getElementsByClassName gets a nodelist, or an array-like object containing elements if you will, as there can be more than one element with the same class.
getElementsByClassName does this even if only one element matches the class.
You can generally recognize methods like that be the s in getElements, which means it gets multiple elements, i.e. a nodeList.
getElementById only gets one element as ID's are unique.
To get the first element in the nodelist, use bracket notation, like so:
document.getElementsByClassName("div_name")[0].cloneNode(true);
or one could use querySelector, which gets the first matching element only
document.querySelector(".div_name").cloneNode(true);
The jQuery solution would be:
$('.div_name').clone(true);
and to iterate over elements with a certain classname you'd use a loop
var elems = document.getElementsByClassName("div_name");
for ( var i=0; i<elems.length; i++ ) {
printHTML( elems[i].cloneNode(true) );
}
Due to getElementsByClassName returns an object's array, so you have to use for loop to iterates among them, as follows:
for (i = 0; i < document.getElementsByClassName("div_name").length; i++){
printHTML( document.getElementsByClassName("div_name")[i].cloneNode(true));
}
otherwise, if you know the index of the element you have let we say 1
printHTML( document.getElementsByClassName("div_name")[1].cloneNode(true));
This does not work? :
printHTML( document.getElementsByClassName("class_name")[0].cloneNode(true));
You can loop through the elements and clone one by one...
var e = document.getElementsByClassName('div');
for (var i = 0; i < e.length; i += 1) {
// Clone e[i] here
console.log(e[i].cloneNode(true));
}

Can't set styles to elements with a specified class javascript

This is my javascript code:
document.getElementsByClassName('loader').style.opacity = "0";
this code will give an error in my console displayed below:
TypeError: 'undefined' is not an object (evaluating 'document.getElementsByClassName('loader').style.opacity = "0"')
I have already tried these, but these also do not work:
document.document.querySelectorAll('.loader').style.opacity = '0';
document.document.querySelector('.loader').style.opacity = '0';
My html code:
<div class="loader">Some Text</div>
The .getElementsByClassName() function returns a list of elements. You have to iterate over that list with your own code.
var loaders = document.getElementsByClassName('loader');
for (var i = 0; i < loaders.length; ++i)
loaders[i].style.opacity = '0';
If you just want to operate on particular members of the list, you can (if you're careful) treat it like an array. I mention being careful because .getElementsByClassName() returns a "live" NodeList. If you remove the class "loader" from one or more of the elements, the list will change. (That's not the case for the lists returned from .querySelectorAll().)
If you select elements by classes you most will most likely end up with an array of elements. You need to set the parameters for each one. Pure JS doesn't behave as jQuery regarding this.
First make your selection by
var elements = document.getElementsByClassName('loader');
then cycle through them
for(var i=0; i<elements.length; i++){
elements[i].style.opacity = "0";
}
See if this helps..

JavaScript - Manipulation HTMLElements inside JS object

In my current project, I've encountered a somewhat strange behavior (from my point of view) when I'm trying to alter the properties af a html element.
In my code, a have defined a javascript object - This object has a 'this.element' property, containing a html element, that gets passed through the constructor. Within this object, I have a couple of functions. In one of theese functions I'm trying to alter some styling of that object, by doing:
this.element.style.visibility = "hidden";
I get no errors when trying to do this, but the style remains unchanged. After some time, I discovered a workaround:
document.getElementById(this.element.id).style.visibility = "hidden";
Which is basically the same. This works, and I can set the style of my element. Though this workaround works, it requires my elements to have ID. While this is not a problem, my coding could get a whole lot easier, if I could get around this.
I'm testing this with Chrome, which is the same browser we'll use once the project is ready for deployment, so using a different browser is not really an option for me.
I would greatly appreciate if anybody can help me understand/solve this situation :)
- Thanks in advance
EDIT: Some more code.
This example I threw together illustrates what I'm doing. However when I run this on it's own, I can't achieve the behavior I was describing.
I don't know if this is of importance, but in my case the function representing "changeAllStyles" works fine when getting called just after the constructor. All subsequence calls of this function, is due to an invocation of the "onMessage" event, coming from websockets.
var myObjArray = [];
function init(){
//Using jQuery to select all elements containing the "data-whatever" attribute:
var elements = $('*[data-whatever]');
//Create a myObj object for each of theese elements:
for (var i = 0; i < elements.length; i++) {
var params = elements[i].getAttribute("data-whatever");
myObjArray.push(new myObj(elements[i], params));
myObjArray[i].changeStyle();
}
}
function myObj(element, params){
this.element = element;
this.params = params;
this.changeStyle = function(){
this.element.style.visibility = "hidden";
};
}
function changeAllStyles(){
for (var i = 0; i < myObjArray.length; i++) {
myObjArray[i].changeStyle();
}
}
It sounds as though elsewhere in the code you're removing the DOM element after having initialized this.element and then recreating it, like this:
HTML:
<div id='bar'><span id='foo'>This is foo</span> inside 'bar'</div>
JavaScript:
var foo = document.getElementById('foo');
var bar = document.getElementById('bar');
bar.innerHTML = "<span id='foo'>This is a new foo</span> inside 'bar'";
foo.style.visibility = "hidden"; // <== No effect, wrong element
As you can see, we're getting the "foo" element, and getting the "bar" element, and then replacing the contents of "bar" with brand-new stuff. That means all of the elements inside "bar" are removed, and then new elements are created. It happens that one of the new elements has the ID "foo" and so
document.getElementById(foo.id).style.visibility = "hidden";
...would work even though foo.style.visibility = "hidden"; doesn't work, because it's a completely different element that happens to have the same ID.
There is nothing wrong with the code you show, except its complexity maybe.
My gut feeling is that unholy things are occuring behind the scene. I cannot debug the code I can't see, but I can propose a cheap workaround.
You could as well collect the elements with document.querySelectorAll('[data-whatever]'), get back your individual parameters with getAttribute() and fiddle with style directly, like so :
function changeAllStyles()
{
var elements = document.querySelectorAll ('[data-whatever]');
for (var i = 0; i < elements.length; i++)
{
var elt = elements[i];
my_parameters = elt.getAttribute ('data-whatever');
play_with (my_parameters);
elt.style.color = a_nice_shade_of_pinkish_brown;
}
}
I concur with T.J. Crowder.
My bet is, someone is destroying and recreating the DOM elements behind your back. If they are recreated by copy, you can still access their clones by Id or class or attributes, but the references to the original DOM elements are dead.
To track this down, you could try something like:
this.changeStyle = function()
{
this.element.__check = "booh!";
var possible_clone = document.getElementById (this.element.id);
if (possible_clone.__check != "booh!")
{
report_to_boss ("something fishy there");
}
};

Use getElementById on newly created DOM object before it is inserted into the document -- HOW?

The getElementById method cannot be used unless the DOM element is attached to the document. By what method or mechanism can I access and modify a DOM element before it is attached to the document?
I have a large DOM element and I want to access a piece of it. I would classically use getElementById, but the DOM element is not attached to the document.
var element = document.createElement("div");
element.id = 'testqq';
var el = document.getElementById('testqq'); // el will be null!
source: https://developer.mozilla.org/en/dom/document.getelementbyid
I am looking for an answer that does not use jQuery. I love jQuery, but it is not an option for my current problem.
Well, you already have a reference to it with your element variable.
But I'll bet you mean you want something nested inside. If that's right, you could use getElementsByTagName('*'), then iterate over the collection that's returned, testing the ID property until yours is found.
var div = document.createElement('div');
div.innerHTML = '<p>yo</p><div><span id="tester">hi</span></div>';
var all = div.getElementsByTagName('*');
// search for element where ID is "tester"
for (var i = 0, len = all.length; i < len; i++) {
if (all[i].id === 'tester') {
var result = all[i];
break;
}
}
alert( result.id );
Before you try the loop, you can test to see if querySelectorAll is supported in the browser, and use that if so.
if( div.querySelectorAll ) {
var result = div.querySelectorAll('#tester');
} else {
// Do the loop with getElementsByTagName()
}
The document.createElement function returns a new element object, but that element doesn't exist in the DOM until you insert it.
The document object has no recollection of elements you asked it to create, so when you call document.getElementById('testqq'), it has absolutely no idea what you're looking for.
In your example, you have a reference to the element already. Why do you need to ask the document for it?

Determine the length of this Node-set?

Javascript for Mozilla:
while (result)
{
alert(result.childNodes[0].nodeValue);
//txt=txt+result.childNodes[0].nodeValue+"<br/>";
result=nodes.iterateNext();
}
This is intended to return a series of strings from an xml file. When I use the alert line, it alerts as expected with the proper strings in a series. The "txt" variable on the commented line is supposed to go to an innerHTML further down in the function. But Firebug keeps telling me that the result.childNodes[0] is undefined. Why would it be defined in the alert but not the next line?
I hope that's enough code to determine the problem...if not I will post more.
Thanks for help
[edit]
this is the definition of result:
var x=xmlDoc.responseXML;
var nodes=x.evaluate(path, x, null, XPathResult.ANY_TYPE, null);
var result=nodes.iterateNext();
I am retrieving XML
[edit]
okay I put the iterator in a for loop like this:
for (var i=0; i<2; i++)
{
var res=(xmlNodes.childNodes[0].nodeValue);
txt=txt+res+"<br/>";
xmlNodes=xmlQuery.iterateNext();
}
because I had a theory that once the iteration was null the loop would fail. So that's what happened--it worked until I set the loop one instance higher than the amount of nodes available. now I am trying to figure out how to get the "length" of the node-set. How do I do that?
I don't see any problems in the code you have shown so far. If childNodes[0] is undefined, then it has to be a text node or an empty node, and you should see an exception when trying to access a property such as nodeValue of childNodes[0] which is undefined. The exception will show up on alert or concatenation, or any other type of access.
This is the answer to your updated question.
now I am trying to figure out how to get the "length" of the node-set. How do I do that?
You can have the following types of node-sets returned from the evaluate function:
Iterators
Snapshots
First Nodes
I'll skip "first nodes" as that doesn't apply in this situation.
Iterators
With iterators, you only get an iterateNext() method for traversing nodes sequentially. Iterators refer to live nodes, meaning if the nodes in the document were to change while you are traversing the resultset, the iterator will become invalid.
Here's an example with using an iterator to go over each resulting node:
var results = doc.evaluate("expr", doc, null, ORDERED_SNAPSHOT, null);
var node;
while(node = results.iterateNext()) {
console.log(node);
}
If you want to use iterators, and find the number of matching results, a wrapper function that iterates through all nodes and returns them in an array might be useful.
function evaluateXPath(document, expression) {
var ANY_TYPE = XPathResult.ANY_TYPE;
var nodes = document.evaluate(expression, document, null, ANY_TYPE, null);
var results = [], node;
while(node = nodes.iterateNext()) {
results.push(node);
}
return results;
}
Get nodes as an array and loop the array:
var results = evaluateXPath(doc, "expr");
for(var i = 0; i < results.length; i++) {
console.log(results[i]);
}
Snapshots
Snapshots provide a static result of the nodes at the time of querying. Changes to the document will not affect this snapshot. Useful interfaces here will be the snapshotLength property, and snapshotItem(index) function.
Here's an example using a snapshot result:
var ORDERED_SNAPSHOT = XPathResult.ORDERED_NODE_SNAPSHOT_TYPE;
var results = doc.evaluate("expr", doc, null, ORDERED_SNAPSHOT, null);
for(var i = 0; i < results.snapshotLength; i++) {
var node = results.snapshotItem(i);
console.log(node);
}
See a working example.
It seems you are developing this for Firefox. Did you consider using E4X for this purpose? It provides a really easy interface for dealing with XML documents - for creating, manipulating, and querying.
now I am trying to figure out how to
get the "length" of the node-set. How
do I do that?
In XPath this is achieved using the standard XPath count() function.
count(someExpression)
evaluates to the number of nodes selected by someExpression.

Categories