ItBursa. Блок второй


Динамическое создание HTML элементов

Экспериментировать будем на странице http://bash.im/random

Находим элементы и меняем их при помощи innerHTML

var elements = document.querySelectorAll(".text");
elements[0].innerHTML = "Test"; 

Мы изменили уже существующий элемент.
Как добавить новый?

Можно в innerHTML указать теги. Но это не лучший вариант. Если внутри элемента, который редактируем таким образом, были обработчики событий, они могут потеряться. Могут не потеряться, зависит от браузера. Но полагаться на удачу не следует. Ищем другой способ. Нам нужен механизм, позволяющий добавить новый элемент. а не переписать существующий.

Чтобы что-то добавить, вначале нужно это "что-то" создать.
Создаём новый элемент:

var newElement = document.createElement("span");

Важно! Тег создаваемого элемента указывается в виде строки.

Добавим текст:

newElement.innerHTML = "Я создан динамически";

и форматирование

newElement.style.backgroundColor = "red";
newElement.style.color = "white";
newElement.style.fontSize = "24px";
newElement.style.border = "solid green 2px";

Создали. Как вставить?
Нам нужно получить родительский элемент и ему добавить ребёнка

var p = elements[1];
p.appendChild(newElement); 

О. Получилось.
Ок. Предположим, нам нужно вставить этот элемент несколько раз.
Пробуем:

var p = elements[2];
p.appendChild(newElement); 

Хмм. На новом месте элемент появился. А на старом исчез
Почему так?
Ну, у нас был только один элемент...
Если стоит задача создать много однотипных элементов, придётся каждый из них создавать через  createElement. Это важно помнить.

Поговорим о функции .appendChild();
Каждый элемент с точки зрения js является узлом - node

 
Google --- mdn --- node --- https://developer.mozilla.org/ru/docs/Web/API/Node

И даже на русском!

Здесь указаны методы Node

Node.appendChild()
    Вставляет Node как последний дочерний узел данного элемента.
Node.insertBefore()
    Вставляет первым Node данный в качестве параметра, непосредственно перед вторым, потомком данного элемента Node.
Node.replaceChild()
    Заменяет одного потомка Node из существующего на второй указанный в параметре.

Ок. Элемент создали. Как удалить?
Для библиотек, вроде jQuery, удаление осуществляется через element.remove

В js vanilla нам необходимо найти родительский элемент у попросить его удалить конкретного ребёнка.

newElement.parentNode.removeChild(newElement);

Всё! Мы это сделали!

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

Д.З. Разобрать document.createElementFragment, чем он отличается от простого  document.createElement

 Хорошая статья на эту тему

Создаём:

var fragment = document.createDocumentFragment()

в него можно добавлять другие узлы.

fragment.appendChild(node)

Особенность: когда documentFragment вставляется в DOM - то он исчезает, а вместо него вставляются его дети.

Это используем, если нужно добавить несколько однотипных элементов в DOM. Тогда вначале их добавляем в фрагмент, а потом его в DOM и на этом получаем выигрыш в производительности.

Состояния. Хранение состояний

Немного теории.

Состояния - набор характеристик, в которых находится элемент.

Пример: банкомат.
Состояния:
  • ждём карту;
  • вводим пин-код;
  • выдача денег;
  • печать чека.

При этом есть правила перехода между состояниями: из состояния ждём карту невозможно перейти к состоянию выдача денег пока не введён пин-код.

Конечный автомат - это набор состояний и правил перехода между ними.

Любое js-приложение обладает состояниями.
Состояние - это то, что сделал пользователь.
Для игры крестики-нолики состоянием будет состояние текущего поля.

Рано или поздно возникнет ситуация, когда пользователь обновит страницу. Мы хотим, чтобы её текущее состояние было сохранено.

Учимся сохранять состояние и восстанавливать его после обновления страницы

Создаём новый проект "Список дел"

Разметка:

   <ul>
      
   </ul>
   Enter new:
   <input>
   <button>Add</button>

JS-код
Получаем переменные:

var button = document.querySelector("button");
var input = document.querySelector("input");
var list = document.querySelector("ul");

Отслеживаем клики по button
Выводим в консоль значение input.value

button.addEventListener("click", function(){
    console.log(input.value);
});

Это была проверка
Добавим введённый в поле текст в список:

button.addEventListener("click", function(){
    var l = input.value;
    var li = document.createElement("li");
    li.innerText = l;
    list.appendChild(li);
    input.value = "";
});

Работает. Но при обновлении страницы текст исчезает.
Как сохранить введённый текст при обновлении страницы?

Создаём переменную state, в которой будем сохранять текущее состояние:

var state = {
    items: []
}

Теперь, когда добавляем элемент в список, будем одновременно добавлять его в массив item:

state.item.push(l);

...и сохранять объект state в хранилище localStorage

localStorage имеет два метода setItem - поместить в хранилище, и getItem - получить из хранилища.

Важно! localStorage может сохранять только строку. Чтобы преобразовать объект к строке используем метод JSON.stringify(state)

    localStorage.setItem("data", JSON.stringify(state));

здесь
localStorage - локальное хранилище;
setItem - получает данные;
data - ключ - имя, по которому хранятся значения;
JSON.stringify(state) - объект state преобразованный в строку.
 Основные методы для работы с JSON в JavaScript – это:
  • JSON.parse – читает объекты из строки в формате JSON.
  • JSON.stringify – превращает объекты в строку в формате JSON, используется, когда нужно из JavaScript передать данные по сети.  
Источник

Код полностью

var button = document.querySelector("button");
var input = document.querySelector("input");
var list = document.querySelector("ul");

var state = {
    items: []
};

var data = localStorage.getItem("data");
if(data) {
    state = JSON.parse(data);
}

update();

function update(){
    list.innerHTML = "";
    for(var i = 0; i < state.items.length; i++) {
        var li = document.createElement("li");
        li.innerText = state.items[i];
        list.appendChild(li); 
    }
}

button.addEventListener("click", function(){
    var l = input.value;
    state.items.push(l);
    update();
    localStorage.setItem("data", JSON.stringify(state));
    input.value = "";
});


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

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

1. При загрузке страницы в sessionStorage записываешь, например, ключ loaded = true
2. Если при открытии страницы ключ loaded === true, то считай, что страницу перезагрузили.
Отсюда

Т.е. условие перезагрузки

window.onload = function() {
    var loaded = sessionStorage.getItem('loaded');
    if(loaded) {
        alert("1");
    } else {
        sessionStorage.setItem('loaded', true);
    }
}

alert("1") выводится только при перезагрузке страницы

Код полностью

var button = document.querySelector("button");
var input = document.querySelector("input");
var list = document.querySelector("ul");
var state = {
    items: []
};

var data = localStorage.getItem("data");
if(data) {
    state = JSON.parse(data);
}

window.onload = function() {
    var loaded = sessionStorage.getItem('loaded');
    if(loaded) {
        update();
    } else {
        sessionStorage.setItem('loaded', true);
    }
}

function update(){
    list.innerHTML = "";
    for(var i = 0; i < state.items.length; i++) {
        var li = document.createElement("li");
        li.innerText = state.items[i];
        list.appendChild(li); 
    }
}

button.addEventListener("click", function(){
    var l = input.value;
    var li = document.createElement("li");
    li.innerText = l;
    list.appendChild(li);
    input.value = "";
   
    state.items.push(l);
    localStorage.setItem("data", JSON.stringify(state));
});