0

I am trying to move two elements on load with Javascript. My HTML looks like this:

<body>
    <div class="something">
        <nav class="navbar"></nav>
        <footer></footer>
    </div>
</body>

What am trying to achieve is HTML like this:

<body>
    <nav class="navbar"></nav>
    <div class="something"></div>
    <footer></footer>
</body>

Basically, the nav element below the opening body tag, and the footer at the end.

I did try something like this:

var a = document.getElementsByClassName("navbar");
document.body.appendChild(a);

Can anybody try to help me with this?

KarthikNayak98
  • 363
  • 3
  • 13
Mihalma
  • 91
  • 1
  • 8
  • `.appendChild()` inserts an element as the last child of the parent element, hence this is not the right tool for `.navbar` (but it would work for the footer) – Andreas Feb 10 '21 at 11:51
  • `.getElementsByClassName()` returns a list of elements. Either access the element with `[0]` or have a look at `.querySelector()` + `.insertBefore()` – Andreas Feb 10 '21 at 11:52

2 Answers2

2

See comments:

// Get the `.something` element and its parent
const something = document.querySelector(".something");
const parent = something.parentElement;

// Get the `navbar` inside it and move it in front of it
const navbar = something.querySelector(".navbar");
parent.insertBefore(navbar, something);

// Get the `footer` inside it and insert it after it
const footer = something.querySelector("footer");
parent.insertBefore(footer, something.nextElementSibling);
<div class="something">
    <nav class="navbar"></nav>
    <footer></footer>
</div>

After that runs, if you right-click the output pane and look at the HTML, you'll see they're in the requested order (ignore the script that the snippet puts there).

If you have this repeated on a page, all you have to change is how you get the something elements to work on, using a loop:

for (const something of document.querySelectorAll(".something")) {
    // ...rest of the code goes here unchanged...
}

Note that that requires that the NodeList from querySelectorAll is iterable, which it is in modern environments. You can polyfill that for slightly older environments that support iteration but hadn't yet made NodeList iterable as I describe in this answer. For environments that don't support iterability, that link also shows how to polyfill forEach if NodeList doesn't have it, which you'd use like this:

document.querySelectorAll(".something").forEach(something => {
    // ...rest of the code goes here unchanged...
});
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • its better to just use ids instead of classes, since classes are meant to be used in multiple while ids are meant to be unique – Luke_ Feb 10 '21 at 11:57
  • @Luke_ - I'm using the structure the OP quoted. Also, I don't think `id`s are needed or relevant here. For instance, suppose it weren't `document.body`, but something they were doing in a loop over multiple `something` elements... (That's actually why I used `something.parentElement` rather than `document.body`.) – T.J. Crowder Feb 10 '21 at 11:59
  • 1
    For the unaware like me... In OPs example `something.nextElementSibling` would be `null` (and a ` – Andreas Feb 10 '21 at 12:06
0

You can use inserBefore to do this. My answer is like T.J. Crowder answer but this will work if you have multiple instances of those elements by looping over all found instances.

document.querySelectorAll('.navbar').forEach((el) => {
  el.parentNode.parentNode.insertBefore(el, el.parentNode);
});

document.querySelectorAll('footer').forEach((el) => {
  el.parentNode.parentNode.insertBefore(el, el.parentNode.nextElementSibling);
});
<body>
    <div class="something">
        something
        <nav class="navbar">navbar</nav>
        <footer>footer</footer>
    </div>
    
    <div class="something">
        something2
        <nav class="navbar">navbar2</nav>
        <footer>footer2</footer>
    </div>
</body>
Mark Baijens
  • 13,028
  • 11
  • 47
  • 73