Получение доступа к узлам DOM для UX-аналитики

Для получения доступа и управлением содержанием веб-страницы мы будем использовать JavaScript. С помощью JS можно выцепить из элементов HTML нужную информацию и модифицировать для проведения A/B теста или прототипирования. Не смотря на то, что статья про работу с узлами HTML, технически она про работу в целом с DOM.

DOM это интерфейс (API) для HTML и XML-страниц, обеспечивающий структуру и необходимые методы для работы с элементами, и написан на C++. Каждый элемент на странице это узел, в том числе и содержание узла тоже является дочерним узлом. Можно выделить узлы-элементы, это тэги вроде body, head, html, и текстовые узлы с контентом внутри этих тэгов, у которых не может быть потомков. Отдельно в DOM находятся и комментарии к коду, и точка входа в DOM.

Получение доступа по имени элемента

● Начнем с объекта document, у которого есть свойства и методы для доступа к элементам. Самый простой и распространенный способ получения элемента из любой части документа это getElementById, который возвращает элемент по его id. Например, нам нужно найти элемент с уникальным id «userData» и сохранить HTML контент в переменную.

var dataCollection = document.getElementById( "masthead" ).innerHTML;

Отлично работает при условии, что все id в документе уникальны и не повторяются. Если есть много элементов с одинаковым id, то поведение непредсказуемо. Также DOM позволяет менять стили style с помощью getElementById. Разумеется, скрипт для управления элементами DOM должен размещаться после создания этих элементов. Это можно сделать за счет размещения скрипта <script> после <body>, использования onload или слушателя событий.

document.getElementById("masthead").style.color = "#347632";
document.getElementById("masthead").style.backgroundColor ="#456714";
document.getElementById("bio").textContent = "Прекрасный человек";

Из важных нюансов также можно отметить, что имена свойств, которые пишутся через дефис (border-right-padding), в JS и DOM пишутся слитно и с заглавной буквы каждое слово, borderRightPadding. Еще нюанс: у вас есть переменная myMug, и она хранит ссылку на объект с ID. Если уничтожить объект, то он останется висеть в DOM, но на странице его видно не будет. Чтобы его уничтожить полностью, нужно преобразовать его в null. Сборщик мусора не тронет объект, пока на него ссылается переменная.

 

getElementsByName возвращает коллекцию элементов с классом, причем не важно, один класс у элемента или много. Работает только с элементами, для которых явно предусмотрен атрибут name (это может быть form, input, a, select, textarea и т.п.). Не сработает с div, p. Как и с любой коллекцией, можно использовать свойство length для получения длины списка объектов и метод item() для получения самого списка. Используется редко, но если надо вбить данные в поля и нажать «submit», то пригождается.

var elems = document.getElementsByName("description");
document.getElementsByName('login')[0].value = 'login';
document.getElementsByName('password')[0].value = 'password';
document.getElementsByName('goOff')[0].form.submit()

 

getElementsByClassName() делает тоже самое, что и getElementById, но по значению атрибута class, и возвращает коллекцию элементов HTMLCollection.

<div class="article">Статья</div>
<div class="long article">Длинная статья</div>

<script>
  var articles = document.getElementsByClassName('article');
  alert( articles.length ); // 2, найдёт оба элемента
</script>

 

querySelectorAll() куда интереснее и выручает в сложных ситуациях. Позволяет получить доступ к узлам DOM по CSS-селекторам. Возвращает NodeList. Если вы захотите удивить коллег, то напишите querySelectorAll.forEach, будет работать в некоторых случаях. В примере ниже мы не только получили все input, но и преобразовали в нормальный массив. Работает для ≥ 5.1. Для быстрой работы в консоли я использую сокращенную запись $$("li.group"), но такая запись работает не во всех браузерах.

var listDomeArray = document.querySelectorAll('input');
listDomeArray.forEach(function() {
}); 
Array.prototype.forEach.call(domList, function() {
});

 

document.getElementsByTagName() ищет все элементы с заданным тэгом внутри элемента, который мы укажем вместо document. Возвращает список узлов (коллекцию), который очень похож по поведению на массивы. Соответственно, и обращаться к ним нужно по индексу. Если указать в качестве передаваемого аргумента символ *, то метод вернем все элементы из HTML-документа.

var myTime = document.getElementsByTagName('*'); //выбираем все элементы на странице

Код выше извлечет ВСЕ элементы и заключит их в список узлов (коллекцию). Соответственно, для получения доступа к каждому узлу по очереди нужен цикл. В принципе, querySelectorAll() и getElementsByTagName взаимозаменяемы. Важно понимать, что получать все элементы на динамически обновляемых страницах не так страшно, как кажется, т.к. на выходе мы получаем не массив.
var dataMy = document.getElementsByTagName("div");
for( var i = 0; i < dataMy.length; i++ ) {
// описание того, что надо сделать
}

Важно отметить, что полученные похожие на массивы сущности это HTMLCollection or NodeList objects, про которые я говорил выше. Отличие от обычных массивов одно, наследуется Object.prototype вместо Array.prototype. Соответственно, можно забыть про forEach (), push (), map (), filter () и slice (). Преобразовать в нормальный массив можно с помощью:

var almostArray = {
0: 'myItem1',
1: 'myItem2',
2: 'myItem3',
length: 3
};
var fullyArray = Array.prototype.slice.call(almostArray);
fullyArray = [].slice.call(almostArray);
fullyArray.indexOf('myItem1');

Проверить, прошло ли преобразование и чем является наш объект, массивом или любым другим объектом, можно с помощью следующей конструкции. Она не сработает, если наш массив был создан во фрейме, то проверка не пройдет корректно, т.к. другой прототип.

var whatItIs = [], itWillFlase = '';
alert(whatItIs instanceof Array);
alert(itWillFlase instanceof Array);

Если же задача в получении доступа сразу ко всем элементам с определенным классом, то можно использовать такое решение:

var elements = document.querySelectorAll('.user');
var handler = function(e) {
console.log("ваш код");
};
 
for (let btn of elements) {
btn.addEventListener('click', handler);
}

 

 

 

Доступ к значениям атрибутов

● Это все было очень полезно, но чаще всего недостаточно получить доступ к элементам. Значения атрибутов тоже важны и их нужно менять. Для этого используется метод GetAttribute(). Он прост в использовании, т.к. ему требуется только один аргумент: имя атрибута. Обратите внимание, в примере мы использовали "src", но могли и также использовать "alt" или "id". Если в элементе не будет найден указанный атрибут, то будет возвращены null или пустая строка.

var homeCont = document.getElementById("home");
alert( homeCont.getAttribute("src") );

● Теперь надо научиться всем этим делом управлять. Мы хотим поменять значение атрибута src. В первую очередь, это setAttribute(). Работает он также просто, достаточно указать атрибут, который нужно изменить и на какой атрибут мы его будем менять. А главное, работает быстро.

var item = document.getElementById("afisha");
item.setAttribute("class", "democlass");

Таким способом можно менять link у .css файлов, указав в качестве атрибута другой href. GetAttribute() и setAttribute() хороши тем, что поддерживают старые браузеры.

InnerHtml позволяет получить доступ к разметке и тексту внутри элемента, и менять их. Можно даже полностью удалить содержимое body, или можно получить код страницы, которая динамически меняется. Открывает хорошие возможности для тестов. для примитивных задач, вроде вставки текста, лучше использовать node.textContent, меньше проблем безопасности.
var list = document.getElementById("header");
for(var i = 1; i <= 5000; i++) {
list.innerHTML += `<li>item ${i}</li>`; // update 5000 times
}


Про innerHTML поговорим подробнее, т.к. он не входит в стандарт, но при этом работает почти везде и работает быстрее операций с DOM. Он способен забрать всю разметку и содержание внутри указанного элемента и очистить его.
swillovPage = document.getElementsByTagName("body")[0];
swillovPage.innerHTML = swillovPage.innerHTML;
swillovPage.innerHTML = ''


Если вставить внутрь innerHTML некорректный код, например elem.innerHTML += ‘</div>’, то браузер не сможет это распарсить и результат будет непредсказуемым. И для простых задач, вроде замены значения title, учше использовать document.title = 'New title';, а не писать огромные скрипты.

Добавление новых элементов

createElement() позволяет создать новый узел элемента, функция принимает только один аргумент: элемент, который требуется создать. Сразу после применения метода новый элемент не появится на странице, но JS уже будет о нем знать. Многие подумают, не дубль ли это innerHtml? Нет, потому что это гораздо более подходящий инструмент для добавления элементов в HTML, и гораздо более шустрый. innerHTML удаляет все дочерние элементы, разбирает полученную строку и результат добавляет как дочерние элементы. createElement() работает без всех этих вычислений.

var newDiv = document.createElement("div");

 

registerElement. Синтаксис чуть ниже, в котором tag-name это имя тэга, например «mug-shops» (дефис обязателен). И options отвечает за новый элемент, который наследуется от HTMLElement. Иначе не будет стандартных методов и свойств. По факту, происходит регистрация нового кастомного элемента и возврат его конструктора.

var <em>constructor</em> = document.registerElement(<em>tag-name</em>, <em>options</em>);

 

● Давайте рассмотрим createTextNode(), с помощью которого можно создать новый элемент, в частности текст. Его качественное отличие от innerHTML в том, что используя innerHTML нам необходимо проходить весь путь построения DOM. А вот между createElement() и createTextNode() большой разницы в скорости работы замечено не было.

var itsText = document.createTextNode ("нужный стринг");

Вот мы создали новую строку текста и новый элемент, но где же он? Для добавления текста в документ используется метод appendChild. У него есть только один аргумент, имя узла, который мы хотим добавить в DOM. Важно указать, какой из уже существующих элементов будет родительским, т.к. будет добавлен узел в конец списка дочерних узлов. Помимо appendChild, можно использовать insertBefore. Начнем с appendChild().

var myNewText = document.createElement('div');
myNewText.textContent = "Это новый элемент.";
document.body.appendChild(myNewText);

или

document.domain = 'google.ru';
var output = document.createElement('p');
document.body.appendChild(output);

insertBefore пригодится, когда нужно вставить элемент в список дочерних элементов родителя перед определенным элементом. Оперируем двумя  аргументами: новый дочерний узел и смежный узел, который будет следовать после вставляемого.  Комбнириуя appendChild(), insertBefore() и replaceChild() можно вставить узлы в любое место страницы.

 

Клонирование и удаление элементов

● Элементы можно клонировать с помощью elem.cloneNode(true) и удалять с помощью removeChild.

var post = document.getElementById("_Q5");
post.parentNode.removeChild(post);

Теперь вы сможете более гибко получать, изменять, и добавлять данные при подготовке тестирования и проверке гипотез. Всего немного JavaScript, и проверка гипотез становится куда быстрее и затрагивает все меньше участников команды. И готовьтесь к проблемам со старыми IE, куда уж без этого.

«Взаимодействуя с данным сайтом, вы, как пользователь, автоматически даете согласие согласие на обработку персональных данных»

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.