I'm currently learning JavaScript and as far as I know have not learned anything that could explain the following:
<div id="parent">
<div>One</div>
<div>Two</div>
</div>
<script>
function test(node) {
var divs = node.children;
console.log(divs);
var div = document.createElement("div");
node.appendChild(div);
console.log(divs);
}
test(document.querySelector("#parent"));
</script>
I want the variable divs to be an object containing the children divs of node that exist when that line of code is run. Which it is, however it seems to update when a child is added to the parent node. What explains this behaviour; am I creating a reference to the element, and if so how do I achieve what I want to?
In JavaScript, every object is passed by a reference instead of value. The .children property of a node is an object (HTMLCollection object to be specific). What that means is that 'divs' variable will not contain the children at the time it is run, it will refer to the children property. When children change and new nodes are added, accessing the divs variable will return the current state of children, so it will contain the new child as well. If you just want to just copy the reference to initial children, you can create another variable.
function test(node) {
var children = node.children;
var divs = [];
// copy every child to the divs array
for (var i = 0; i < children.length; i++) {
divs.push(children[i]);
}
var div = document.createElement("div");
node.appendChild(div);
// since divs is just an array copy of the initial children,
// when children change, the divs still only contains the initial nodes
console.log(divs);
// however, as we said before, children will still be a reference to
// current node's children, so
console.log(children); // outputs 3 divs
}
test(document.querySelector("#parent"));
divs holds a reference to the children property of the selected node. For this reason, if you change this children property, the content of divs also changes.
If you want to keep divs stable, create a shallow copy of the array-like object children:
function test(node) {
var divs = Array.prototype.slice.call(node.children);
console.log(divs);
var div = document.createElement("div");
node.appendChild(div);
console.log(divs);
}
You could create a new array
var divs = new Array(node.children.length);
for(var i = 0; i < node.children.length; i++){
divs[i] = node.children[i]
}
console.log(divs);
var div = document.createElement("div");
node.appendChild(div);
console.log(divs);
You can use querySelectorAll, it returns a static list not live. For more details you can check this link.
https://www.w3.org/TR/selectors-api/#queryselectorall
<div id="parent">
<div>One</div>
<div>Two</div>
</div>
<script>
function test(node) {
var divs = document.querySelectorAll('#'+node.id +' div');
console.log(divs);
var div = document.createElement("div");
node.appendChild(div);
console.log(divs);
}
test(document.querySelector("#parent",'#parent div'));
</script>
Related
I'm trying to put a content div into each parent div, but this way it puts all the content divs to the first parent div, not each parent div. I've tried to do it in 2 for loops, tried forEach, but just can't figure it out.
for (let i = 0; i < 5; i++) {
const parent = document.createElement("div");
parent.classList.add("parent");
parent.setAttribute("id","parent");
document.getElementById("container").appendChild(parent);
const content = document.createElement("div");
content.classList.add("content");
document.getElementById("parnet").appendChild(content);
}
Ty for your answer
You're using id's to select your parents with. But you can't have multiple elements with the same id value. getElementById will also look for the first occurence of the id, so you will always get the first parent element.
Besides that, you already have a reference to the parent in your parent variable. No need to look it up again, just use the reference you already have.
const container = document.getElementById("container");
for (let i = 0; i < 5; i++) {
const content = document.createElement("div");
content.classList.add("content");
const parent = document.createElement("div");
parent.classList.add("parent");
parent.append(content);
container.append(parent);
}
Trying to create DOM element "gota" from template. First I create template:
function htmlToElement(html) {
var template = document.createElement('template');
template.innerHTML = html;
return template.content.firstChild;
}
let gota = htmlToElement('<div class="gota"><div class="gota-rastro"><div class="rastro"></div></div><div class="gota-cabeza"></div></div>');
Second, I create collection from CSS class "gotea" and iterate for each element to append template:
function gotear() {
let gotas = document.getElementsByClassName('gotea');
for (let i = 0; i < gotas.length; i++) {
gotas[i].appendChild(gota);
}
}
gotear();
This just add "gota" element to a only one random element of the collection:
How can I add this template to ALL elements in a collection?
You're only creating one element. Then you're using that same element with appendChild multiple times, so you move it from one parent to the next.
You can clone the element with cloneNode(true) and append the clone:
gotas[i].appendChild(gota.cloneNode(true));
Side note: You can use insertAdjacentHTML rather than htmlToElement to insert elements based on that HTML directly:
function gotear() {
let gotas = document.getElementsByClassName('gotea');
for (let i = 0; i < gotas.length; i++) {
gotas[i].insertAdjacentHTML(
"beforeend",
'<div class="gota"><div class="gota-rastro"><div class="rastro"></div></div><div class="gota-cabeza"></div></div>'
);
}
}
gotear();
Granted, that means parsing the HTML repeatedly. But if not useful here, it might be useful elsewhere. (There's also insertAdjacentText.)
How can i expand kendotreeView nodes with no children?, i need help checking if parent has child node if there is one or more child i want to keep it collapsed otherwise expanded.
I used the following Jquery code and works fine but it find by text value. i rather check all node with child li and expand if no child exist.
var treeview;
treeview = $("#treeview").data("kendoTreeView");
var nodeToExpand = treeview.findByText('abc.');
treeview.expand(nodeToExpand);
is there anyways to replace the code above to collapse or expand based on that?
You can iterate over all nodes, and if any do not have children nodes, expand them like this:
var treeView = $("#treeview").data("kendoTreeView");
var nodes = treeView.dataSource.view();
for (var i = 0; i < nodes.length; i++) {
if (!nodes[i].hasChildren) {
treeView.expand(nodes[i]);
}
}
I'm attempting to use JavaScript to add rows to a table. I created an anonymous function iterate that iterates id names, and that works fine. The problem is somewhere in my class.
class CreateTable{
constructor(text) {
this.text = text
}
makeTableRow(){
let self = this;
let row = document.createElement('tr');
for (let i = 0; i < 1; i++) {
let el = document.createElement('td');
el.setAttribute('id', iterate(i));
row.appendChild(el);
}
let en = document.getElementById('id1');
console.log(en);
en.innerHTML = self.text;
return row;
}
}
I adapted this class from something that already works, with some tweaks. The en variable is returning a typeError:en is null, and I can't figure it out. What I want to do is create two empty td elements with callable ids, and then add text to the first element immediately. What is the actual problem that is going on here?
The document.getElementById() function returns null if there is no element with the specified ID in the DOM. The elements that you create in the loop have not been added to the DOM, they're appended to a tr that isn't in the DOM either (it's just returned from your function without having been appended to anything).
Just add the required text directly at the time you create the element:
makeTableRow(){
let self = this;
let row = document.createElement('tr');
for (let i = 0; i < 1; i++) {
let el = document.createElement('td');
el.setAttribute('id', iterate(i));
if (i === 0) // for first element
el.innerHTML = self.text; // set the content
row.appendChild(el);
}
return row;
}
Incidentally, you say you want to create two td elements, but your loop only runs for one iteration. The for condition should be i < 2 if you want it to run twice.
Is null because doesn't is identifying the "id", that meaning that you are writing your script before the id="id1".
You need to write your script at the end, just before of "body"
.......
<div id="id1">
</div>
.....
<script>
....
</script>
</body>
I have cloned div duplicate elements in body with the class name "combo". I need to remove all duplicates except the original div element
The problem is that cloned objects will have the same attributes as the original, so it's rather hard to distinguish them, however, you could try this:
(function()
{
//or indeed: querySelector('.combo') which returns a single DOM ref
var original = document.querySelectorAll('.combo')[0];//reference to the original
//clone and add
function removeClones()
{
var i,all = document.querySelectorAll('.combo');
for(i=0;i<all.length;i++)
{
if (all[i] !== original)
{//this is a clone
all[i].parentNode.removeChild(all[i]);
}
}
}
}());
That should do it. An alternative method would be to add a class to the clones, prior to appending them to the DOM:
var clone = original.cloneNode(true);
clone.className += ' combo-clone';
//then, to remove:
var clones = document.querySelectorAll('combo-clone');//selects all clones
var fn = function(originalEl){
var els = document.querySelectorAll('.combo');
for(var i=0; i<els.length; i++){
if( els[i] !== originalEl ){
els[i].parentNode.removeChild(els[i]);
}
}
}
Keep a reference to the cloned elements somewhere (such as an array). Loop over that array and call foo.parentNode.removeChild(foo) on each value.