0

I have a heading element created using document.createElement:

let h1 = document.createElement("h1");
h1.innerHTML = "Heading"

I have an empty <div> in the body too:

<div></div>

When I use document.body.appendChild(h1), it does as expected:

<h1>Heading</h1>
<div></div>

But I want to append the h1 element to both the <div> and the body. The problem is that appendChild moves the element if it is already appended.

The thing keeping me from modifying innerHTML or cloning is that, lets say I want to change both the element's instances' innerHTML. So for that I need access to the element's DOM. And I may not know what is the exact position of the instance.

Does anyone know some other method to do this or a workaround to appendChild?

RealZombs
  • 135
  • 1
  • 12
  • Does this answer your question? [Append multiple items in JavaScript](https://stackoverflow.com/questions/36798005/append-multiple-items-in-javascript) – Ali Sheikhpour Jul 24 '21 at 10:54
  • Not exactly; my question is how to append a single element to multiple parents. The above describes how to append multiple children to a single parent... – RealZombs Jul 24 '21 at 10:57
  • 2
    To my knowledge there is no way of having a "single element" appear multiple times as children of different parents. Each DOM element must be a "separate" element. If you want to be able to manipulate these instances easily, then maybe assigning a meaningful class might help. – Carsten Massmann Jul 24 '21 at 11:30
  • Have two different elements but add a class to them (if they need to be distinguished from other H1 elements), then target the elements by their class. – Andy Jul 24 '21 at 11:31
  • I do know that if a unique selector is assigned to the group of elements (like a class or an attribute), then I can target all the elements. But this may add undesirable side effects when using alongside some libraries. Therefore, I wanted to know if there was an easier/simpler method than this. – RealZombs Jul 24 '21 at 11:38
  • have look around for my updated answer it can grab pretty much what you intend to do or at least come close to it. Rest is all creative logics. – Syed Jul 24 '21 at 12:08
  • This might be the answer I am looking for. Will have a try and tell you! – RealZombs Jul 24 '21 at 12:26
  • Thanks @Syed! It worked for my use case! But the only issue is that it needs to have a list of the places where it was created. It might be a problem for others but not for me. Thanks again! P.S. Can you remove the previous part of the answer? It might confuse others – RealZombs Jul 24 '21 at 12:28
  • @RealZombs welcome, as you said it and as I had mentioned in response to your earlier comment. It is not something that could be achieved as in defualt bechaivour from native Js engine. Foir more universal approach I was going to give in a JS proxy on variable and creating a trap on setter as to when ever the vraible gets updated it triggers the said function. even in that case some where down the line in scheme of things one has to provide lits of items to manipulate. – Syed Jul 24 '21 at 13:29
  • @RealZombs I ahve added an advance version as an example and is what I would be thinking on line of is I was faced with your scenario. – Syed Jul 24 '21 at 14:04

3 Answers3

1

UPDATED POST CONVERSATION

//create function that makes loops through each query. We are using query selctor here.
function appendIt (a,b){
for (let i = 0; i < a.length; i++) {
let h1 = document.createElement("h1");
h1.innerHTML = b;
  document.querySelector(a[i]).appendChild(h1);

}

};
//simply run query to get the desired effect on inner html 
function updateIt (a,b){
for (let i = 0; i < a.length; i++) {
  document.querySelector(a[i]).innerHTML = b;
  
}

}; 


//magic in action
appendIt (['body', 'div'],'heading');
updateIt (['body>h1', 'div>h1'],'headingsssss');
<!DOCTYPE html>
<html>
<body>
<div></div>
</body>
</html>

Note are in the example;

//problem statement innerHTML is using js getter and setter function behind the scene. It takes one time value and never gets trigered again when original value assigned is changed.  
let abc = 'heading';
let h1 = document.createElement("h1");
h1.innerHTML = abc;
document.body.appendChild(h1);
//below chnage will have no effect.
abc = 'heading2';
   //To bring to effect innerHTML will have to be triggered again.
<div></div>

let abc = 'heading';
let h1 = document.createElement("h1");
h1.innerHTML = abc;
document.body.appendChild(h1);
h1.innerHTML = 'heading2';
abc = 'heading3';
//innerHTML triggered again.
<div></div>

Now no matter you clone it or otherwise you will have to make a function of set number of clones and append them accordingly then create a fucntion for update, which will loop each element and change innerHTML one by one.

ADDITIONAL EXAMPLE:

//adavnce magic in action

//create a proxy and create a trap on setter
let proxy = new Proxy({}, {
    
    set(target, property, value) {
        if (property == 'data'){
        target['display'](value);
        target['data'] = value;
//for any other propety just set the value as it is
        } else {target[property] = value;}

        
    }
});
//give it a list of elements to work with 
proxy.list = ['body', 'div'];
proxy.display = function (a){
//run a little check if items are added if so simply update
if(proxy.status) {
for (let i = 0; i < proxy.list.length; i++) {
  document.querySelector(proxy.list[i]+'>h1').innerHTML = a; 
} 

}

//or add items
else {

    for (let i = 0; i < proxy.list.length; i++) {
    let h1 = document.createElement("h1");
    h1.innerHTML = a;
      document.querySelector(proxy.list[i]).appendChild(h1);
    
    }
    
    proxy.status = true;
    
    }


}; 

//Let the fireworks rip
proxy.data=48;
// Get a cup of tea and some biscuits
setTimeout(function(){proxy.data=57; }, 3000);
<!DOCTYPE html>
<html>
<body>
<div></div>
</body>
</html>

Addtional examples does precisely what you want and will update every thing on single change of variable (though as object).

Syed
  • 696
  • 1
  • 5
  • 11
  • Thanks, but this is not the correct answer. Using this method, if the original's innerHTML was changed, it does not reflect in the cloned node appended. I don't quite want different references and having to modify the innerHTML for both; I want 1 reference that (if changed) reflects in all instances where it has been added. – RealZombs Jul 24 '21 at 11:01
  • though late but got your point ..... it wont be a simple function though bit of a l;ogic run let me see if i can recreate it. – Syed Jul 24 '21 at 11:05
1

I think you are having the problem with appendChild() referring to the same instance of H1 all the time.

One Node can't be in two points of the document simultaneously. So if the node already has a parent, the node is first removed, then appended at the new position.

You can create an instance inside your loop itself so it is a fresh DOM Node every time.

let h1 = document.createElement("h1");
h1.innerHTML = "Heading"

document.body.appendChild(h1);
if (document.body.hasChildNodes()) {
//Will not work    
//  let h1 = document.createElement("h1");
//  h1.innerHTML = "Heading"
  let children = document.body.querySelectorAll('div');
  for (let i = 0; i < children.length; i++) {
      let h1 = document.createElement("h1");
      h1.innerHTML = "Heading"
      children[i].appendChild(h1);
  }
}
<body>
<div></div>
<div></div>
<div></div>
<body>
Tushar Shahi
  • 16,452
  • 1
  • 18
  • 39
  • Hi, like the previous answer, this answer also creates **multiple elements** rather than **multiple instances**. But I want multiple instances of the same element. – RealZombs Jul 24 '21 at 11:40
  • And you want to add them to the DOM in multiple places? – Tushar Shahi Jul 24 '21 at 11:41
  • Yes, precisely what I want to do, and be able to change the element and have all instances of it be changed. – RealZombs Jul 24 '21 at 11:44
1

Well, as a single instance will not be possible I constructed something (a "mini-makeshift-jQuery") to enable what you wanted to do: create clones of an original heading, while being able to the change all clones with one command, without having to "look" for them in the document:

const h1=document.createElement("h1");
h1.innerHTML = "Heading";
document.body.appendChild(h1);

const hds=[h1];
hds.appendCloneTo=function(p){
  this.push(this[0].cloneNode(true));
  p.appendChild(this[this.length-1]);
}
hds.html=function(ht){
  this.forEach(e=>e.innerHTML=ht)}

// test:
hds.appendCloneTo(document.querySelector("div"));
hds.appendCloneTo(document.querySelector("p"));
document.querySelector("button").onclick=()=>hds.html("changed!");
<div>div1</div>

<div>div2</div>

<p>paragraph</p>

<button>change heading</button>
Carsten Massmann
  • 26,510
  • 2
  • 22
  • 43