Как работать с localStorage
Пытаюсь создать подобие чек-листа. Не могу разобраться с localstorage. Ответил на собственный вопрос(см.ниже). Но возникла проблема при перезагрузке страницы сохраненные данные невозможно отредактировать. Можно ли решить эту проблему? Или придется другим способом реализовать чек-лист?
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Список дел</title>
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div class="page">
<div class="container">
<h2>Список дел</h2>
<div class="main">
<input id="add" type="text" placeholder="Введите описание вашего дела...">
<button id="button_add">Добавить</button>
</div>
<hr><ul id="check_list"></ul>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
#deleteTask {
width: 18px;
height: 18px;
opacity: 0.3;
margin-left: 10px;
}
#deleteTask:hover {
opacity: 1;
cursor: pointer;
}
#deleteTask:before, #deleteTask:after {
position: absolute;
content: ' ';
height: 15px;
width: 2px;
background-color: #333;
}
#deleteTask:before {
transform: rotate(45deg);
}
#deleteTask:after {
transform: rotate(-45deg);
}
.done span {
text-decoration: line-through;
color: gray;
}
let buttonAdd = document.getElementById('button_add');
let checklist = document.getElementById('check_list');
let add = document.getElementById('add');
buttonAdd.addEventListener('click', function(){
if (add.value === ''){
return alert('Ошибка: Пустая строка');
}
let li = document.createElement('li');
let newTask = li.innerHTML =`
<span id = 'newTask' data-elem = 'text'>` + add.value + `</span>
<input id = 'checkTask' data-elem = 'checkbox' type = 'checkbox'>
<span id = 'deleteTask' data-elem = 'remove'></span>`;
add.value = '';
checklist.appendChild(li);
li.addEventListener('click', function(event) {
if (event.target.dataset.elem) {
switch (event.target.dataset.elem) {
case 'text':
let span = event.target;
let input = document.createElement('input');
input.value = event.target.innerHTML;
span.innerHTML = '';
span.appendChild(input);
input.addEventListener('keydown', function(e) {
if (e.keyCode === 13) {
span.innerHTML = this.value;
};
});
break;
case 'checkbox':
li.classList.toggle('done');
break;
case 'remove':
li.parentElement.removeChild(li);
event.preventDefault();
break;
};
};
});
});
Ответы (3 шт):
Сделал чтобы вам понять примерный принцип действия:
let buttonAdd = document.getElementById('button_add');
let checklist = document.getElementById('check_list');
let add = document.getElementById('add');
const tasks = localStorage.getItem('data') ? JSON.parse(localStorage.getItem('data')) : []
console.log(tasks)
tasks.forEach((item) => {
let li = document.createElement('li');
li.innerHTML = item
checklist.appendChild(li);
})
const saveTasks = () => {
localStorage.setItem('data', JSON.stringify(tasks));
}
buttonAdd.addEventListener('click', function(){
if (add.value === ''){
return alert('Ошибка: Пустая строка');
}
let li = document.createElement('li');
let newTask = li.innerHTML =`
<span id = 'newTask' data-elem = 'text'>` + add.value + `</span>
<input id = 'checkTask' data-elem = 'checkbox' type = 'checkbox'>
<span id = 'deleteTask' data-elem = 'remove'></span>`;
add.value = '';
const taskId = tasks.push(newTask)
saveTasks()
checklist.appendChild(li);
li.addEventListener('click', function(event) {
if (event.target.dataset.elem) {
switch (event.target.dataset.elem) {
case 'text':
let span = event.target;
let input = document.createElement('input');
input.value = event.target.innerHTML;
span.innerHTML = '';
span.appendChild(input);
input.addEventListener('keydown', function(e) {
if (e.keyCode === 13) {
span.innerHTML = this.value;
};
});
break;
case 'checkbox':
li.classList.toggle('done');
break;
case 'remove':
tasks.splice(newTask, 1)
saveTasks()
li.parentElement.removeChild(li);
event.preventDefault();
break;
};
};
});
});
Хотя по-хорошему тут надо весь код переписывать. В частности список должен быть сделан в виде массива объектов, а при добавлении в массив и удалении - список нужно перерисовывать.
Собственно получилось сохранить. Но при перезагрузке страницы. Редактирование сохраненной строки не представляется возможным. Можно ли это исправить?
let buttonAdd = document.getElementById('button_add');
let checklist = document.getElementById('check_list');
let add = document.getElementById('add');
let todo;
function toLocal(){
todo = checklist.innerHTML;
localStorage.setItem('todo', todo);
}
buttonAdd.addEventListener('click', function newElem(){
if (add.value === ''){
return alert('Ошибка: Пустая строка');
}
let li = document.createElement('li');
let newTask = li.innerHTML =`
<span id = 'newTask' data-elem = 'text'>` + add.value + `</span>
<input id = 'checkTask' data-elem = 'checkbox' type = 'checkbox'>
<span id = 'deleteTask' data-elem = 'remove'></span>`;
add.value = '';
checklist.appendChild(li);
li.addEventListener('click', function(event) {
if (event.target.dataset.elem) {
switch (event.target.dataset.elem) {
case 'text':
let span = event.target;
let input = document.createElement('input');
input.value = event.target.innerHTML;
span.innerHTML = '';
span.appendChild(input);
input.addEventListener('keydown', function(e) {
if (e.keyCode === 13) {
span.innerHTML = this.value;
};
});
toLocal();
break;
case 'checkbox':
li.classList.toggle('done');
toLocal();
break;
case 'remove':
li.parentElement.removeChild(li);
event.preventDefault();
toLocal();
break;
};
};
});
});
if(localStorage.getItem('todo')) {
checklist.innerHTML = localStorage.getItem("todo");
};
Хотел добавить пару строчек, случайно получилось 150+ )
У каждой записи должен быть свой уникальный идентификатор. В таком случае все записи можно хранить в объекте:
{
"0": { text: 'запись-1' },
"1": { text: 'запись-2' },
"8": { text: 'запись-8', checked: 1 },
}
Состояние checked тоже придется хранить, чтобы после обновления страницы вернуть все галочки на свои места. А т.к. в localStorage можно хранить только строки, для сохранения объектов необходимо использовать JSON.stringify(obj), а для чтения - JSON.parse(string)
+ Вы и так использовали делегирование событий, но отдельно для каждого <li>. Заодно вынес обработчики на родительский <ul>: При добавлении / удалении записей не придется вешать новые обработчики.
Приставки storage, item намекают, что здесь напрашивается группирование в объекты или классы. Но пока обойдемся разбросанными функциями.
* Код рабочий, но localStorage не работает при запуске на этом сайте. Пробуйте здесь
const storage_KEY = 'check_list';
let storage_items = (function get_saved_data() {
let items = localStorage.getItem(storage_KEY);
try {
let data = JSON.parse(items);
if (!data) throw new Error('no data');
if (typeof data != 'object') {
throw new Error('Expected Object, but found: ' + data);
}
return data;
// Если благополучно получилось прочесть JSON, в переменную storage_items
// попадет этот объект.
} catch(e) {
console.log(e);
return {}; // иначе, новый пустой {}
}
})();
console.log('%cITEMS:', 'color: orange', storage_items);
// Любое действие со storage, выполняет операции в объекте storage_items,
// затем вызывает storage_update(), который записывает объект в localStorage
function storage_update() {
//test_start
// Временный блок, чтобы поймать ошибки во время написания,
// если вдруг прилетит id-не число.
if (Object.keys(storage_items).some(isNaN)) {
console.error(storage_items);
throw new Error('NaN!');
}
//test_end
localStorage.setItem(storage_KEY, JSON.stringify(storage_items));
}
function storage_empty() {
localStorage.setItem(storage_KEY, null);
}
/***/
function storage_add(item_id, item_text) {
storage_items[item_id] = { text: item_text };
storage_update();
}
function storage_remove(item_id) {
delete storage_items[item_id];
storage_update();
}
/***/
function storage_edit_item(item_id, new_text) {
storage_items[item_id].text = new_text;
storage_update();
}
function storage_check_item(item_id) {
storage_items[item_id].checked = 1;
storage_update();
}
function storage_uncheck_item(item_id) {
delete storage_items[item_id].checked;
storage_update();
}
function storage_max_id() {
// Выдает максимальное число из всех ключей (id) объекта.
// Нужен, чтобы id новых задач не пересекались со старыми.
return Math.max(0, ...Object.keys(storage_items));
}
/*****/
// <заметка>
// id элементов <li> создаются в виде id="task-число",
// но во все функции storage должны передаваться в виде числа.
let checklist = document.getElementById('check_list');
(function _init_list_from_storage() {
// * Список всех записей уже получен и сохранен в объект storage_items
let html = '';
for (let id in storage_items) {
let obj = storage_items[id];
html += item_html(obj.text, id, obj.checked);
}
checklist.insertAdjacentHTML('beforeend', html);
})();
checklist.addEventListener('click', function(e) {
let dataset = e.target.dataset.elem;
if (!dataset) return;
switch (dataset) {
case 'text' : item_edit(e); break;
case 'checkbox': item_toggle_done(e); break;
case 'remove' : item_remove(e); break;
}
});
function item_edit(e) {
// Здесь можно использовать contentEditable, не стал менять вашу реализацию
let span = e.target;
let input = document.createElement('input');
input.value = span.textContent;
span.textContent = '';
span.appendChild(input);
input.addEventListener('keydown', function(e) {
if (e.keyCode === 13) {
span.textContent = this.value;
let id = item_numeric_id(span.closest('li'));
storage_edit_item(id, this.value)
}
});
}
function item_toggle_done(e) {
let li = e.target.closest('li');
li.classList.toggle('done');
let id = item_numeric_id(li);
(e.target.checked ? storage_check_item : storage_uncheck_item)(id);
// см. «Тернарный оператор».
// Здесь выбирается одна из функций в зависимости от условия,
// которая затем вызывается с аргументом (id);
}
function item_remove(e) {
e.preventDefault();
let li = e.target.closest('li');
storage_remove(item_numeric_id(li));
li.remove();
}
/***/
let add_btn = document.getElementById('button_add');
let add_inp = document.getElementById('add');
add_btn.addEventListener('click', function() {
if (add_inp.value === '') {
return alert('Ошибка: Пустая строка');
}
item_add(add_inp.value);
add.value = '';
});
/***/
let unique_id = storage_max_id() + 1;
function item_numeric_id(li) {
// "task-11" → 11
return Number(li.id.match(/\d+/)[0]); // «Регулярные выражения»
}
function item_html(text, id, checked) {
return (`
<li id="task-${ id }" class="${ checked ? 'done': '' }">
<span data-elem="text">${ text }</span>
<input data-elem="checkbox" type="checkbox" ${ checked ? 'checked' : '' }>
<span class="delete" data-elem="remove"></span>
</li>
`);
}
function item_add(text) {
let id = unique_id++;
checklist.insertAdjacentHTML('beforeend', item_html(text, id));
storage_add(id, text);
}
.delete {
width: 18px;
height: 18px;
opacity: 0.3;
margin-left: 10px;
}
.delete:hover {
opacity: 1;
cursor: pointer;
}
.delete::before,
.delete::after {
position: absolute;
content: ' ';
height: 15px;
width: 2px;
background-color: #333;
}
.delete::before {
transform: rotate(45deg);
}
.delete::after {
transform: rotate(-45deg);
}
.done span {
text-decoration: line-through;
color: gray;
}
<div class="page">
<div class="container">
<h2>Список дел</h2>
<div class="main">
<input id="add" type="text" placeholder="Введите описание вашего дела...">
<button id="button_add">Добавить</button>
</div>
<hr>
<ul id="check_list"></ul>
</div>
</div>