Как я могу удалить JS созданный LI с JS созданной кнопкой? - PullRequest
0 голосов
/ 16 февраля 2020

Я создаю приложение TODO в JavaScript. Как понять, как связать два созданных JavaScript элемента и удалить их из DOM?

TL; DR: Место, где я застреваю, - это навигация по DOM после списка элемент был создан. Я пытался использовать setAttribute для созданных элементов с инкриминирующим тегом «id», но я все еще могу понять, как перебирать LI, чтобы найти, какая кнопка была нажата.

Код:

        <div class="entry-field">
          <label class="task-input" for="task-input">Task:</label>
          <input type="text" class="task-field" placeholder="Enter Item"/>
          <button class="add-btn">ADD</button>
        </div>
        <section class="task-list">
          <h2>Task List</h2>
          <div class="list-wrap"></div>
            <ul class="item-log">
              <li class="list-item">
                <span class="item-text">this is the item</span>
                <button class="delete-btn">delete</button>
              </li>
            </ul>
          </div>
        </section>
      </main>

JavaScript:
let index = 0;

//Obtaining user text, creating a Span element, append span with user text:
function addItem() {
  let itemLog = document.querySelector(".item-log");
  let listItem = document.createElement("LI");
  let deleteBtn = document.createElement("BUTTON");
  let userEntry = document.querySelector(".task-field").value;
  let spanItem = document.createElement("SPAN");
  index++;
  //Creating LI "list-item" and append UL "itemLog"
  itemLog.appendChild(listItem);
  listItem.classList.add("list-item");

  listItem.setAttribute("id", index);
  //Create Span element, assign text from user input, append LI
  spanItem.innerHTML = userEntry;
  listItem.appendChild(spanItem);
  //Create deleteBtn and append to LI
  deleteBtn.innerHTML = "Delete";
  deleteBtn.classList.add("delete-btn");
  deleteBtn.setAttribute("onclick", "deleteItem()");
  deleteBtn.setAttribute("id", index);
  listItem.appendChild(deleteBtn);

  userEntry.value = "";
}

/*
2) Deleting item from list:
  -use event listener to trigger parent node
  -delete child
  */

function deleteItem() {
  let listItem = this.parentElement;
  listItem.parentElement.removeChild(listItem);
}

// Page event listeners
let addBtn = document.querySelector(".add-btn");

addBtn.addEventListener("click", addItem);

Ответы [ 4 ]

0 голосов
/ 16 февраля 2020

JS Fiddle - приложение Todo

При создании приложений, в которых необходимо динамически вставлять похожие элементы и отслеживать их с помощью системы ID, я рекомендую более организованный подход, чтобы избежать путаницы в вашем коде.

Для базовых c приложений вы можете создавать собственные классы, каждый из которых имеет методы element, template, ui, uiEventCallbacks, toggleUIEvents и render. В каждом классе element будет основным элементом HTML, который содержит разметку template. Метод render анализирует текст template и присваивает его element.innerHTML. Чтобы создать экземпляр приложения, просто создайте новый экземпляр базового класса, отобразите его, а затем добавьте элемент в тело.

Я создал приложение Todo с вашей разметкой. Есть классы TodoApp, Todos и Todo. TodoApp содержит Todos, а Todos содержит экземпляры Todo.

Todo

const Todo = class {
  constructor(settings) {
    this.settings = settings
  }
  get settings() { return this._settings }
  set settings(settings) { this._settings = settings }
  get element() {
    if(!this._element) {
      this._element = document.createElement('li')
      this._element.setAttribute('class', 'list-item')
      this._element.setAttribute('id', this.settings.id )
    }
    return this._element
  }
  get ui() { return {
    deleteButton: this.element.querySelectorAll(':scope > .delete-btn'),
  } }
  get uiEventCallbacks() { return {
    deleteButtonClick: (event) => {
      let customEvent = new CustomEvent('removeTodo', {
        bubbles: true,
        details: {
          id: this.settings.id
        }
      })
      this.element.dispatchEvent(
        customEvent
      )
    },
  } }
  remove() { 
    this.element.parentElement.removeChild(this.element)
    }
  template() { return `
    <span>${this.settings.value}</span>
    <button class="delete-btn">Delete</button>
  ` }
  toggleUIEvents() {
    [
      'removeEventListener',
      'addEventListener'

    ].forEach((eventMethod) => {
      this.ui.deleteButton.item(0)[eventMethod]('click', this.uiEventCallbacks.deleteButtonClick)
    })
  }
  render() {
    const template = this.template()
    this.element.innerHTML = template
    this.toggleUIEvents()
    return this
  }
}

Todos

const Todos = class {
    constructor() {}
  get element() {
    if(!this._element) {
      this._element = document.createElement('section')
      this._element.setAttribute('class', 'task-list')
    }
    return this._element
  }
  get ui() { return {
    itemLog: this.element.querySelectorAll('.item-log')
  } }
  get uiEventCallbacks() { return {
    removeTodo: (event) => {
      console.log(event)
      this.removeTodo()
    },
  } }
  get todos() {
    this._todos = this._todos || []
    return this._todos
  }
  template() { return `
    <h2>Task List</h2>
    <div class="list-wrap">
      <ul class="item-log"></ul>
    </div>
  ` }
  toggleUIEvents() {
    [
      'removeEventListener',
      'addEventListener'
    ].forEach((eventMethod) => {
      this.ui.itemLog.item(0)[eventMethod]('removeTodo', this.uiEventCallbacks.removeTodo)
    })
  }
  addTodo(data) {
    const todo = new Todo(data)
    this.todos.push(todo)
    this.ui.itemLog.item(0).appendChild(
      todo.render().element
    )
    return this
  }
  removeTodo(id) {
    let todoIndex = this.todos.reduce((_todoIndex, todo, todoIndex) => {
      if(todo.id === id) _todoIndex = todoIndex
      return _todoIndex
    }, -1)
    let todo = this.todos.splice(todoIndex, 1)[0]
    todo.remove()
    return this
  }
  render(data) {
    const template = this.template(data)
    this.element.innerHTML = template
    this.toggleUIEvents()
    return this
  }
}

Приложение Todo

const TodoApp = class {
  constructor() {}
  get element() {
    if(!this._element) {
      this._element = document.createElement('main')
    }
    return this._element
  }
  get ui() { return {
    addButton: this.element.querySelectorAll('.add-btn'),
    input: this.element.querySelectorAll('.task-field'),
  } }
  get uiEventCallbacks() { return {
    addTodo: (event) => {
      let taskFieldData = this.ui.input.item(0).value
      this.todos.addTodo({
        id: this.uuid(),
        value: taskFieldData,
      })
    },
  } }
  get todos() {
    this._todos = this._todos || new Todos()
    return this._todos
  }
  uuid() {
    var uuid = '', i, random
    for (i = 0; i < 32; i++) {
      random = Math.random() * 16 | 0
      if (i === 8 || i === 12 || i === 16 || i === 20) {
        uuid += "-"
      }
      uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random)).toString(16)
    }
    return uuid
  }
  template() { return `
    <div class="entry-field">
      <label class="task-input" for="task-input">Task:</label>
      <input type="text" class="task-field" placeholder="Enter Item"/>
      <button class="add-btn">ADD</button>
    </div>
  ` }
  toggleUIEvents() {
    [
      'removeEventListener',
      'addEventListener'
    ].forEach((eventMethod) => {
      this.ui.addButton.item(0)[eventMethod]('click', this.uiEventCallbacks.addTodo)
    })
  }
  render(data) {
    const template = this.template(data)
    this.element.innerHTML = template
    this.element.appendChild(this.todos.render().element)
    this.toggleUIEvents()
    return this
  }
}

Создание экземпляра приложения Todo

const todoApp = new TodoApp()
const body = document.querySelector('body')
body.insertAdjacentElement('afterbegin', todoApp.render().element)
0 голосов
/ 16 февраля 2020

В любом приемнике событий мыши всегда будет параметр MouseEvent, передаваемый в обработчик событий. Этот MouseEvent объект имеет свойство ie target, которое является HTMLElement, которое было целью события. Например:

let div = document.querySelector('div.button');
let toggleState = 0;
let messages = ['Clicked!', 'Click me!'];
let handler = function(event) {
  event.target.innerText = messages[toggleState];
  toggleState ^= 1;
}

div.addEventListener("click", handler, false);
<div class="button">Click me!</div>

Кроме того, каждый HTMLElement имеет метод remove(), который удаляет его из DOM. Например:

document.querySelector('div').remove()

или:

let handler = function(event) {
  event.target.remove();
}
document.querySelector('div').addEventListener('click', handler, false);
0 голосов
/ 16 февраля 2020
  • Назначение обработчика щелчка при создании элемента Delete.
  • Создание массива задач для предварительного заполнения

const myTasks = [
  "This is the item",
  "Well, this is Another item"
];

const EL_text = document.querySelector("#text");
const EL_add = document.querySelector("#add");
const EL_tasks = document.querySelector("#tasks");

function addTask(text) {

  // Determine if we have a text string argument (prepopulate)
  // otherwise grab the input value:
  text = typeof text === 'string' ? text : EL_text.value.trim(); // Trim your values!
  if (!text) return; // do nothing if no value!
  
  const EL_task = document.createElement('li');
  const EL_delete = document.createElement('button');
  
  EL_delete.textContent = "Delete";
  EL_delete.addEventListener('click', () => EL_task.remove());
  
  EL_task.insertAdjacentHTML('afterbegin', `<div>${text}</div>`);
  EL_task.appendChild(EL_delete);
  
  EL_tasks.appendChild(EL_task);
  EL_text.value = ''; // Clear current input text value
}

EL_add.addEventListener("click", addTask);
// Prepopulate
myTasks.forEach(addTask);
#tasks {
  padding: 0;
  list-style: none;
}
#tasks li {
  display: flex;
  background: #eee;
  padding: 0.6em;
  margin-bottom: 0.5em;
  border-radius: 0.4em;
}
#tasks li div {
  flex: 1;
}
<label>Task:<input id="text" type="text" placeholder="Enter text"></label>
<button id="add">ADD</button>
<ul id="tasks"></ul>
0 голосов
/ 16 февраля 2020

Проблема в вашем примере заключается в том, что this относится к вашей функции deleteItem. Если вы сохраните его в функции deleteItem, то увидите, что this относится к окну, а не к нажатой кнопке.

Если вместо setAttribute установить onClick, вы можете добавить прослушиватель событий: deleteBtn.addEventListener("click", deleteItem);

Внутри функции deleteItem у вас будет доступ к объекту события, а также к цели этого события, и ваш код будет работать так, как ожидается.

function deleteItem(event) {
  let listItem = event.target.parentElement;
  listItem.parentElement.removeChild(listItem);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...