-1

I have a parent div with some child elements. I want to re-order child elements based on two id values. for example 1,4. It means to grab the item with id 1 and insert it above the item with id 4.

    <div class="parent">
        <div id="1">First</div>
        <div id="2">Second</div>
        <div id="3">Third</div>
        <div id="4">Fourth</div>
        <div id="5">Fifth</div>
    </div>

Making a drag and drop component for react. And this is what i have tried

    const element = document.getElementById('1') //dragStart
    const targetElement = document.getElementById('4') //dragEnter

    const parent = document.querySelector('.parent') // drop
    parent.insertBefore(element, targetElement)

But problem is when i grab the first element and want to put it on the bottom (last child). It fails to do so. How to put a child element after last child with insertBefore() method?

FishLegs
  • 221
  • 3
  • 15
  • 1
    *"Tried with parent.insertBefore(). But not getting the desired result."* Show us that attempt so we can help you understand where it went wrong. That's exactly the right method to use. – T.J. Crowder Mar 01 '20 at 11:38
  • https://stackoverflow.com/questions/21267120/sort-by-id-element-using-jquery This might help you – Amit Mar 01 '20 at 11:39
  • 1
    Provided these elements are in the **first** element with `class="parent"`, your code works: https://jsfiddle.net/tjcrowder/yfwob2hs/ So I'm guessing they aren't in the first element with that class. But in any case, you don't want to get the parent separately. If you want to put the element in front of the target, use the target's `parentNode` as in my answer. – T.J. Crowder Mar 01 '20 at 12:26

2 Answers2

1

Don't know how you are using insertBefore() but there should not be any issues:

Update: The issue could be that your code is running before the DOM is fully loaded. You can wrap your code with DOMContentLoaded:

<script>
document.addEventListener('DOMContentLoaded', (event) => {
  const element = document.getElementById('1') //dragStart
  const targetElement = document.getElementById('4') //dragEnter

  const parent = document.querySelector('.parent') // drop
  parent.insertBefore(element, targetElement)
});

</script>

<div class="parent">
  <div id="1">First</div>
  <div id="2">Second</div>
  <div id="3">Third</div>
  <div id="4">Fourth</div>
  <div id="5">Fifth</div>
</div>

Placing the first element as the last element using nextSibling:

<script>
document.addEventListener('DOMContentLoaded', (event) => {
  const parentNode = document.querySelector('.parent');
  const element = document.getElementById('1') //dragStart
  const targetElement = document.querySelector('.parent').lastElementChild //get last child
  parentNode.insertBefore(element, targetElement.nextSibling);
});

</script>

<div class="parent">
  <div id="1">First</div>
  <div id="2">Second</div>
  <div id="3">Third</div>
  <div id="4">Fourth</div>
  <div id="5">Fifth</div>
</div>
Mamun
  • 66,969
  • 9
  • 47
  • 59
1

Note: This answers the original question. The question has now been edited to reference React. You wouldn't use the following in a React project. You'd reorder the state that the DOM represents, and then let React handle updating the DOM.


You're right to use insertBefore:

function moveElement(move, before) {
    // Get the element to move
    const elToMove = document.getElementById(move);
    // Get the element to put it in front of
    const elBefore = document.getElementById(before);
    // Move it
    elBefore.parentNode.insertBefore(elToMove, elBefore);
}

function moveElement(move, before) {
    const elToMove = document.getElementById(move);
    const elBefore = document.getElementById(before);
    elBefore.parentNode.insertBefore(elToMove, elBefore);
}

setTimeout(() => {
    moveElement("1", "4");
}, 800);
<div class="parent">
    <div id="1">First</div>
    <div id="2">Second</div>
    <div id="3">Third</div>
    <div id="4">Fourth</div>
    <div id="5">Fifth</div>
</div>

Side note: I suggest avoiding having id values that start with digits. Although they're perfectly valid HTML and they work just fine with getElementById, they're a pain if you need to target them with CSS, because a CSS ID selector (#example) can't start with an unescaped digit. For instance, document.querySelector("#1") fails. You have to escape the 1 with a hex sequence, which isn't terrifically clear: document.querySelector("#\\31") (the characters \, 3, and 1: 0x31 = 49 = the Unicode code point for 1).

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • I have updated my question with the problem i am getting this way. Your css advice is helpful – FishLegs Mar 01 '20 at 11:59
  • @FishLegs - See [my comment](https://stackoverflow.com/questions/60474666/how-to-re-order-html-child-elements-in-dom-based-on-id-value/60474705?noredirect=1#comment106984385_60474666) on the question. – T.J. Crowder Mar 01 '20 at 12:26
  • can't we use splice method instead of insertBefore? – FishLegs Mar 01 '20 at 13:52
  • 1
    @FishLegs - What `splice` method? There aren't any arrays or strings involved here, which are the two things that have `splice` that I'm aware of. Also...why would you want to? `insertBefore` is exactly the right tool for this. – T.J. Crowder Mar 01 '20 at 13:54
  • i think in react its not allowed to interact with dom directly. So i want to use ref's, nodelists converted to array and splice. React Framework doesn't like simple things like insertBefore or querySelector – FishLegs Mar 01 '20 at 16:49
  • 1
    @FishLegs - The [version of the question I answered](https://stackoverflow.com/revisions/60474666/1) said nothing about React. Questions are not supposed to be moving targets. And yes indeed, you shouldn't be directly manipulating the DOM in React. Instead, reorder the state that the DOM represents, and let React reorder the DOM. But you wouldn't do that with `splice`, because `splice` modifies the original array and you never directly modify state in React. Instead, create a *new* array in the updated order. – T.J. Crowder Mar 01 '20 at 17:59