Reputation: 133
I have code that allows me to drag and drop to rearrange items and move them from container to container, but I also want to update the number inside the span tag to match it's position in order.
For example, "Colorado" is in the 0 position, but when I drag it after "Utah" I want to update "Montana" to have "0" next to it, "Utah" to "1", and "Colorado" to "2", but I don't know how to do that.
Here is the JS fiddle: https://jsfiddle.net/benschnell/0wbfoqk9/
const draggables = document.querySelectorAll('.draggable')
const containers = document.querySelectorAll('.container')
draggables.forEach(draggable => {
draggable.addEventListener('dragstart', () => {
draggable.classList.add('dragging')
})
draggable.addEventListener('dragend', () => {
draggable.classList.remove('dragging')
})
})
containers.forEach(container => {
container.addEventListener('dragover', dragOver);
})
function dragOver(e) {
e.preventDefault()
const afterElement = getDragAfterElement(this, e.clientY)
const draggable = document.querySelector('.dragging')
if (afterElement == null) {
this.appendChild(draggable)
} else {
this.insertBefore(draggable, afterElement)
}
}
function getDragAfterElement(container, y) {
const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')]
return draggableElements.reduce((closest, child) => {
const box = child.getBoundingClientRect()
const offset = y - box.top - box.height / 2
if (offset < 0 && offset > closest.offset) {
return { offset: offset, element: child }
} else {
return closest
}
}, { offset: Number.NEGATIVE_INFINITY }).element
}
<ul class="container">
<li class="draggable" draggable="true"><span>0</span> Colorado</li>
<li class="draggable" draggable="true"><span>1</span> Montana</li>
<li class="draggable" draggable="true"><span>2</span> Utah</li>
<li class="draggable" draggable="true"><span>3</span> Montana</li>
<li class="draggable" draggable="true"><span>4</span> Wyoming</li>
</ul>
<ul class="container">
<li class="draggable" draggable="true"><span>0</span> Maine</li>
<li class="draggable" draggable="true"><span>1</span> North Carolina</li>
<li class="draggable" draggable="true"><span>2</span> Florida</li>
<li class="draggable" draggable="true"><span>3</span> Virginia</li>
<li class="draggable" draggable="true"><span>4</span> New York</li>
</ul>
Upvotes: 5
Views: 86
Reputation: 4626
I added in the eventlistener for the draggable a call for update the numbers.
draggable.addEventListener('dragend', (e) => {
draggable.classList.remove('dragging');
dragUpdateNr();
})
This function looks first the UL's and goes with foreach over them. Here I search for all LI's. Over these founded elements I go with a simple for-loop and set the textContent from the first child (this is the SPAN with the number) to the index from the loop.
function dragUpdateNr() {
let uls = document.querySelectorAll('ul.container');
uls.forEach(ul => {
let lis = ul.querySelectorAll('li.draggable');
for (let i=0; i<lis.length; i++) {
let li = lis[i];
li.firstChild.textContent = i;
}
});
}
Here is your extended JSFifiddle for testing and here the hole code with these 2 extensions.
const draggables = document.querySelectorAll('.draggable')
const containers = document.querySelectorAll('.container')
draggables.forEach(draggable => {
draggable.addEventListener('dragstart', () => {
draggable.classList.add('dragging')
})
draggable.addEventListener('dragend', (e) => {
draggable.classList.remove('dragging');
dragUpdateNr();
})
})
containers.forEach(container => {
container.addEventListener('dragover', dragOver);
})
function dragOver(e) {
e.preventDefault()
const afterElement = getDragAfterElement(this, e.clientY)
const draggable = document.querySelector('.dragging')
if (afterElement == null) {
this.appendChild(draggable)
} else {
this.insertBefore(draggable, afterElement)
}
}
function getDragAfterElement(container, y) {
const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')]
return draggableElements.reduce((closest, child) => {
const box = child.getBoundingClientRect()
const offset = y - box.top - box.height / 2
if (offset < 0 && offset > closest.offset) {
return { offset: offset, element: child }
} else {
return closest
}
}, { offset: Number.NEGATIVE_INFINITY }).element
}
function dragUpdateNr() {
let uls = document.querySelectorAll('ul.container');
uls.forEach(ul => {
let lis = ul.querySelectorAll('li.draggable');
for (let i=0; i<lis.length; i++) {
let li = lis[i];
li.firstChild.textContent = i;
}
});
}
.container {
max-width: 300px;
}
.container li {
height: 30px;
background-color: #fafafa;
border: 1px solid #888;
display: flex;
align-items: center;
}
.dragging {
opacity: 0.5;
}
.container li span {
background-color: #000;
margin-right: 10px;
color: #fff;
padding: 0 10px;
height: 100%;
display: flex;
align-items: center;
}
<ul class="container">
<li class="draggable" draggable="true"><span>0</span> Colorado</li>
<li class="draggable" draggable="true"><span>1</span> Montana</li>
<li class="draggable" draggable="true"><span>2</span> Utah</li>
<li class="draggable" draggable="true"><span>3</span> Montana</li>
<li class="draggable" draggable="true"><span>4</span> Wyoming</li>
</ul>
<ul class="container">
<li class="draggable" draggable="true"><span>0</span> Maine</li>
<li class="draggable" draggable="true"><span>1</span> North Carolina</li>
<li class="draggable" draggable="true"><span>2</span> Florida</li>
<li class="draggable" draggable="true"><span>3</span> Virginia</li>
<li class="draggable" draggable="true"><span>4</span> New York</li>
</ul>
Upvotes: 0
Reputation: 18197
You can loop the list elements in each container and renumber them according to their index:
const draggables = document.querySelectorAll('.draggable')
const containers = document.querySelectorAll('.container')
draggables.forEach(draggable => {
draggable.addEventListener('dragstart', () => {
draggable.classList.add('dragging')
})
draggable.addEventListener('dragend', () => {
draggable.classList.remove('dragging')
})
})
containers.forEach(container => {
container.addEventListener('dragover', dragOver);
})
function dragOver(e) {
e.preventDefault()
const afterElement = getDragAfterElement(this, e.clientY)
const draggable = document.querySelector('.dragging')
if (afterElement == null) {
this.appendChild(draggable)
} else {
this.insertBefore(draggable, afterElement)
}
document.querySelectorAll('.container').forEach(c => {
c.querySelectorAll('li.draggable').forEach((r, i) => {
r.querySelector('span').innerHTML = i
})
})
}
function getDragAfterElement(container, y) {
const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')]
return draggableElements.reduce((closest, child) => {
const box = child.getBoundingClientRect()
const offset = y - box.top - box.height / 2
if (offset < 0 && offset > closest.offset) {
return {
offset: offset,
element: child
}
} else {
return closest
}
}, {
offset: Number.NEGATIVE_INFINITY
}).element
}
<ul class="container">
<li class="draggable" draggable="true"><span>0</span> Colorado</li>
<li class="draggable" draggable="true"><span>1</span> Montana</li>
<li class="draggable" draggable="true"><span>2</span> Utah</li>
<li class="draggable" draggable="true"><span>3</span> Montana</li>
<li class="draggable" draggable="true"><span>4</span> Wyoming</li>
</ul>
<ul class="container">
<li class="draggable" draggable="true"><span>0</span> Maine</li>
<li class="draggable" draggable="true"><span>1</span> North Carolina</li>
<li class="draggable" draggable="true"><span>2</span> Florida</li>
<li class="draggable" draggable="true"><span>3</span> Virginia</li>
<li class="draggable" draggable="true"><span>4</span> New York</li>
</ul>
Upvotes: 1