Re-render after drag and drop

Viewed 28

If I drag and drop one element, then everything works fine, but if I add more elements, then everything does not work correctly.

I want to re-render all the elements after drag and drop. I don't care about their order, as long as they are in the right columns.

Please tell me what I'm doing wrong.

codepen

const newTasks = document.querySelector('.trello__body--new')
const inProgress = document.querySelector('.trello__body--in-progress')
const completed = document.querySelector('.trello__body--completed')
const columns = document.querySelectorAll('.trello__body')

let currentDragItemID = null

const tasks = [
    { id: 1, text: 'Learn JS', status: 'new' },
    { id: 2, text: 'Learn React', status: 'new' },
    { id: 3, text: 'Learn TypeScript', status: 'new' },
    { id: 4, text: 'Learn Node.js', status: 'new' },
    { id: 5, text: 'Learn Next.js', status: 'new' },
]

const dragStart = event => {
    currentDragItemID = parseInt(event.target.getAttribute('task-id'))
    event.target.classList.add('trello__item--hide')
}

const dragEnd = event => (event.target.className = 'trello__item')

const columnDragOver = event => event.preventDefault()

const columnDragEnter = event => event.target.classList.add('trello__body--active')

const columnDragLeave = event => event.target.classList.remove('trello__body--active')

const columnDragDrop = event => {
    const status = event.target.getAttribute('status')

    const updatedTasks = tasks.map(task => {
        if (task.id === currentDragItemID) {
            return { ...task, status: status }
        }
        return task
    })

    event.target.classList.remove('trello__body--active')
    render(updatedTasks)
}

function render(array) {
    document.querySelectorAll('.trello__item').forEach(elem => elem.remove())

    array.forEach(item => {
        const task = document.createElement('div')
        task.className = 'trello__item'
        task.innerHTML = item.text
        task.setAttribute('task-id', item.id)
        task.draggable = true

        switch (item.status) {
            case 'new':
                newTasks.append(task)
                break
            case 'in-progress':
                inProgress.append(task)
                break
            case 'completed':
                completed.append(task)
                break
            default:
                break
        }

        task.addEventListener('dragstart', dragStart)
        task.addEventListener('dragend', dragEnd)
    })
}

render(tasks)

columns.forEach(item => {
    item.addEventListener('dragover', columnDragOver)
    item.addEventListener('dragenter', columnDragEnter)
    item.addEventListener('dragleave', columnDragLeave)
    item.addEventListener('drop', columnDragDrop)
})
1 Answers

The issues can be found in the columnDragDrop function:

const updatedTasks = tasks.map(task => {
    if (task.id === currentDragItemID) {
        return { ...task, status: status }
    }
    return task
})

Here you use tasks to alter the values, then you use that new array to render it: render(updatedTasks).

This works fine, for the first time ;)

The second time this will run, you'll use tasks again, but thats the original data, not the data you've created on the previous drop.

So'll need to 'save' the result of that map().


Easiest way is to change tasks from const to let and overwrite that variable each time so you'll use the most up-to-date data:

tasks = tasks.map(task => {
    if (task.id === currentDragItemID) {
        return { ...task, status: status }
    }
    return task
})

Applying that logic gives us this demo:

const newTasks = document.querySelector('.trello__body--new')
const inProgress = document.querySelector('.trello__body--in-progress')
const completed = document.querySelector('.trello__body--completed')
const columns = document.querySelectorAll('.trello__body')

let currentDragItemID = null

let tasks = [
    { id: 1, text: 'Learn JS', status: 'new' },
    { id: 2, text: 'Learn React', status: 'new' },
    { id: 3, text: 'Learn TypeScript', status: 'new' },
    { id: 4, text: 'Learn Node.js', status: 'new' },
    { id: 5, text: 'Learn Next.js', status: 'new' },
]

const dragStart = event => {
    currentDragItemID = parseInt(event.target.getAttribute('task-id'))
    event.target.classList.add('trello__item--hide')
}

const dragEnd = event => (event.target.className = 'trello__item')

const columnDragOver = event => event.preventDefault()

const columnDragEnter = event => event.target.classList.add('trello__body--active')

const columnDragLeave = event => event.target.classList.remove('trello__body--active')

const columnDragDrop = event => {
    const status = event.target.getAttribute('status')

    tasks = tasks.map(task => {
        if (task.id === currentDragItemID) {
            return { ...task, status: status }
        }
        return task
    })
 
    event.target.classList.remove('trello__body--active')
    render(tasks)
}

function render(array) {
    document.querySelectorAll('.trello__item').forEach(elem => elem.remove())

    array.forEach(item => {
        const task = document.createElement('div')
        task.className = 'trello__item'
        task.innerHTML = item.text
        task.setAttribute('task-id', item.id)
        task.draggable = true

        switch (item.status) {
            case 'new':
                newTasks.append(task)
                break
            case 'in-progress':
                inProgress.append(task)
                break
            case 'completed':
                completed.append(task)
                break
            default:
                break
        }

        task.addEventListener('dragstart', dragStart)
        task.addEventListener('dragend', dragEnd)
    })
}

render(tasks)

columns.forEach(item => {
    item.addEventListener('dragover', columnDragOver)
    item.addEventListener('dragenter', columnDragEnter)
    item.addEventListener('dragleave', columnDragLeave)
    item.addEventListener('drop', columnDragDrop)
})
* {padding: 0; margin: 0; border: 0; }
*, *:before, *:after {-moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
:focus, :active {outline: none; }
a {color: inherit; }
a:focus, a:active {outline: none; }
a, a:visited {text-decoration: none; }
a:hover {text-decoration: none; }
nav, footer, header, aside {display: block; }
html, body {height: 100%; width: 100%; font-size: 100%; line-height: 1; font-size: 14px; -ms-text-size-adjust: 100%; -moz-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; font-family: var(--font-family); font-weight: 400; }
input, button, textarea {font-family: inherit; }
textarea {resize: none; }
input::-ms-clear {display: none; }
button {cursor: pointer; }
button::-moz-focus-inner {padding: 0; border: 0; }
ul li {list-style: none; font-style: normal; }
img {vertical-align: top; }
h1, h2, h3, h4, h5, h6 {font-size: inherit; font-weight: 400; }

/*--------------------*/

:root {--font-family: 'Source Sans Pro', sans-serif; } 
body {display: flex; flex-direction: column; justify-content: center; align-items: center; background: #0f1217; } 
.container {max-width: 1000px; width: 100%; } 
.trello {display: flex; flex-direction: row; height: 100vh; } 
.trello__column {height: 100%; border: 1px solid #1d1f25; display: flex; flex-direction: column; justify-content: flex-start; flex: 1; } 
.trello__header {border-bottom: 1px solid #1d1f25; font-size: 22px; padding: 20px; font-weight: 600; color: #fff; text-align: center; background: #2e323b; } 
.trello__body {display: flex; flex-direction: column; justify-content: flex-start; overflow-y: auto; height: 100%; } 
.trello__item {background: #1d1f25; border: 1px solid #2e323b; color: #fff; padding: 20px; font-size: 18px; cursor: grab; opacity: 1; transition: 0.3s; width: 100%; } 
.trello__item--active {background: #cf4f40; } 
.trello__body--active {border: 1px solid #cf4f40; } 
.trello__item--hide {opacity: 0.2; transition: 0.3s; }
<div class="container">
  <section class="trello">
    <div class="trello__column ">
      <div class="trello__header">New</div>
      <div class="trello__body trello__body--new" status="new">
      </div>
    </div>
    <div class="trello__column ">
      <div class="trello__header">In progress</div>
      <div class="trello__body trello__body--in-progress" status="in-progress"></div>
    </div>
    <div class="trello__column ">
      <div class="trello__header">Completed</div>
      <div class="trello__body trello__body--completed" status="completed"></div>
    </div>
  </section>
</div>

Related