54

I have html code that looks roughly like this:

<div id="id1">
  <div id="id2">
    <p>some html</p>
    <span>maybe some more</span>
  </div>
  <div id="id3">
    <p>different text here</p>
    <input type="text">
    <span>maybe even a form item</span>
  </div>
</div>

Obviously there's more to it than that, but that's the basic idea. What I need to do is switch the location of #id2 and #id3, so the result is:

<div id="id1">
  <div id="id3">...</div>
  <div id="id2">...</div>
</div>

Does anyone know of a function (I'm sure I'm not the first person to require this functionality) that can read and write the two nodes (and all their children) so as to swap their location in the DOM?

user41435
  • 543
  • 1
  • 4
  • 4

5 Answers5

78

In this case, document.getElementById('id1').appendChild(document.getElementById('id2')); should do the trick.

More generally you can use insertBefore().

frnhr
  • 12,354
  • 9
  • 63
  • 90
Greg
  • 316,276
  • 54
  • 369
  • 333
  • 6
    `.prepend()` might be what some are looking for too. Here's [a short list](https://vanillajstoolkit.com/reference/#DOM-Injection) of more useful methods. – Adam Johnson Jun 10 '20 at 16:53
3

This function takes any node that is passed into it and wraps it with the tag given. In the example code snippet I wrapped a span tag with a section tag.

function wrap(node, tag) {
  node.parentNode.insertBefore(document.createElement(tag), node);
  node.previousElementSibling.appendChild(node);
}

function wrap(node, tag) {
  node.parentNode.insertBefore(document.createElement(tag), node);
  node.previousElementSibling.appendChild(node);
}
let toWrap = document.querySelector("#hi");
wrap(toWrap, "section");
console.log(document.querySelector("section > #hi"), " section wrapped element");
<span id="hi">hello there!</span>
zfrisch
  • 8,474
  • 1
  • 22
  • 34
2

You can use insertAdjacentElement instead of appendChild to have more control about the position of element with respect to a target element.

Syntax: targetElement.insertAdjacentElement(position, element). It has four position codes as:

  • 'beforebegin': Before the targetElement itself.
  • 'afterbegin': Just inside the targetElement, before its first child.
  • 'beforeend': Just inside the targetElement, after its last child.
  • 'afterend': After the targetElement itself.

it appears as:

//beforebegin
<p>
  //afterbegin
  foo
  //beforeend
</p>
//afterend

In your case, you can write the code as:

document.getElementById('id2').insertAdjacentElement('beforebegin', document.getElementById('id3'));

Note that this way, you don't need reference the parent (container) element!

Also consider You have more elements than id2, id3, eg: id4, id5, id6. Now, if you want to reposition for example id5 after id2, its as simple as:

function changePosition() {
   document.getElementById('id2').insertAdjacentElement('afterend', document.getElementById('id5'));
}
<div id='container'>
  <div id='id1'>id1</div>
  <div id='id2'><u>id2</u></div>  
  <div id='id3'>id3</div>  
  <div id='id4'>id4</div>  
  <div id='id5'><b>id5</b></div>  
  <div id='id6'>id6</div>  
</div>

<p><input type='button' onclick="changePosition()" value="change position"></p>
S.Serpooshan
  • 7,608
  • 4
  • 33
  • 61
1

In my opinion is worth adding that if you need just a visual change (the DOM will stay the same but I will change in the UI) you can use the CSS order property.

It is probably more efficient that working on the DOM like the other answers, althought again doesn't really change the DOM structure so of course is not a real answer to this question.

Example:

document.addEventListener("DOMContentLoaded", function () {

  const btnEl = document.getElementById('btn-swap');
  const elToSwap = document.getElementById('id2');
  
  
  btnEl.addEventListener('click', e => {
      elToSwap.classList.toggle("first");
    });

});
.container {
  display: flex;
  flex-direction: column;
}

.first {
  order: -1;
}
<div class="container">
  <div id="id1">first DIV</div>
  <div id="id2">second DIV</div>
</div>

<button id="btn-swap">swap divs</button>
GBra 4.669
  • 1,512
  • 3
  • 11
  • 23
0

Short

I just add button (at the bottom) and js to your html

id3.after(id2);

function swap() {
  id3.after(id2);
}
<div id="id1">
  <div id="id2">
    <p>some html</p>
    <span>maybe some more</span>
  </div>
  <div id="id3">
    <p>different text here</p>
    <input type="text">
    <span>maybe even a form item</span>
  </div>
</div>

<button onclick="swap()">swap</button>
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345