DOM - объектная модель, используемая для XML/HTML-документов
DOM – это представление документа в виде дерева объектов, доступное для изменения через JavaScript
DOM нужен для того, чтобы манипулировать страницей – читать информацию из HTML, создавать и изменять элементы.
Все элементы страницы, включая теги, текст, комментарии, являются узлами DOM
document.documentElement- Элемент <HTML>
document.body- Элемент <BODY>
По всем узлам:
Только по элементам:
все навигационные свойства – только для чтения
for (var i = 0; i < document.body.children.length; i++) {
alert( document.body.children[i] ); // DIV, UL, DIV, SCRIPT
}
elem.firstElementChild === elem.children[0]
elem.lastElementChild === elem.children[elem.children.length - 1]Кроме того:
var elements = document.querySelectorAll('ul > li:last-child');
for (var i = 0; i < elements.length; i++) {
alert( elements[i].innerHTML ); // "тест", "пройден"
}
var elems = document.body.children;
for (var i = 0; i < elems.length; i++) {
if (elems[i].matches('a[href$="zip"]')) {
alert( "Ссылка на архив: " + elems[i].href );
}
}
var numberSpan = document.querySelector('.num');
// ближайший элемент сверху подходящий под селектор li
alert(numberSpan.closest('li').className) // subchapter
// ближайший элемент сверху подходящий под селектор .chapter
alert(numberSpan.closest('.chapter').tagName) // LI
// ближайший элемент сверху, подходящий под селектор span
// это сам numberSpan, так как поиск включает в себя сам элемент
alert(numberSpan.closest('span') === numberSpan) // trueСвойство classList – это объект для работы с классами.
elem.dataset.* - Значения атрибутов вида data-* (IE10+).
elem.attributes все атрибуты элемента
elem.className в виде строки
<div id="elem" about="Elephant"></div>
alert( elem.getAttribute('About') ); // (1) 'Elephant', атрибут получен
elem.setAttribute('Test', 123); // (2) атрибут Test установлен
var attrs = elem.attributes; // (4) можно получить коллекцию атрибутов
for (var i = 0; i < attrs.length; i++) {
alert( attrs[i].name + " = " + attrs[i].value );
}
<body class="main page">
var classList = document.body.classList;
classList.remove('page'); // удалить класс
classList.add('post'); // добавить класс
for (var i = 0; i < classList.length; i++) { // перечислить классы
alert( classList[i] ); // main, затем post
}
alert( classList.contains('post') ); // проверить наличие класса
<div id="elem" data-about="Elephant" data-user-location="street"> </div>
alert( elem.dataset.about ); // Elephant
alert( elem.dataset.userLocation ); // street
Создание
document.createElement(tag)- Создать элемент с тегом tag
document.createTextNode(txt) - Создать текстовый узел с текстом txt
node.cloneNode(deep) - Клонировать существующий узел, если deep=false, то без потомков.
Вставка и удаление
innerHTML вставит именно HTML, а createTextNodeинтерпретирует теги как текст.
var div = document.createElement('div');
var textElem = document.createTextNode('Тут был я');
<ol id="list">
<li>0</li>
<li>1</li>
<li>2</li>
</ol>
var newLi = document.createElement('li');
newLi.innerHTML = 'Привет, мир!';
list.appendChild(newLi);
list.insertBefore(newLi, list.children[1]);// создать копию узла
var div2 = div.cloneNode(true);
// копию можно подправить
div2.querySelector('strong').innerHTML = 'Супер!';
// вставим её после текущего сообщения
div.parentNode.insertBefore(div2, div.nextSibling);var data = prompt("Введите текст для пункта списка", "");
var li = document.createElement('li');
li.appendChild(document.createTextNode(data));
ul.appendChild(li);getComputedStyle(element[, pseudo]) получить текущее используемое значение свойства с учетом всего каскада, вычисленный и примененный (только чтение)
document.body.style.backgroundColor = prompt('background color?', 'green');
div.style.cssText="color: red !important; \
background-color: yellow; \
width: 100px; \
text-align: center; \
blabla: 5; \
";
var computedStyle = getComputedStyle(document.body);
alert( computedStyle.marginTop ); Элемента
Страницы
document.documentElement.clientHeight - ширина/высота видимой области
прокрутка(изменение):
Чтобы запретить прокрутку страницы, достаточно поставить document.body.style.overflow = "hidden".
Событие – это сигнал от браузера о том, что что-то произошло. Существует много видов событий. Посмотрим список самых часто используемых, пока просто для ознакомления:
События мыши:
События на элементах управления:
Клавиатурные события:
События документа:
События CSS:
<input type="button" id="button" onclick="sayThanks()" />
button.onclick = sayThanks;
elem.onclick = function() {
alert( 'Спасибо' );
};
elem.addEventListener( "click" , function() {alert('Спасибо!')});Внутри обработчика события this ссылается на текущий элемент, то есть на тот, на котором он сработал.
Достоинства
Недостатки
Отложенный вызов через setTimeout(func, 0) используется не только в событиях, а вообще – всегда, когда мы хотим, чтобы некая функция func сработала после того, как текущий скрипт завершится.
elem.onclick = function(event) {
// вывести тип события, элемент и координаты клика
alert(event.type + " на " + event.currentTarget);
alert(event.clientX + ":" + event.clientY);
}
element.onclick = function(event) {
event = event || window.event;
// Теперь event - объект события во всех браузерах.
};event.type Тип события
event.clientX / event.clientY Координаты курсора в момент клика (относительно окна)
event.target это исходный элемент, на котором произошло событие, в процессе всплытия он неизменен
event.currentTarget(this) Элемент, на котором сработал обработчик. текущий элемент, до которого дошло всплытие, на нём сейчас выполняется обработчик.
При наступлении события обработчики сначала срабатывают на самом вложенном элементе, затем на его родителе, затем выше и так далее, вверх по цепочке вложенности.
события «всплывают» от внутреннего элемента вверх через родителей, подобно тому, как всплывает пузырек воздуха в воде.
Самый глубокий элемент, который вызывает событие, называется «целевым» или «исходным»элементом и доступен как event.target.
Отличия от this (=event.currentTarget):
event.stopPropagation(). Для остановки всплытия
три стадии прохода события:
Алгоритм:
Каждый обработчик имеет доступ к свойствам события:
Любой обработчик может остановить событие вызовом event.stopPropagation(), но делать это не рекомендуется, так как в дальнейшем это событие может понадобиться, иногда для самых неожиданных вещей.
Обычно делегирование – это средство оптимизации интерфейса. Мы используем один обработчик для схожихдействий на однотипных элементах.
Алгоритм:
table.onclick = function(event) {
var target = event.target;
// цикл двигается вверх от target к родителям до table
while (target != table) {
if (target.tagName == 'TD') {
// нашли элемент, который нас интересует!
highlight(target);
return;
}
target = target.parentNode;
}
// возможна ситуация, когда клик был вне <td>
// если цикл дошёл до table и ничего не нашёл,
// то обработчик просто заканчивает работу
}table.onclick = function(event) {
var target = event.target;
var td = target.closest('td');
if (!td) return; // клик вне <td>, не интересует
// если клик на td, но вне этой таблицы (возможно при вложенных таблицах)
// то не интересует
if (!table.contains(td)) return;
// нашли элемент, который нас интересует!
highlight(td);
}Плюсы
Минусы
Шаблон проектирования «поведение» (behavior) позволяет задавать хитрые обработчики на элементе декларативно, установкой специальных HTML-атрибутов и классов.
Приём проектирования «поведение» состоит из двух частей:
document.onclick = function(event) {
var target = event.target;
var id = target.getAttribute('data-toggle-id');
if (!id) return;
var elem = document.getElementById(id);
elem.hidden = !elem.hidden;
};Счётчик:
<button data-counter>1</button>
Ещё счётчик:
<button data-counter>2</button>
<script>
document.onclick = function(event) {
if (!event.target.hasAttribute('data-counter')) return;
var counter = event.target;
counter.innerHTML++;
};
</script>Есть два способа отменить действие браузера:
<a id="linkToGoogle" href="http://google.com">Google<a>
<script>
var linkToGoogle = document.querySelector('linkToGoogle');
linkToGoogle.addEventListener('click', function (e) {
e.preventDefault();
window.location.href = 'http://yandex.ru'; // Найдется всё!
});
</script>
<a href="/" onclick="return false">Нажми здесь</a>
или
<a href="/" onclick="event.preventDefault()">здесь</a>document.forms.my -- форма с именем 'my'
document.forms[0] -- первая форма в документе
<fieldset name="set">
<legend>fieldset</legend>
<input name="text" type="text">
</fieldset>
var form = document.forms.my; // можно document.forms[0]
var elem = form.elements.one; // можно form.elements[0]
alert( form.elements.set.elements.text ); //для fieldset
alert(elem.form == form); // trueinput.value = "Новое значение";
textarea.value = "Новый текст";
if (input.checked) {
alert( "Чекбокс выбран" );
}
<form name="form">
<select name="genre" multiple>
<option value="blues" selected>Мягкий блюз</option>
<option value="rock" selected>Жёсткий рок</option>
<option value="classic">Классика</option>
</select>
</form>
var form = document.forms[0];
var select = form.elements.genre;
for (var i = 0; i < select.options.length; i++) {
var option = select.options[i];
if(option.selected) {
alert( option.value );
}
}
select.selectedIndex = 0; // первая опция
var selectedOption = select.options[select.selectedIndex];var option = new Option("Текст", "value", true, true);selected выбрана ли опция
index номер опции в списке селекта
text Текстовое содержимое опции (то, что видит посетитель).
События focus/blur происходят при получении и снятия фокуса с элемента.
Они не всплывают. Но на фазе перехвата их можно перехватить. Это странно, но это так, не спрашивайте почему.
Везде, кроме Firefox, поддерживаются всплывающие альтернативы focusin/focusout.
По умолчанию многие элементы не могут получить фокус. Например, если вы кликните по DIV, то фокусировка на нем не произойдет.
Но это можно изменить, если поставить элементу атрибут tabIndex. Этот атрибут также дает возможность контролировать порядок перехода при нажатии Tab.
Текущий элемент на котором фокус: document.activeElement
| Событие | Описание |
|---|---|
| change | Изменение значения любого элемента формы. Для текстовых элементов срабатывает при потере фокуса |
| input | Событие срабатывает только на текстовых элементах. Оно не ждет потери фокуса, в отличие от change. |
При генерации события submit Обработчик в нём может проверить данные и, если они неверны, то вывести ошибку и сделать event.preventDefault() – тогда форма не отправится на сервер.
Чтобы отправить форму на сервер из JavaScript – нужно вызвать на элементе формы метод form.submit().
Основные принципы:
function Menu(options) {
var elem;
function getElem() {
if (!elem) render();
return elem;
}
function render() {
elem = document.createElement('div');
elem.className = "menu";
var titleElem = document.createElement('span');
elem.appendChild(titleElem);
titleElem.className = "title";
titleElem.textContent = options.title;
elem.onmousedown = function() {
return false;
};
elem.onclick = function(event) {
if (event.target.closest('.title')) {
toggle();
}
}
}
function renderItems() {
var items = options.items || [];
var list = document.createElement('ul');
items.forEach(function(item) {
var li = document.createElement('li');
li.textContent = item;
list.appendChild(li);
});
elem.appendChild(list);
}
// продолжение
function open() {
if (!elem.querySelector('ul')) {
renderItems();
}
elem.classList.add('open');
};
function close() {
elem.classList.remove('open');
};
function toggle() {
if (elem.classList.contains('open')) close();
else open();
};
this.getElem = getElem;
this.toggle = toggle;
this.close = close;
this.open = open;
}// создать объект меню с данным заголовком и опциями
var menu = new Menu({
title: "Сладости",
items: [
"Торт",
"Пончик",
"Пирожное",
"Шоколадка",
"Мороженое"
]
});
// получить сгенерированный DOM-элемент меню
var elem = menu.getElem();
// вставить меню в нужное место страницы
document.body.appendChild(elem);class Voter {
constructor({element} = {}) {
this.element = element;
this.voteElement = element.querySelector('.vote');
this.initiate();
}
initiate() {
this.element.addEventListener('click', e => {
if (e.target.closest('.up')) {
this.upVote();
} else
if (e.target.closest('.down')) {
this.downVote();
}
});
this.element.addEventListener('mousedown', e => {
e.preventDefault();
});
}
upVote() {
this.voteElement.textContent++;
}
downVote() {
this.voteElement.textContent--;
}
setVote(value) {
this.voteElement.textContent = value;
}
}function Voter(options) {
var elem = this._elem = options.elem;
this._voteElem = elem.querySelector('.vote');
elem.onmousedown = function() {
return false;
};
elem.onclick = this._onClick.bind(this);
}
Voter.prototype._onClick = function(event) {
if (event.target.closest('.down')) {
this._voteDecrease();
} else if (event.target.closest('.up')) {
this._voteIncrease();
}
};
Voter.prototype._voteIncrease = function() {
this._voteElem.innerHTML = +this._voteElem.innerHTML + 1;
};
Voter.prototype._voteDecrease = function() {
this._voteElem.innerHTML = +this._voteElem.innerHTML - 1;
};
Voter.prototype.setVote = function(vote) {
this._voteElem.innerHTML = +vote;
};