KAD
KAD

Reputation: 11122

How to get reference to the new DOM object after changing content with outerHTML?

I have a division that I need to change its outer HTML upon an event. The problem is that upon setting the outerHTML I am not able to reference the new selected DOM object unless I explicitly catch it again.

Is there a way to directly update the variable reference upon calling outerHTML (in my case the reference of the div variable below) ?

$("#changeDiv").click(function(){

  var div = $(this).prev();
  div[0].outerHTML = `<div id="imSecondtDiv"> <p> World </p> </div>`;
  console.log(div); // logs [div#imFirstDiv, prevObject: n.fn.init[1], context: button#changeDiv]
  
  // the following line does not affect the newly added division 
  // since the var `div` references the old DOM object
  // unless I add div = $(this).prev(); before setting the html of 
  // the paragraph it will not set it
  div.find('p').html('Override'); 

});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="imFirstDiv"> <p> Hello </p> </div>
<button id="changeDiv" >Change Div 1</button>

Upvotes: 11

Views: 1829

Answers (3)

Mori
Mori

Reputation: 6830

Is there a way to directly update the variable reference upon calling outerHTML?

No. According to MDN Web Docs:

While the element will be replaced in the document, the variable whose outerHTML property was set will still hold a reference to the original element.

const p = document.querySelector('p');
p.outerHTML = '<p>Goodbye, world!</p>';
console.log(p.textContent);
<p>Hello, world!</p>

As a workaround, however, you can create a live HTMLCollection from which to get the element both before and after the update:

const Ps = document.getElementsByTagName('p');
Ps[0].outerHTML = '<p>Goodbye, world!</p>';
console.log(Ps[0].textContent);
<p>Hello, world!</p>

Upvotes: 0

Jakub M.
Jakub M.

Reputation: 331

I have solved this by getting a reference element (sibling or parent) of tag that's going to be replaced.

Here is a function which is not dependent on which element are you going to change:

function replaceElement(ele, outerHTML)
{
  var parent = false, refEle;
  //if element that's going to be changed has previousElementSibling, take it as reference. If not, the parentElement will be the reference.
  if (ele.previousElementSibling !== null)
    refEle = ele.previousElementSibling;
  else
  {
    refEle = ele.parentElement;
    //indicate that parentElement has been taken as reference
    parent = true;
  }
  //change the outerHTML
  ele.outerHTML = outerHTML;
  //return the correct reference
  if (parent)
    return refEle.firstElementChild;
  else return refEle.nextElementSibling;
}

So in your case, you would invoke it this way:

div[0] = replaceElement(div[0], '<div id="imSecondtDiv"> <p> World </p> </div>');

I hope it will work with jQuery as well, as I am writing all my scripts only in native javascript.

Upvotes: 4

Rhumborl
Rhumborl

Reputation: 16609

As you are seeing changing the outerHTML makes things behave a bit strangely, as you are completely replacing the original element but still referencing the old one.

It would be better to create a new div, add it after() the old one then remove() the old one. This maintains the position of the div in the correct place.

$("#changeDiv").click(function(){

  // get the oldDiv
  var oldDiv = $(this).prev();

  // Create a newDiv
  var newDiv = $('<div id="imSecondtDiv"> <p> World </p> </div>');

  // add newDiv after oldDiv one, then remove oldDiv from the DOM.
  oldDiv.after(newDiv).remove();
  
  // now you still have the reference to newDiv, so do what you want with it
  newDiv.find('p').html('Override'); 

});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="imFirstDiv"> <p> Hello </p> </div>
<button id="changeDiv" >Change Div 1</button>

Using outerHTML

If you really really do need to use outerHTML, you can simply grab $(this).prev() again:

$("#changeDiv").click(function(){

  var div = $(this).prev();
  div[0].outerHTML = `<div id="imSecondtDiv"> <p> World </p> </div>`;

  // the "new" div is now before the button, so grab the reference of THAt one
  div = $(this).prev();

  // the following line does not affect the newly added division 
  // since the var `div` references the old DOM object
  // unless I add div = $(this).prev(); before setting the html of 
  // the paragraph it will not set it
  div.find('p').html('Override'); 

});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="imFirstDiv"> <p> Hello </p> </div>
<button id="changeDiv" >Change Div 1</button>

Upvotes: 2

Related Questions