The created element does not take javascript functionality

Asked

Viewed 70 times

1

Uncaught Typeerror: Failed to execute 'appendchild' on 'Node': Parameter 1 is not of type 'Node'. At Htmldivelement.dragover

staff do not know why, was doing a project of the Masterclass of Rocketseat where a Kanban frame is created, I am adding the functionality that when the user clicks on a button it adds a new card in the frame To Do. follows the code:

//seleciona todos os elementos 'card' do HTML
const cards = document.querySelectorAll('.card');
//seleciona todas as 'dropzones'do HTML
const dropzones = document.querySelectorAll('.dropzone');
//seleciona os botões para adicionar ma nova tarefa
const addCard = document.querySelectorAll('.btn-add-card'); 

const dropzoneCreate = document.getElementById('status-to-do')


//função que adiciona uma nova tarefa
function criaDiv () {
    let newcard = document.createElement('div');
    return newcard;
}

function creatCard() {
    let newcard = criaDiv();
    newcard.innerHTML = `<div class="card" draggable="true">
                            <div class="status" ></div>
                            <div class="content"><input type="text" placeholder="Digite aqui"></div>
                        </div>`;

    dropzoneCreate.appendChild(newcard);
}

//para cada 'card' ele vai pegar os eventos
cards.forEach(card => {
    card.addEventListener('dragstart', startDragging);
    card.addEventListener('drag', dragging);
    card.addEventListener('dragend', endDragging);
});

function startDragging() {
    dropzones.forEach(dropzone => {
        dropzone.classList.add('highlight');
    });

    this.classList.add('ghost-card');
}

function dragging() {}

function endDragging() {
    dropzones.forEach(dropzone => {
        dropzone.classList.remove('highlight');
    });

    this.classList.remove('ghost-card');
}

//local onde serão soltados os cards
dropzones.forEach(dropzone => {
    dropzone.addEventListener('dragenter', dragEnter);
    dropzone.addEventListener('dragover', dragOver);
    dropzone.addEventListener('dragleave', dragLeave);
    dropzone.addEventListener('drop', drop);
});

function dragEnter() {}

function dragOver() {
    this.classList.add('over');
    const cardDragged = document.querySelector('.ghost-card')
    this.appendChild(cardDragged);
}

function dragLeave() {
    this.classList.remove('over');
}

function drop() {
    this.classList.remove('over');

}
* {
    padding: 0;
    margin: 0;
}

html{
    font-family: 'Poppins', sans-serif;
}

body {
    background-color: #130f0d;
    color: whitesmoke;
}

.workspace {
    list-style: none;
    display: flex;
    margin-top: 32px;
}

    .board {
        background-color: #141316;
        border: 1px solid #FD951f11;
        border-radius: 5px;
        margin: 0 16px;
    }

        h3 {
            padding: 16px;
            color: #FD951Fcc;
        }

        .board button{
            float: right;
            text-align: center;
            margin-top: -43px;
            font-size: 25px;
            padding-right: 37px;
            outline: 0 none;
            border: 0 none;
            cursor: pointer;
        }

         button img{
            float: right;
            text-align: center;
            font-size: 25px;
            outline: 0 none;
            border: 0 none;
            cursor: pointer;
            position: absolute;
        }

        .dropzone {
            padding: 16px;
            min-width: 282px;
            min-height: 200px;
            transition: 0.5s;
        }

        .highlight{
            background-color: #FD952f08;
        }

            .card{
                padding: 16px;
                box-shadow: 0 2px 2px -1px #FD951Fcc;;
                border-radius: 4px;
                width: 250px;
                margin: 25px 0px;
                font-size: 18px;
                background-color: #1a1a1c;
                transition: 0.5s;
            }


            .over {
                background-color: #4cd13711;
            }

            .ghost-card {
                cursor: move;
                opacity: 0.4;
                transform:  rotate(5deg);

            }

                #status-to-do > .card > :nth-child(0n+1){
                    background-color: aqua;
                }
                #status-doing  > .card > :nth-child(0n+1){
                    background-color: blue;
                }
                #status-done  > .card > :nth-child(0n+1){
                    background-color: red;
                }

                .status {
                    width: 30px;
                    height: 8px;
                    background-color: gray;
                    margin-bottom: 16px;
                    border-radius: 8px;
                }

                .content input {
                    outline: 0 none;
                    border: none;
                    font-family: 'Poppins', sans-serif;
                    background-color: #1a1a1c;
                    color: aliceblue;
                    font-size: 18px;
                }
<!DOCTYPE html>
<html lang="pt-br">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="preconnect" href="https://fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,200;0,300;0,400;0,500;1,200;1,300;1,400&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="../../public/css/style.css">
    <title>Kanban Board</title>
</head>
<body>
    <ul class="workspace">
        <li class="board">
            <h3>To Do</h3>
            <button class="btn-add-card" onclick="creatCard();">
                <img src="../../public/images/plus.svg" alt="A">
            </button>
            <div class="dropzone" id="status-to-do">
                <div class="card" draggable="true">
                    <div class="status" ></div>
                    <div class="content"><input type="text" placeholder="Digite aqui"></div>
                </div>
            </div>
        </li>
        <li class="board">
            <h3>Doing</h3>
            <button>
                <img src="../../public/images/plus.svg" alt="A">
            </button>
            <div class="dropzone" id="status-doing">
                <div class="card" draggable="true">
                    <div class="status" >
                    </div>
                    <div class="content"><input type="text" placeholder="Digite aqui"></div>
                </div>
            </div>
        </li>
        <li class="board">
            <h3>Done</h3>
            <button>
                <img src="../../public/images/plus.svg" alt="A">
            </button>
            <div class="dropzone" id="status-done">
                <div class="card" draggable="true">
                    <div class="status" >
                    </div>
                    <div class="content"><input type="text" placeholder="Digite aqui"></div>
                </div>
            </div>
        </li>
    </ul>

    <script src="../../public/js/main.js"  defer></script>
</body>
</html>

When you click the button (in the case of the image not loaded) it adds the new element right, but does not take the javascript functionality, can you help me? thank you

2 answers

2


The Event listeners dragstart, drag and dragend are registered only for existing card elements in the DOM. When new card elements are added to the DOM, they do not have any registered Listener Event and therefore do not behave like the others.

To resolve this, as Augusto suggested, you will need to register those listeners whenever you add a new card element to the DOM. Alternatively, you can use "Event delegation" Pattern, that is, register the listeners in elements that are always in the DOM and then detect the element through the event.target.

Below is your code with the appropriate changes to record events with "Event delegation".

//seleciona todos os elementos 'card' do HTML
const cards = document.querySelectorAll('.card');
//seleciona todas as 'dropzones'do HTML
const dropzones = document.querySelectorAll('.dropzone');
//seleciona os botões para adicionar ma nova tarefa
const addCard = document.querySelectorAll('.btn-add-card');

const dropzoneCreate = document.getElementById('status-to-do')


//função que adiciona uma nova tarefa
function criaDiv() {
  let newcard = document.createElement('div');
  return newcard;
}

function creatCard() {
  let newcard = criaDiv();
  newcard.innerHTML = `<div class="card" draggable="true">
                            <div class="status" ></div>
                            <div class="content"><input type="text" placeholder="Digite aqui"></div>
                        </div>`;

  dropzoneCreate.appendChild(newcard);
}

// Início: Registo de event listeners utilizando event delegation pattern
document.querySelector('.workspace').addEventListener(
  'dragstart',
  (event) => {
    if (event.target.classList.contains('card')) startDragging(event.target);
  },
);

document.querySelector('.workspace').addEventListener(
  'drag',
  (event) => {
    if (event.target.classList.contains('card')) dragging(event.target);
  },
);

document.querySelector('.workspace').addEventListener(
  'dragend',
  (event) => {
    if (event.target.classList.contains('card')) endDragging(event.target);
  },
);
// Fim: Registo de event listeners utilizando event delegation pattern


function startDragging(card) {
  dropzones.forEach(dropzone => {
    dropzone.classList.add('highlight');
  });

  card.classList.add('ghost-card');
}

function dragging(card) {}

function endDragging(card) {
  dropzones.forEach(dropzone => {
    dropzone.classList.remove('highlight');
  });

  card.classList.remove('ghost-card');
}

//local onde serão soltados os cards
dropzones.forEach(dropzone => {
  dropzone.addEventListener('dragenter', dragEnter);
  dropzone.addEventListener('dragover', dragOver);
  dropzone.addEventListener('dragleave', dragLeave);
  dropzone.addEventListener('drop', drop);
});

function dragEnter() {}

function dragOver() {
  this.classList.add('over');
  const cardDragged = document.querySelector('.ghost-card')
  this.appendChild(cardDragged);
}

function dragLeave() {
  this.classList.remove('over');
}

function drop() {
  this.classList.remove('over');

}
* {
  padding: 0;
  margin: 0;
}

html {
  font-family: 'Poppins', sans-serif;
}

body {
  background-color: #130f0d;
  color: whitesmoke;
}

.workspace {
  list-style: none;
  display: flex;
  margin-top: 32px;
}

.board {
  background-color: #141316;
  border: 1px solid #FD951f11;
  border-radius: 5px;
  margin: 0 16px;
}

h3 {
  padding: 16px;
  color: #FD951Fcc;
}

.board button {
  float: right;
  text-align: center;
  margin-top: -43px;
  font-size: 25px;
  padding-right: 37px;
  outline: 0 none;
  border: 0 none;
  cursor: pointer;
}

button img {
  float: right;
  text-align: center;
  font-size: 25px;
  outline: 0 none;
  border: 0 none;
  cursor: pointer;
  position: absolute;
}

.dropzone {
  padding: 16px;
  min-width: 282px;
  min-height: 200px;
  transition: 0.5s;
}

.highlight {
  background-color: #FD952f08;
}

.card {
  padding: 16px;
  box-shadow: 0 2px 2px -1px #FD951Fcc;
  ;
  border-radius: 4px;
  width: 250px;
  margin: 25px 0px;
  font-size: 18px;
  background-color: #1a1a1c;
  transition: 0.5s;
}

.over {
  background-color: #4cd13711;
}

.ghost-card {
  cursor: move;
  opacity: 0.4;
  transform: rotate(5deg);
}

#status-to-do>.card> :nth-child(0n+1) {
  background-color: aqua;
}

#status-doing>.card> :nth-child(0n+1) {
  background-color: blue;
}

#status-done>.card> :nth-child(0n+1) {
  background-color: red;
}

.status {
  width: 30px;
  height: 8px;
  background-color: gray;
  margin-bottom: 16px;
  border-radius: 8px;
}

.content input {
  outline: 0 none;
  border: none;
  font-family: 'Poppins', sans-serif;
  background-color: #1a1a1c;
  color: aliceblue;
  font-size: 18px;
}
<!DOCTYPE html>
<html lang="pt-br">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="preconnect" href="https://fonts.gstatic.com">
  <link href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,200;0,300;0,400;0,500;1,200;1,300;1,400&display=swap" rel="stylesheet">
  <link rel="stylesheet" href="../../public/css/style.css">
  <title>Kanban Board</title>
</head>

<body>
  <ul class="workspace">
    <li class="board">
      <h3>To Do</h3>
      <button class="btn-add-card" onclick="creatCard();">
                <img src="../../public/images/plus.svg" alt="A">
            </button>
      <div class="dropzone" id="status-to-do">
        <div class="card" draggable="true">
          <div class="status"></div>
          <div class="content"><input type="text" placeholder="Digite aqui"></div>
        </div>
      </div>
    </li>
    <li class="board">
      <h3>Doing</h3>
      <button>
                <img src="../../public/images/plus.svg" alt="A">
            </button>
      <div class="dropzone" id="status-doing">
        <div class="card" draggable="true">
          <div class="status">
          </div>
          <div class="content"><input type="text" placeholder="Digite aqui"></div>
        </div>
      </div>
    </li>
    <li class="board">
      <h3>Done</h3>
      <button>
                <img src="../../public/images/plus.svg" alt="A">
            </button>
      <div class="dropzone" id="status-done">
        <div class="card" draggable="true">
          <div class="status">
          </div>
          <div class="content"><input type="text" placeholder="Digite aqui"></div>
        </div>
      </div>
    </li>
  </ul>

  <script src="../../public/js/main.js" defer></script>
</body>

</html>

  • I liked this implementation. + 1

  • Thank you very much, it worked out the way I wanted it to

  • sorry but I am well Noob using the platform, where is the option to mark as solved? thanks

1

All that was left was to register the events dragstart, drag and dragend next to the new card created in function creatCard(), the existing code only records these events to the cards created on HTML.

//seleciona todos os elementos 'card' do HTML
const cards = document.querySelectorAll('.card');
//seleciona todas as 'dropzones'do HTML
const dropzones = document.querySelectorAll('.dropzone');
//seleciona os botões para adicionar ma nova tarefa
const addCard = document.querySelectorAll('.btn-add-card');

const dropzoneCreate = document.getElementById('status-to-do')


//função que adiciona uma nova tarefa
function criaDiv() {
  let newcard = document.createElement('div');
  return newcard;
}

function creatCard() {
  let newcard = criaDiv();
  newcard.innerHTML = `<div class="card" draggable="true">
                            <div class="status" ></div>
                            <div class="content"><input type="text" placeholder="Digite aqui"></div>
                        </div>`;
  /******************************************************
   ******************************************************
   ****Aqui adicione os eventos ao novo cartão criado**** 
   ******************************************************
   ******************************************************/
  newcard.addEventListener('dragstart', startDragging);
  newcard.addEventListener('drag', dragging);
  newcard.addEventListener('dragend', endDragging);

  dropzoneCreate.appendChild(newcard);
}

//**************************************************************
//Esse código será executado apenas um vez ao carregar o arquivo
//**************************************************************
cards.forEach(card => {
  card.addEventListener('dragstart', startDragging);
  card.addEventListener('drag', dragging);
  card.addEventListener('dragend', endDragging);
});

function startDragging() {
  dropzones.forEach(dropzone => {
    dropzone.classList.add('highlight');
  });

  this.classList.add('ghost-card');
}

function dragging() {}

function endDragging() {
  dropzones.forEach(dropzone => {
    dropzone.classList.remove('highlight');
  });

  this.classList.remove('ghost-card');
}

//local onde serão soltados os cards
dropzones.forEach(dropzone => {
  dropzone.addEventListener('dragenter', dragEnter);
  dropzone.addEventListener('dragover', dragOver);
  dropzone.addEventListener('dragleave', dragLeave);
  dropzone.addEventListener('drop', drop);
});

function dragEnter() {}

function dragOver() {
  this.classList.add('over');
  const cardDragged = document.querySelector('.ghost-card');
  this.appendChild(cardDragged);
}

function dragLeave() {
  this.classList.remove('over');
}

function drop() {
  this.classList.remove('over');

}
* {
  padding: 0;
  margin: 0;
}

html {
  font-family: 'Poppins', sans-serif;
}

body {
  background-color: #130f0d;
  color: whitesmoke;
}

.workspace {
  list-style: none;
  display: flex;
  margin-top: 32px;
}

.board {
  background-color: #141316;
  border: 1px solid #FD951f11;
  border-radius: 5px;
  margin: 0 16px;
}

h3 {
  padding: 16px;
  color: #FD951Fcc;
}

.board button {
  float: right;
  text-align: center;
  margin-top: -43px;
  font-size: 25px;
  padding-right: 37px;
  outline: 0 none;
  border: 0 none;
  cursor: pointer;
}

button img {
  float: right;
  text-align: center;
  font-size: 25px;
  outline: 0 none;
  border: 0 none;
  cursor: pointer;
  position: absolute;
}

.dropzone {
  padding: 16px;
  min-width: 282px;
  min-height: 200px;
  transition: 0.5s;
}

.highlight {
  background-color: #FD952f08;
}

.card {
  padding: 16px;
  box-shadow: 0 2px 2px -1px #FD951Fcc;
  ;
  border-radius: 4px;
  width: 250px;
  margin: 25px 0px;
  font-size: 18px;
  background-color: #1a1a1c;
  transition: 0.5s;
}

.over {
  background-color: #4cd13711;
}

.ghost-card {
  cursor: move;
  opacity: 0.4;
  transform: rotate(5deg);
}

#status-to-do>.card> :nth-child(0n+1) {
  background-color: aqua;
}

#status-doing>.card> :nth-child(0n+1) {
  background-color: blue;
}

#status-done>.card> :nth-child(0n+1) {
  background-color: red;
}

.status {
  width: 30px;
  height: 8px;
  background-color: gray;
  margin-bottom: 16px;
  border-radius: 8px;
}

.content input {
  outline: 0 none;
  border: none;
  font-family: 'Poppins', sans-serif;
  background-color: #1a1a1c;
  color: aliceblue;
  font-size: 18px;
}
<!DOCTYPE html>
<html lang="pt-br">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="preconnect" href="https://fonts.gstatic.com">
  <link href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,200;0,300;0,400;0,500;1,200;1,300;1,400&display=swap" rel="stylesheet">
  <link rel="stylesheet" href="../../public/css/style.css">
  <title>Kanban Board</title>
</head>

<body>
  <ul class="workspace">
    <li class="board">
      <h3>To Do</h3>
      <button class="btn-add-card" onclick="creatCard();">
                <img src="../../public/images/plus.svg" alt="A">
            </button>
      <div class="dropzone" id="status-to-do">
        <div class="card" draggable="true">
          <div class="status"></div>
          <div class="content"><input type="text" placeholder="Digite aqui"></div>
        </div>
      </div>
    </li>
    <li class="board">
      <h3>Doing</h3>
      <button>
                <img src="../../public/images/plus.svg" alt="A">
            </button>
      <div class="dropzone" id="status-doing">
        <div class="card" draggable="true">
          <div class="status">
          </div>
          <div class="content"><input type="text" placeholder="Digite aqui"></div>
        </div>
      </div>
    </li>
    <li class="board">
      <h3>Done</h3>
      <button>
                <img src="../../public/images/plus.svg" alt="A">
            </button>
      <div class="dropzone" id="status-done">
        <div class="card" draggable="true">
          <div class="status">
          </div>
          <div class="content"><input type="text" placeholder="Digite aqui"></div>
        </div>
      </div>
    </li>
  </ul>

  <script src="../../public/js/main.js" defer></script>
</body>

</html>

Browser other questions tagged

You are not signed in. Login or sign up in order to post.