Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Изменение поведения выбранных чекбоксов (https://javascript.ru/forum/misc/84220-izmenenie-povedeniya-vybrannykh-chekboksov.html)

NovichokJS 10.07.2022 13:48

Изменение поведения выбранных чекбоксов
 
У меня есть список ul с чекбоксами в каждом li.

<ul class="list">
<li class="list__item"><input type="checkbox" class="list__item-checkbox">Buy milk</li>
<li class="list__item"><input type="checkbox" class="list__item-checkbox">Pick up Tom from airport</li>
<li class="list__item"><input type="checkbox" class="list__item-checkbox">Visit party</li>
</ul>


Мне нужно изменить поведения каждого li элемента при клике на соответствующий чекбокс. Делаю таким образом и у меня добавляется класс 'list__item_done' только к первому элементу li. При клике на другие чекбоксы класс 'list__item_done' не назначается. Как это исправить?

const chbx = document.querySelector('.list__item-checkbox');
const li = document.querySelector('.list__item');

chbx.addEventListener('click', () => {
  if (chbx.checked) {
    li.classList.add('list__item_done');
  } else {
    li.classList.remove('list__item_done');
  }
});

рони 10.07.2022 13:59

Цитата:

Сообщение от NovichokJS
Как это исправить?

прочитать ответы на предыдущую тему или сделать цикл по всем li

NovichokJS 10.07.2022 14:10

Цитата:

Сообщение от рони (Сообщение 546569)
прочитать ответы на предыдущую тему или сделать цикл по всем li

const chbx = document.querySelector('.list__item-checkbox');
const li = document.querySelectorAll('.list__item');

chbx.addEventListener('click', () => {
  for (let i = 0; i < li.length; i++) {
    if (chbx.checked) {
      li.classList.add('list__item_done');
    } else {
      li.classList.remove('list__item_done');
    }
  }
});


Ошибка: cannot reat property undefined (reading 'add')

рони 10.07.2022 14:16

NovichokJS,
:-? :-? :-?

<!DOCTYPE html>
<html>

<head>
    <title>Untitled</title>
    <meta charset="utf-8">
    <style type="text/css">
        ul {
            list-style: none;
            width: 240px;
        }

        .list__item_done {
            background-image: -webkit-linear-gradient(left, #0000CD, #FF0000);
            background-image: linear-gradient(to right, #0000CD, #FF0000);
            color: #FFFFFF;
        }
    </style>
    <script>
        /* делегирование */
        document.addEventListener("DOMContentLoaded", function() {
            let ul = document.querySelector(".list");
            ul.addEventListener("change", function({
                target
            }) {
                target.closest("li").classList.toggle("list__item_done", target.checked)
            })
        })
        /* цикл */
       /* document.addEventListener("DOMContentLoaded", function() {
            document.querySelectorAll(".list li").forEach(li =>
            li.addEventListener("change", function({
                target
            }) {
                li.classList.toggle("list__item_done", target.checked)
            }))
        })*/
    </script>
</head>

<body>
    <ul class="list">
        <li class="list__item"><input type="checkbox" class="list__item-checkbox">Buy milk</li>
        <li class="list__item"><input type="checkbox" class="list__item-checkbox">Pick up Tom from airport</li>
        <li class="list__item"><input type="checkbox" class="list__item-checkbox">Visit party</li>
    </ul>
</body>

</html>

рони 10.07.2022 14:21

NovichokJS,
<!DOCTYPE html>
<html>

<head>
    <title>Untitled</title>
    <meta charset="utf-8">
    <style type="text/css">
        ul {
            list-style: none;
            width: 240px;
        }

        .list__item_done {
            background-image: -webkit-linear-gradient(left, #0000CD, #FF0000);
            background-image: linear-gradient(to right, #0000CD, #FF0000);
            color: #FFFFFF;
        }
    </style>
    <script>
        document.addEventListener("DOMContentLoaded", function() {
            const chbx = document.querySelector('.list');
            const lis = chbx.querySelectorAll('.list__item');

            chbx.addEventListener('click', () => {
                for (let i = 0; i < lis.length; i++) {
                    let li = lis[i];
                    if (li.querySelector(":checked")) {
                        li.classList.add('list__item_done');
                    } else {
                        li.classList.remove('list__item_done');
                    }
                }
            });

        })
    </script>
</head>

<body>
    <ul class="list">
        <li class="list__item"><input type="checkbox" class="list__item-checkbox">Buy milk</li>
        <li class="list__item"><input type="checkbox" class="list__item-checkbox">Pick up Tom from airport</li>
        <li class="list__item"><input type="checkbox" class="list__item-checkbox">Visit party</li>
    </ul>
</body>

</html>

рони 10.07.2022 14:24

NovichokJS,
<!DOCTYPE html>
<html>
<head>
    <title>Untitled</title>
    <meta charset="utf-8">
    <style type="text/css">
        ul {
            list-style: none;
            width: 240px;
        }
        .list__item_done {
            background-image: -webkit-linear-gradient(left, #0000CD, #FF0000);
            background-image: linear-gradient(to right, #0000CD, #FF0000);
            color: #FFFFFF;
        }
    </style>
    <script>
        document.addEventListener("DOMContentLoaded", function() {
            const chbx = document.querySelector('.list');
            const lis = chbx.querySelectorAll('.list__item');
            for (let li of lis) {
                li.addEventListener('click', () => {
                    if (li.querySelector(":checked")) {
                        li.classList.add('list__item_done');
                    } else {
                        li.classList.remove('list__item_done');
                    }
                })
            };
        })
    </script>
</head>
<body>
    <ul class="list">
        <li class="list__item"><input type="checkbox" class="list__item-checkbox">Buy milk</li>
        <li class="list__item"><input type="checkbox" class="list__item-checkbox">Pick up Tom from airport</li>
        <li class="list__item"><input type="checkbox" class="list__item-checkbox">Visit party</li>
    </ul>
</body>
</html>

NovichokJS 10.07.2022 14:38

Рони, спасибо. А эта обёртка обязательна в данном случае? - document.addEventListener("DOMContentLoaded", function() {
такого даже не учил еще. Без нее также отработает код

рони 10.07.2022 14:54

Цитата:

Сообщение от NovichokJS
А эта обёртка обязательна в данном случае?

скрипт можно вставить в любое место страницы, без DOMContentLoaded только после элементов с которыми скрипт работает.

NovichokJS 10.07.2022 14:59

ясно. А как отсортировать выполненные(перечеркнутые таски)? чтобы те, которые чекаем спускались в самый вниз. Понимаю что надо наверное как-то сортануть массив lis, вызвав метод sort(), но как именно что и куда вставить не получается у меня

рони 10.07.2022 15:03

NovichokJS,
<!DOCTYPE html>
<html>

<head>
    <title>Untitled</title>
    <meta charset="utf-8">
    <script>
        alert(document.body ? "тело страницы создано" : "тело страницы отсутствует");
    </script>
</head>

<body>
    <script>
        alert(document.body ? "тело страницы создано" : "тело страницы отсутствует");
    </script>
</body>

</html>

рони 10.07.2022 15:05

Цитата:

Сообщение от NovichokJS
которые чекаем спускались в самый вниз.

append and prepend

NovichokJS 10.07.2022 15:09

Цитата:

Сообщение от рони (Сообщение 546577)
NovichokJS,
<!DOCTYPE html>
<html>

<head>
    <title>Untitled</title>
    <meta charset="utf-8">
    <script>
        alert(document.body ? "тело страницы создано" : "тело страницы отсутствует");
    </script>
</head>

<body>
    <script>
        alert(document.body ? "тело страницы создано" : "тело страницы отсутствует");
    </script>
</body>

</html>

это что?)

NovichokJS 10.07.2022 15:18

Цитата:

Сообщение от рони (Сообщение 546578)
append and prepend

спасибо, понял ulElement.append(li);

рони 10.07.2022 15:20

NovichokJS,
<!DOCTYPE html>
<html>

<head>
    <title>Untitled</title>
    <meta charset="utf-8">
    <style type="text/css">
        ul {
            list-style: none;
            width: 240px;
        }

        .list__item_done {
            background-image: -webkit-linear-gradient(left, #0000CD, #FF0000);
            background-image: linear-gradient(to right, #0000CD, #FF0000);
            color: #FFFFFF;
        }
    </style>
    <script>
        document.addEventListener("DOMContentLoaded", function() {
            const ul = document.querySelector('.list');
            const lis = Array.from(ul.querySelectorAll('.list__item'));
            ul.addEventListener('click', () => {
                ul.append(...lis);
                for (let li of lis) {
                    if (li.querySelector(":checked")) {
                        li.classList.add("list__item_done");
                        ul.append(li)
                    } else {
                        li.classList.remove("list__item_done")
                    }
                }
            });
        })
    </script>
</head>

<body>
    <ul class="list">
        <li class="list__item"><input type="checkbox" class="list__item-checkbox">Buy milk</li>
        <li class="list__item"><input type="checkbox" class="list__item-checkbox">Pick up Tom from airport</li>
        <li class="list__item"><input type="checkbox" class="list__item-checkbox">Visit party</li>
    </ul>
</body>

</html>

рони 10.07.2022 15:25

:write: :lol:
<!DOCTYPE html>
<html>

<head>
    <title>Untitled</title>
    <meta charset="utf-8">
    <style type="text/css">
        ul {
            list-style: none;
            width: 240px;
        }

        .list__item_done {
            background-image: -webkit-linear-gradient(left, #0000CD, #FF0000);
            background-image: linear-gradient(to right, #0000CD, #FF0000);
            color: #FFFFFF;
        }
    </style>
    <script>
        document.addEventListener("DOMContentLoaded", function() {
            const ul = document.querySelector('.list');
            const lis = Array.from(ul.querySelectorAll('.list__item'));
            ul.addEventListener('click', () => {
                const before = [], after = [];
                for (let li of lis) {
                    if (li.querySelector(":checked")) {
                        li.classList.add("list__item_done");
                        after.push(li)
                    } else {
                        li.classList.remove("list__item_done");
                        before.push(li)
                    }
                };

                ul.append(...before, ...after)
            });
        })
    </script>
</head>

<body>
    <ul class="list">
        <li class="list__item"><input type="checkbox" class="list__item-checkbox">Buy milk</li>
        <li class="list__item"><input type="checkbox" class="list__item-checkbox">Pick up Tom from airport</li>
        <li class="list__item"><input type="checkbox" class="list__item-checkbox">Visit party</li>
    </ul>
</body>

</html>

рони 10.07.2022 15:27

Цитата:

Сообщение от NovichokJS
это что?)

это была демонстрация, что если бы скрипт работал с document.body, то находясь выше document.body скрипт выдал бы ошибку.

NovichokJS 10.07.2022 15:34

Цитата:

Сообщение от рони (Сообщение 546583)
это была демонстрация, что если бы скрипт работал с document.body, то находясь выше document.body скрипт выдал бы ошибку.

да, да, понятно)

NovichokJS 10.07.2022 15:41

еще такой вот момент))

я написал костыль выше в коде у себя.
Ну собственно сам массив задач:
const tasks = [
  { text: 'Buy milk', done: false },
  { text: 'Pick up Tom from airport', done: false },
  { text: 'Visit party', done: false },
  { text: 'Visit doctor', done: true },
  { text: 'Buy meat', done: true },
];


И костыль, с помощью которого добавляю таски:

const handlerButton = () => {
  const getTask = document.querySelector('.task-input');
  if (getTask.value !== '') {
    tasks.push({ text: getTask.value, done: false });

    const getListElem = document.querySelector('.list');

    const getListItemElem = document.createElement('li');
    getListItemElem.classList.add('list__item');
    getListItemElem.innerText = getTask.value;

    const getCheckboxElem = document.createElement('input');
    getCheckboxElem.setAttribute('type', 'checkbox');
    getCheckboxElem.classList.add('list__item-checkbox');

    getListElem.prepend(getListItemElem);
    getListItemElem.prepend(getCheckboxElem);
    getTask.value = '';
  }
};

clickButton.addEventListener('click', handlerButton);


clickButton.addEventListener('click', handlerButton);

const ulElement = document.querySelector('.list');
const lis = ulElement.querySelectorAll('.list__item');

const handlerChexbox = () => {
  for (let i = 0; i < lis.length; i++) {
    let li = lis[i];
    if (li.querySelector(':checked')) {
      li.classList.add('list__item_done');
      ulElement.append(li);
    } else {
      li.classList.remove('list__item_done');
    }
  }
};
ulElement.addEventListener('click', handlerChexbox);


Оно работает, но не полностью. Мне надо чтобы добавленные таски также при отметке чекбокса спускались вниз. Сейчас они не добавляют класс list__item_done и не спускаются у меня.



Собственно, как и куда привязать блоки кода чтобы работало опускание отмеченных чекбоксом тасков вниз?

рони 10.07.2022 17:05

Цитата:

Сообщение от NovichokJS
Собственно, как и куда привязать блоки кода чтобы работало опускание отмеченных чекбоксом тасков вниз?

код выше #15

NovichokJS 10.07.2022 17:29

Цитата:

Сообщение от рони (Сообщение 546587)
код выше #15

работает немного криво. Если несколько кликов сделать на чекбок, только тогда отмечает его, подчеркивает текст и опускает вниз таски. Вот итоговый:

const tasks = [
  { text: 'Buy milk', done: false },
  { text: 'Pick up Tom from airport', done: false },
  { text: 'Visit party', done: false },
  { text: 'Visit doctor', done: true },
  { text: 'Buy meat', done: true },
];

const listElem = document.querySelector('.list');
const clickButton = document.querySelector('.create-task-btn');

const handlerButton = () => {
  const getTask = document.querySelector('.task-input');
  if (getTask.value !== '') {
    tasks.push({ text: getTask.value, done: false });

    console.log(tasks);

    const getListItemElem = document.createElement('li');
    getListItemElem.classList.add('list__item');
    getListItemElem.innerText = getTask.value;

    const getCheckboxElem = document.createElement('input');
    getCheckboxElem.setAttribute('type', 'checkbox');
    getCheckboxElem.classList.add('list__item-checkbox');

    listElem.prepend(getListItemElem);
    getListItemElem.prepend(getCheckboxElem);
    getTask.value = '';
  }
};

clickButton.addEventListener('click', handlerButton);

const handlerChexbox = () => {
  const lis = Array.from(listElem.querySelectorAll('.list__item'));
  listElem.addEventListener('click', () => {
    const before = [],
      after = [];
    for (let li of lis) {
      if (li.querySelector(':checked')) {
        li.classList.add('list__item_done');
        after.push(li);
      } else {
        li.classList.remove('list__item_done');
        before.push(li);
      }
    }

    listElem.append(...before, ...after);
  });
};
listElem.addEventListener('click', handlerChexbox);

NovichokJS 10.07.2022 17:32

и это мне понравилось еще :lol: :

NovichokJS
Кандидат Javascript-наук
Карма: +12

NovichokJS 10.07.2022 18:11

Рони, у меня уже получилось всё синхронизировать и теперь работает как надо, спасибо!!!

рони 10.07.2022 18:30

NovichokJS,
делайте полноценные примеры, и менять надо только состояние const tasks, остальное всё лучше перенести в render

NovichokJS 10.07.2022 22:27

Цитата:

Сообщение от рони (Сообщение 546591)
NovichokJS,
делайте полноценные примеры, и менять надо только состояние const tasks, остальное всё лучше перенести в render

а что вы имеете ввиду "полноценные примеры"?

касательно этого "менять надо только состояние const tasks, остальное всё лучше перенести в render" - помогите пожалуйста.

Сейчас функционально то работает. Но понятно, что нужно улучшить код, рефакторинг...Вот весь итоговый код:

<body>
    <h1 class="title">Todo List</h1>
    <main class="todo-list">
      <div class="actions">
        <input class="task-input" type="text" />
        <button class="btn create-task-btn">Create</button>
      </div>
      <ul class="list"></ul>
    </main>
    <script src="index.js"></script>
  </body>


const tasks = [
  { text: 'Buy milk', done: false },
  { text: 'Pick up Tom from airport', done: false },
  { text: 'Visit party', done: false },
  { text: 'Visit doctor', done: true },
  { text: 'Buy meat', done: true },
];

const listElem = document.querySelector('.list');

const renderTasks = tasksList => {
  const listItemsElems = tasksList
    .sort((a, b) => a.done - b.done)
    .map(({ text, done }) => {
      const listItemElem = document.createElement('li');
      listItemElem.classList.add('list__item');
      if (done) {
        listItemElem.classList.add('list__item_done');
      }

      const checkboxElem = document.createElement('input');
      checkboxElem.setAttribute('type', 'checkbox');
      checkboxElem.checked = done;
      checkboxElem.classList.add('list__item-checkbox');

      listItemElem.append(checkboxElem, text);

      return listItemElem;
    });

  listElem.append(...listItemsElems);
};

renderTasks(tasks);

const clickButton = document.querySelector('.create-task-btn');

const handlerButton = () => {
  const getTask = document.querySelector('.task-input');
  if (getTask.value !== '') {
    tasks.push({ text: getTask.value, done: false });

    const getListItemElem = document.createElement('li');
    getListItemElem.classList.add('list__item');
    getListItemElem.innerText = getTask.value;

    const getCheckboxElem = document.createElement('input');
    getCheckboxElem.setAttribute('type', 'checkbox');
    getCheckboxElem.classList.add('list__item-checkbox');

    listElem.prepend(getListItemElem);
    getListItemElem.prepend(getCheckboxElem);
    getTask.value = '';
  }
};

clickButton.addEventListener('click', handlerButton);

const handlerChexbox = () => {
  const lis = Array.from(listElem.querySelectorAll('.list__item'));
  const before = [],
    after = [];
  for (let li of lis) {
    if (li.querySelector(':checked')) {
      li.classList.add('list__item_done');
      after.push(li);
    } else {
      li.classList.remove('list__item_done');
      before.push(li);
    }
  }

  listElem.append(...before, ...after);
};
listElem.addEventListener('click', handlerChexbox);


Как привести к такому порядку:
при добавлении / изменении задачи - сначало добавить бы / обновить соответствующий элемент в массиве, где хранятся таски. После этого, заново отрисовать весь список в соответствии с обновленным массивом - вызов функции render.

рони 10.07.2022 23:41

NovichokJS,
<!DOCTYPE html>
<html>
<head>
    <title>Untitled</title>
    <meta charset="utf-8">
</head>
<body>
    <h1 class="title">Todo List</h1>
    <main class="todo-list">
        <div class="actions">
            <input class="task-input" type="text" />
            <button class="btn create-task-btn">Create</button>
        </div>
        <ul class="list"></ul>
    </main>
    <script>
        const tasks = [{
                text: 'Buy milk',
                done: false
            },
            {
                text: 'Pick up Tom from airport',
                done: false
            },
            {
                text: 'Visit party',
                done: false
            },
            {
                text: 'Visit doctor',
                done: true
            },
            {
                text: 'Buy meat',
                done: true
            },
        ];
        const listElem = document.querySelector('.list');
        const renderTasks = tasksList => {
            const listItemsElems = tasksList
                .sort((a, b) => a.done - b.done || (a.text > b.text ? 1 : -1))
                .map(({
                    text,
                    done
                }, i) => {
                    const listItemElem = document.createElement('li');
                    listItemElem.classList.add('list__item');
                    if (done) {
                        listItemElem.classList.add('list__item_done');
                    }
                    const checkboxElem = document.createElement('input');
                    checkboxElem.setAttribute('type', 'checkbox');
                    checkboxElem.checked = done;
                    checkboxElem.dataset.i = i;
                    checkboxElem.classList.add('list__item-checkbox');
                    listItemElem.append(checkboxElem, text);
                    return listItemElem;
                });
            listElem.innerHTML = '';
            listElem.append(...listItemsElems);
        };
        renderTasks(tasks);
        const clickButton = document.querySelector('.create-task-btn');
        const handlerButton = () => {
            const getTask = document.querySelector('.task-input');
            let text = getTask.value.trim();
            if (text) {
                tasks.push({
                    text,
                    done: false
                });
                getTask.value = '';
                renderTasks(tasks);
            }
        };
        clickButton.addEventListener('click', handlerButton);
        const handlerChexbox = ({
            target: {
                dataset: {
                    i
                }
            }
        }) => {
            tasks[i].done = !tasks[i].done;
            renderTasks(tasks);
        };
        listElem.addEventListener('change', handlerChexbox);
    </script>
</body>
</html>

NovichokJS 11.07.2022 10:41

спасибо, круто!

NovichokJS 11.07.2022 10:57

правда вот только не понял вообще что тут происходит:

const handlerChexbox = ({

            target: {

                dataset: {

                    i

                }

            }

        }) => {

            tasks[i].done = !tasks[i].done;

            renderTasks(tasks);

        };

рони 11.07.2022 11:17

NovichokJS,
при формировании checkbox записывается его индекс в dataset. строка 54
checkboxElem.dataset.i = i;

когда происходит изменение этого элемента в listElem.
строка 84
listElem.addEventListener('change', handlerChexbox);


этот индекс извлекается из dataset, находится элемент массива (объект) с таким же индексом и в этом объекте изменяется свойство done, на противоположное, было false, станет true и наоборот.

рони 11.07.2022 11:24

NovichokJS,
тоже самое ...
const handlerChexbox = (event) => {
            let target = event.target;
            let i = target.dataset.i;
            let obj = tasks[i];
            if(obj.done === true) obj.done = false;
            else obj.done = true;
            renderTasks(tasks);
        };

NovichokJS 11.07.2022 11:37

теперь понял, спасибо!

NovichokJS 11.07.2022 12:38

последнее, хотел бы такой момент еще пофиксить - в dataset.i получаю сейчас строку, а в массиве ищу по индексу (число). Как это исправить?

рони 11.07.2022 12:58

Цитата:

Сообщение от NovichokJS
Как это исправить?

зачем?

NovichokJS 11.07.2022 13:04

Цитата:

Сообщение от рони (Сообщение 546603)
зачем?

тест не проходится у меня

рони 11.07.2022 14:11

Цитата:

Сообщение от NovichokJS
тест не проходится у меня

микроскопа жаль нету)))
но есть тест
let i = "3";
alert([{},{},{},{done: 45}][i].done);

рони 11.07.2022 14:14

NovichokJS,
что не так?
<!DOCTYPE html>
<html>
<head>
    <title>Untitled</title>
    <meta charset="utf-8">
</head>
<body>
    <h1 class="title">Todo List</h1>
    <main class="todo-list">
        <div class="actions">
            <input class="task-input" type="text" />
            <button class="btn create-task-btn">Create</button>
        </div>
        <ul class="list"></ul>
    </main>
    <script>
        const tasks = [{
                text: 'Buy milk',
                done: false
            },
            {
                text: 'Pick up Tom from airport',
                done: false
            },
            {
                text: 'Visit party',
                done: false
            },
            {
                text: 'Visit doctor',
                done: true
            },
            {
                text: 'Buy meat',
                done: true
            },
        ];
        const listElem = document.querySelector('.list');
        const renderTasks = tasksList => {
            const listItemsElems = tasksList
                .sort((a, b) => a.done - b.done || (a.text > b.text ? 1 : -1))
                .map(({
                    text,
                    done
                }, i) => {
                    const listItemElem = document.createElement('li');
                    listItemElem.classList.add('list__item');
                    if (done) {
                        listItemElem.classList.add('list__item_done');
                    }
                    const checkboxElem = document.createElement('input');
                    checkboxElem.setAttribute('type', 'checkbox');
                    checkboxElem.checked = done;
                    checkboxElem.dataset.i = i;
                    checkboxElem.classList.add('list__item-checkbox');
                    listItemElem.append(checkboxElem, text);
                    return listItemElem;
                });
            listElem.innerHTML = '';
            listElem.append(...listItemsElems);
        };
        renderTasks(tasks);
        const clickButton = document.querySelector('.create-task-btn');
        const handlerButton = () => {
            const getTask = document.querySelector('.task-input');
            let text = getTask.value.trim();
            if (text) {
                tasks.push({
                    text,
                    done: false
                });
                getTask.value = '';
                renderTasks(tasks);
            }
        };
        clickButton.addEventListener('click', handlerButton);
        const handlerChexbox = (event) => {
            let target = event.target;
            let i = target.dataset.i;
            let obj = tasks[i];
            if(obj.done === true) obj.done = false;
            else obj.done = true;
            renderTasks(tasks);
        };
        listElem.addEventListener('change', handlerChexbox);
    </script>
</body>
</html>


Часовой пояс GMT +3, время: 00:31.