Почему не обновляется string.length? - PullRequest
0 голосов
/ 01 ноября 2018

Это мой первый фрагмент кода, сделанный самостоятельно. Я пытаюсь создать поле ввода для добавления элементов в список, а затем, если вы нажмете кнопку, чтобы сгенерировать код, он соберет все отмеченные элементы и скопирует текст в другой div.

По сути, мой вопрос касается двух переменных:

const list = listUl.children;
const listCopy = listUl.querySelectorAll('span');

Допустим, у меня есть 4 пункта в списке. Если я добавляю новый элемент в список, я вижу, как list.length добавляет этот новый элемент, он изменяется с 4 на 5. Но этого не происходит с listCopy.length, значение остается равным 4.

Почему это происходит, если lstCopy находится внутри списка? Как я могу обновить listCopy тоже?

Вот мой код:

const addItemInput = document.querySelector('.addItemInput');
const addItemButton = document.querySelector('.addItemButton');
const copyText = document.querySelector('.generateCode');
const listUl = document.querySelector('.list');
const list = listUl.children;
const listCopy = listUl.querySelectorAll('span');
const clonedCode = document.querySelector('.code p');

//FUNCTION: Generate value/items = Draggable, Checkbox, Remove button
const attachItemListButton = (item) => {

    //Draggable
    item.draggable = "true";
    item.classList.add("list--item");

    //Checkbox
    let checkbox = document.createElement('input');
    checkbox.type = 'checkbox';
    checkbox.className = 'checkbox';
    checkbox.name = "chkboxName1";
    checkbox.value = "value";
    checkbox.id = "id";
    item.insertBefore(checkbox, item.childNodes[0] || null);

    //Remove button
    let remove = document.createElement('button');
    remove.className = 'remove';
    remove.textContent = 'x';
    item.appendChild(remove);
};
for (let i = 0; i < list.length; i += 1) {
    attachItemListButton(list[i]);
}


//Cloning code if there are checked items
copyText.addEventListener('click', () => {
  let copyTextFromList = "";
  for (let i = 0; i < listCopy.length; i += 1) {
    if (listCopy[i].parentNode.querySelector("input:checked")) {
      copyTextFromList += listCopy[i].textContent + ',';
    }
  }
  clonedCode.innerHTML = copyTextFromList;
});


//Add item from the input field to the list
addItemButton.addEventListener('click', () => {
  let li = document.createElement('li');
  let span = document.createElement('span');
  span.textContent = addItemInput.value;
  listUl.appendChild(li);
  li.appendChild(span);
  attachItemListButton(li);
  addItemInput.value = '';
});


//FUNCTION: Remove button
listUl.addEventListener('click', (event) => {
    if (event.target.tagName == 'BUTTON') {
        if (event.target.className == 'remove') {
            let li = event.target.parentNode;
            let ul = li.parentNode;
            ul.removeChild(li);
        }
    }
});
/* Google fonts */
@import url('https://fonts.googleapis.com/css?family=Heebo:300,400,700');

/* Root */
:root {
    --color-white:           #fff;
    --color-black:           #2D3142;
    --color-black-2:         #0E1116;
    --color-gray:            #CEE5F2;
    --color-gray-2:          #ACCBE1;
    --color-gray-3:          #CEE5F2;
    --color-green:           #439775;
    --color-blue:           #4686CC;
}

body {
    font-family: 'Heebo', sans-serif;
    font-weight: 400;
    font-size: 16px;
    color: black;

}
h2 {
    font-weight: 700;
    font-size: 1.5rem;
}
h3 {
    font-weight: 700;
    font-size: 1.25rem;
}

button {
    background: var(--color-blue);
    padding: 5px 10px;
    border-radius: 5px;
    color: var(--color-white);
}

[draggable] {
    -moz-user-select: none;
    -khtml-user-select: none;
    -webkit-user-select: none;
    user-select: none;
    -khtml-user-drag: element;
    -webkit-user-drag: element;
}

ul.list {
    list-style-type: none;
    padding: 0;
    max-width: 300px;
}

.list  button {
    background: var(--color-black);
}

.list--item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: auto;
    margin: 5px auto;
    padding: 5px;
    cursor: move;
    background: var(--color-gray);
    border-radius: 5px;
}

.list--item.draggingElement {
    opacity: 0.4;
}

.list--item.over {
    border-top: 3px solid var(--color-green);
}

button.remove {
    margin: auto 0 auto auto;
}

input#id {
    margin: auto 5px auto 0;
}

.button-wrapper {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    max-width: 300px;
}

.button-wrapper .addItemInput { 
    width: 63%;
    border-radius: 5px 0 0 5px;
 }
.button-wrapper .addItemButton { 
    width: 35%;
    border-radius: 0 5px 5px 0;
 }
.button-wrapper .generateCode { 
    width: 100%;
    background: var(--color-green);
    margin-top: 5px;
 }


.code p {
    background: var(--color-gray); padding: 5px;
    border: 1px solid var(--color-gray-2);
    min-height: 20px;
    font-weight: 300;
}
  <ul class="list">
    <li><span>Header</span></li>
    <li><span>Hero</span></li>
    <li><span>Intro</span></li>
    <li><span>Footer</span></li>
  </ul>

  <div class="button-wrapper">
    <input type="text" class="addItemInput" placeholder="Item description">
    <button class="addItemButton">Add item</button>
    <button class="generateCode">Generate code</button>
  </div>

  <div class="code">
    <h2>Code</h2>
    <p></p>
  </div>

Ответы [ 3 ]

0 голосов
/ 01 ноября 2018

Проблема в том, что const listCopy = listUl.querySelectorAll('span'); инициируется массивом span с самого начала.

Чтобы получить обновленный список const listCopy = listUl.querySelectorAll('span'); будет let listCopy = listUl.querySelectorAll('span'); и в вашей функции

//Cloning code if there are checked items
copyText.addEventListener('click', () => {
  // add the following line - in this way you will select the span from the updated list
  listCopy = listUl.querySelectorAll('span');
  let copyTextFromList = "";
  for (let i = 0; i < listCopy.length; i += 1) {
    if (listCopy[i].parentNode.querySelector("input:checked")) {
      copyTextFromList += listCopy[i].textContent + ',';
    }
  }
  clonedCode.innerHTML = copyTextFromList;
});
0 голосов
/ 01 ноября 2018

, если вы хотите использовать querySelectorAll, это может помочь. при этом каждый раз, когда вы проверяете длину, он пересчитывает и возвращает значение. Symbol.iterator поможет вам манипулировать для ... циклов.

const addItemInput = document.querySelector('.addItemInput');
const addItemButton = document.querySelector('.addItemButton');
const copyText = document.querySelector('.generateCode');
const listUl = document.querySelector('.list');
const list = listUl.children;
const listCopy = {
    get length() {
        return listUl.querySelectorAll('span').length
    },
    *[Symbol.iterator]() {
        let i = 0;
        const l = listUl.querySelectorAll('span');
        while( i < l.length ) {
            yield l[i];
            i++;
        }
    }
};
const clonedCode = document.querySelector('.code p');

//FUNCTION: Generate value/items = Draggable, Checkbox, Remove button
const attachItemListButton = (item) => {

    //Draggable
    item.draggable = "true";
    item.classList.add("list--item");

    //Checkbox
    let checkbox = document.createElement('input');
    checkbox.type = 'checkbox';
    checkbox.className = 'checkbox';
    checkbox.name = "chkboxName1";
    checkbox.value = "value";
    checkbox.id = "id";
    item.insertBefore(checkbox, item.childNodes[0] || null);

    //Remove button
    let remove = document.createElement('button');
    remove.className = 'remove';
    remove.textContent = 'x';
    item.appendChild(remove);
};
for (let i = 0; i < list.length; i += 1) {
    attachItemListButton(list[i]);
}


//Cloning code if there are checked items
copyText.addEventListener('click', () => {
  let copyTextFromList = "";
  for (let item of listCopy) {
    if (item.parentNode.querySelector("input:checked")) {
      copyTextFromList += item.textContent + ',';
    }
  }
  clonedCode.innerHTML = copyTextFromList;
});


//Add item from the input field to the list
addItemButton.addEventListener('click', () => {
  let li = document.createElement('li');
  let span = document.createElement('span');
  span.textContent = addItemInput.value;
  listUl.appendChild(li);
  li.appendChild(span);
  attachItemListButton(li);
  addItemInput.value = '';
});


//FUNCTION: Remove button
listUl.addEventListener('click', (event) => {
    if (event.target.tagName == 'BUTTON') {
        if (event.target.className == 'remove') {
            let li = event.target.parentNode;
            let ul = li.parentNode;
            ul.removeChild(li);
        }
    }
});
/* Google fonts */
@import url('https://fonts.googleapis.com/css?family=Heebo:300,400,700');

/* Root */
:root {
    --color-white:           #fff;
    --color-black:           #2D3142;
    --color-black-2:         #0E1116;
    --color-gray:            #CEE5F2;
    --color-gray-2:          #ACCBE1;
    --color-gray-3:          #CEE5F2;
    --color-green:           #439775;
    --color-blue:           #4686CC;
}

body {
    font-family: 'Heebo', sans-serif;
    font-weight: 400;
    font-size: 16px;
    color: black;

}
h2 {
    font-weight: 700;
    font-size: 1.5rem;
}
h3 {
    font-weight: 700;
    font-size: 1.25rem;
}

button {
    background: var(--color-blue);
    padding: 5px 10px;
    border-radius: 5px;
    color: var(--color-white);
}

[draggable] {
    -moz-user-select: none;
    -khtml-user-select: none;
    -webkit-user-select: none;
    user-select: none;
    -khtml-user-drag: element;
    -webkit-user-drag: element;
}

ul.list {
    list-style-type: none;
    padding: 0;
    max-width: 300px;
}

.list  button {
    background: var(--color-black);
}

.list--item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: auto;
    margin: 5px auto;
    padding: 5px;
    cursor: move;
    background: var(--color-gray);
    border-radius: 5px;
}

.list--item.draggingElement {
    opacity: 0.4;
}

.list--item.over {
    border-top: 3px solid var(--color-green);
}

button.remove {
    margin: auto 0 auto auto;
}

input#id {
    margin: auto 5px auto 0;
}

.button-wrapper {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    max-width: 300px;
}

.button-wrapper .addItemInput { 
    width: 63%;
    border-radius: 5px 0 0 5px;
 }
.button-wrapper .addItemButton { 
    width: 35%;
    border-radius: 0 5px 5px 0;
 }
.button-wrapper .generateCode { 
    width: 100%;
    background: var(--color-green);
    margin-top: 5px;
 }


.code p {
    background: var(--color-gray); padding: 5px;
    border: 1px solid var(--color-gray-2);
    min-height: 20px;
    font-weight: 300;
}
  <ul class="list">
    <li><span>Header</span></li>
    <li><span>Hero</span></li>
    <li><span>Intro</span></li>
    <li><span>Footer</span></li>
  </ul>

  <div class="button-wrapper">
    <input type="text" class="addItemInput" placeholder="Item description">
    <button class="addItemButton">Add item</button>
    <button class="generateCode">Generate code</button>
  </div>

  <div class="code">
    <h2>Code</h2>
    <p></p>
  </div>
0 голосов
/ 01 ноября 2018

Существует два варианта NodeList, живой и неживой. querySelectorAll возвращает статический NodeList, который не является живым. .children возвращает живой (технически он возвращает HTMLCollection, но вы можете пока игнорировать это различие).

Чтобы сделать listCopy также живым, вы можете использовать listUl.getElementsByTagName('span')

Чтобы выбрать элементы по их классам, используйте getElementsByClassName. Однако нет способа (о котором я знаю) получить живую коллекцию с помощью запросов CSS или XPath (то есть более сложных).

...