Інтернет Windows Android

Техніки роботи з DOM: батьківські, дочірні і сусідні елементи. Маніпуляції з DOM на чистому JavaScript Перевірка існування дочірніх вузлів

Як правило, коли потрібно виконати будь-які дії з DOM, розробники використовують jQuery. Однак практично будь-яку маніпуляцію з DOM можна зробити і на чистому JavaScript за допомогою його DOM API.

Розглянемо цей API більш докладно:

В кінці ви напишете свою простеньку DOM-бібліотеку, яку можна буде використовувати в будь-якому проекті.

DOM-запити

DOM-запити здійснюються за допомогою метода.querySelector (), який в якості аргументу приймає довільний СSS-селектор.

Const myElement \u003d document.querySelector ( "# foo\u003e div.bar")

Він поверне перший відповідний елемент. Можна і навпаки - перевірити, чи відповідає елемент селектору:

MyElement.matches ( "div.bar") \u003d\u003d\u003d true

Якщо потрібно отримати всі елементи, відповідні селектору, використовуйте наступну конструкцію:

Const myElements \u003d document.querySelectorAll ( ". Bar")

Якщо ж ви знаєте, на який батьківський елемент потрібно послатися, можете просто проводити пошук серед його дочірніх елементів, замість того щоб шукати по всьому коду:

Const myChildElemet \u003d myElement.querySelector ( "input") // Замість: // document.querySelector ( "# foo\u003e div.bar input")

Виникає питання: навіщо тоді використовувати інші, менш зручні методи вроде.getElementsByTagName ()? Є маленька проблема - результат вивода.querySelector () не оновлюється, і коли ми додамо новий елемент (дивіться), він не зміниться.

Const elements1 \u003d document.querySelectorAll ( "div") const elements2 \u003d document.getElementsByTagName ( "div") const newElement \u003d document.createElement ( "div") document.body.appendChild (newElement) elements1.length \u003d\u003d\u003d elements2.length // false

Також querySelectorAll () збирає все в один список, що робить його дуже ефективно.

Як працювати зі списками?

Додатково до всього у.querySelectorAll () є два маленьких нюансу. Ви не можете просто викликати методи на результати і очікувати, що вони застосуються до кожного з них (як ви могли звикнути робити це з jQuery). У будь-якому випадку потрібно буде перебирати всі елементи в циклі. Друге - що повертається об'єкт є списком елементів, а не масивом. Отже, методи масивів не спрацюють. Звичайно, є методи і для списків, щось вроде.forEach (), але, на жаль, вони підходять не для всіх випадків. Так що краще перетворити список в масив:

// Використання Array.from () Array.from (myElements) .forEach (doSomethingWithEachElement) // Або прототип масиву (до ES6) Array.prototype.forEach.call (myElements, doSomethingWithEachElement) // Простіше: .forEach.call (myElements , doSomethingWithEachElement)

У кожного елемента є деякі властивості, що посилаються на «сім'ю».

MyElement.children myElement.firstElementChild myElement.lastElementChild myElement.previousElementSibling myElement.nextElementSibling

Оскільки інтерфейс елемента (Element) успадкований від інтерфейсу вузла (Node), такі властивості теж присутні:

MyElement.childNodes myElement.firstChild myElement.lastChild myElement.previousSibling myElement.nextSibling myElement.parentNode myElement.parentElement

Перші властивості посилаються на елемент, а останні (за ісключеніем.parentElement) можуть бути списками елементів будь-якого типу. Відповідно, можна перевірити і тип елемента:

MyElement.firstChild.nodeType \u003d\u003d\u003d 3 // цей елемент буде текстовим вузлом

Додавання класів і атрибутів

Додати новий клас дуже просто:

MyElement.classList.add ( "foo") myElement.classList.remove ( "bar") myElement.classList.toggle ( "baz")

Додавання властивості для елементу відбувається точно так само, як і для будь-якого об'єкта:

// Отримання значення атрибута const value \u003d myElement.value // Установка атрибута як властивості елемента myElement.value \u003d "(! LANG: foo" // Для установки нескольких свойств используйте.Object.assign() Object.assign(myElement, { value: "foo", id: "bar" }) // Удаление атрибута myElement.value = null !}

Можна використовувати методи.getAttibute (), .setAttribute () і.removeAttribute (). Вони відразу ж поміняють HTML-атрибути елемента (на відміну від DOM-властивостей), що викличе браузерную перерисовку (ви зможете побачити всі зміни, вивчивши елемент за допомогою інструментів розробника в браузері). Такі перемальовування не тільки вимагають більше ресурсів, ніж установка DOM-властивостей, але і можуть привести до непередбачених помилок.

Як правило, їх використовують для елементів, у яких немає відповідних DOM-властивостей, наприклад colspan. Або ж якщо їх використання дійсно необхідно, наприклад для HTML-властивостей при спадкуванні (дивіться).

Додавання CSS-стилів

Додають їх точно так же, як і інші властивості:

MyElement.style.marginLeft \u003d "2em"

Якісь певні властивості можна задавати іспользуя.style, але якщо ви хочете отримати значення після деяких обчислень, то краще використовувати window.getComputedStyle (). Цей метод отримує елемент і повертає CSSStyleDeclaration, що містить стилі як самого елемента, так і його батька:

Window.getComputedStyle (myElement) .getPropertyValue ( "margin-left")

зміна DOM

Можна переміщати елементи:

// Додавання element1 як останнього дочірнього елемента element2 element1.appendChild (element2) // Вставка element2 як дочірнього елемента element1 перед element3 element1.insertBefore (element2, element3)

Якщо не хочеться переміщати, але потрібно вставити копію, використовуємо:

// Створення клону const myElementClone \u003d myElement.cloneNode () myParentElement.appendChild (myElementClone)

Метод.cloneNode () приймає логічне значення як аргумент, при true також клонуються і дочірні елементи.

Звичайно, ви можете створювати нові елементи:

Const myNewElement \u003d document.createElement ( "div") const myNewTextNode \u003d document.createTextNode ( "some text")

А потім вставляти їх як було показано вище. Видалити елемент безпосередньо не вийде, але можна зробити це через батьківський елемент:

MyParentElement.removeChild (myElement)

Можна звернутися і побічно:

MyElement.parentNode.removeChild (myElement)

Методи для елементів

У кожного елемента присутні такі властивості, как.innerHTML і.textContent, вони містять HTML-код і, відповідно, сам текст. У наступному прикладі змінюється вміст елемента:

// Змінюємо HTML myElement.innerHTML \u003d `

New content

beep boop beep boop

`// Таким чином вміст видаляється myElement.innerHTML \u003d null // Додаємо до HTML myElement.innerHTML + \u003d` continue reading ...

Насправді зміна HTML - погана ідея, т. К. Втрачаються всі зміни, які були зроблені раніше, а також перевантажуються обробники подій. Краще використовувати такий спосіб тільки повністю відкинувши весь HTML і замінивши його копією з сервера. Ось так:

Const link \u003d document.createElement ( "a") const text \u003d document.createTextNode ( "continue reading ...") const hr \u003d document.createElement ( "hr") link.href \u003d "foo.html" link.appendChild ( text) myElement.appendChild (link) myElement.appendChild (hr)

Однак це спричинить за собою дві перемальовування в браузері, в той час как.innerHTML призведе тільки до однієї. Обійти це можна, якщо спочатку додати все в DocumentFragment, а потім додати потрібний вам фрагмент:

Const fragment \u003d document.createDocumentFragment () fragment.appendChild (text) fragment.appendChild (hr) myElement.appendChild (fragment)

обробники подій

Один з найпростіших оброблювачів:

MyElement.onclick \u003d function onclick (event) (console.log (event.type + "got fired"))

Але, як правило, його слід уникати. Здесь.onclick - властивість елемента, і по ідеї ви можете його змінити, але ви не зможете додавати інші обробники використовуючи ще одну функцію, що посилається на стару.

Для додавання обробників краще іспользовать.addEventListener (). Він приймає три аргументи: тип події, функцію, яка буде викликатися щоразу при спрацьовуванні, і об'єкт конфігурації (до нього ми повернемося пізніше).

MyElement.addEventListener ( "click", function (event) (console.log (event.type + "got fired"))) myElement.addEventListener ( "click", function (event) (console.log (event.type + " got fired again ")))

Властивість event.target звертається до елементу, за яким закріплено подія.

А так ви зможете отримати доступ до всіх властивостей:

// Властивість `forms` - масив, що містить посилання на всі форми const myForm \u003d document.forms const myInputElements \u003d myForm.querySelectorAll (" input ") Array.from (myInputElements) .forEach (el \u003d\u003e (el.addEventListener (" change ", function (event) (console.log (event.target.value)))))

Запобігання дій за замовчуванням

Для цього використовується метод.preventDefault (), який блокує стандартні дії. Наприклад, він заблокує відправку форми, якщо авторизація на клієнтської стороні не була успішною:

MyForm.addEventListener ( "submit", function (event) (const name \u003d this.querySelector ( "# name") if (name.value \u003d\u003d\u003d "(! LANG: Donald Duck") { alert("You gotta be kidding!") event.preventDefault() } }) !}

Метод.stopPropagation () допоможе, якщо у вас є певний оброблювач події, закріплений за дочірнім елементом, і другий обробник того ж події, закріплений за батьком.

Як говорилося раніше, метод.addEventListener () приймає третій необов'язковий аргумент у вигляді об'єкта з конфігурацією. Цей об'єкт повинен містити будь-які з наступних булевих властивостей (за замовчуванням всі в значенні false):

  • capture: подія буде прикріплено до цього елементу перед будь-яким іншим елементом нижче в DOM;
  • once: подія може бути закріплено лише один раз;
  • passive: event.preventDefault () буде ігноруватися (виняток під час помилки).

Найбільш поширеним властивістю является.capture, і воно настільки поширене, що для цього існує короткий спосіб запису: замість того щоб передавати його в об'єкті конфігурації, просто вкажіть його значення тут:

MyElement.addEventListener (type, listener, true)

Обробники видаляються за допомогою метода.removeEventListener (), що приймає два аргументи: тип події і посилання на оброблювач для видалення. Наприклад властивість once можна реалізувати так:

MyElement.addEventListener ( "change", function listener (event) (console.log (event.type + "got triggered on" + this) this.removeEventListener ( "change", listener)))

спадкування

Припустимо, у вас є елемент і ви хочете додати обробник подій для всіх його дочірніх елементів. Тоді б вам довелося прогнати їх в циклі, використовуючи метод myForm.querySelectorAll ( "input"), як було показано вище. Однак ви можете просто додати елементи в форму і перевірити їх вміст за допомогою event.target.

MyForm.addEventListener ( "change", function (event) (const target \u003d event.target if (target.matches ( "input")) (console.log (target.value))))

І ще один плюс даного методу полягає в тому, що до нових дочірнім елементам обробник буде прив'язуватися автоматично.

анімація

Найпростіше додати анімацію використовуючи CSS з властивістю transition. Але для більшої гнучкості (наприклад для гри) краще підходить JavaScript.

Викликати метод window.setTimeout (), поки анімація не закінчиться, - не найкраща ідея, так як ваш додаток може зависнути, особливо на мобільних пристроях. Краще використовувати window.requestAnimationFrame () для збереження всіх змін до наступної перемальовування. Він приймає функцію як аргумент, яка в свою чергу отримує мітку часу:

Const start \u003d window.performance.now () const duration \u003d 2000 window.requestAnimationFrame (function fadeIn (now)) (const progress \u003d now - start myElement.style.opacity \u003d progress / duration if (progress< duration) { window.requestAnimationFrame(fadeIn) } }

Таким способом досягається дуже плавна анімація. У своїй статті Марк Браун міркує на цю тему.

Пишемо свою бібліотеку

Той факт, що в DOM для виконання будь-яких операцій з елементами весь час доводиться перебирати їх, може здатися дуже виснажливим в порівнянні з синтаксисом jQuery $ ( ". Foo"). Css ((color: "red")). Але чому б не написати кілька своїх методів, що полегшує це завдання?

Const $ \u003d function $ (selector, context \u003d document) (const elements \u003d Array.from (context.querySelectorAll (selector)) return (elements, html (newHtml) (this.elements.forEach (element \u003d\u003e (element.innerHTML \u003d newHtml)) return this), css (newCss) (this.elements.forEach (element \u003d\u003e (Object.assign (element.style, newCss))) return this), on (event, handler, options) (this.elements .forEach (element \u003d\u003e (element.addEventListener (event, handler, options))) return this)))

Складні і важкі веб-додатки стали звичайними в наші дні. Кросбраузерності і прості у використанні бібліотеки типу jQuery з їх широким функціоналом можуть сильно допомогти в маніпулюванні DOM на льоту. Тому не дивно, що багато розробників використовую подібні бібліотеки частіше, ніж працюють з нативним DOM API, з яким було чимало проблем. І хоча відмінності в браузерах як і раніше залишаються проблемою, DOM знаходиться зараз в кращій формі, ніж 5-6 років тому, коли jQuery набирав популярність.

У цій статті я продемонструю можливості DOM з маніпулювання HTML, сфокусувавшись на відносини батьківських, дочірніх і сусідніх елементів. У висновку я дам дані про підтримку цих можливостей в браузерах, але враховуйте, що бібліотека типу jQuery як і раніше залишається гарною опцією в силу наявності багів і непослідовностей в реалізації нативного функціоналу.

Підрахунок дочірніх вузлів

Для демонстрації я буду використовувати наступну розмітку HTML, протягом статті ми її кілька разів змінимо:

  • Example one
  • Example two
  • Example three
  • Example four
  • Example five
  • Example Six


Var myList \u003d document.getElementById ( "myList"); console.log (myList.children.length); // 6 console.log (myList.childElementCount); // 6

Як бачите, результати однакові, хоча техніки використовуються різні. У першому випадку я використовую властивість children. Це властивість тільки для читання, воно повертає колекцію елементів HTML, що знаходяться всередині запитуваної елемента; для підрахунку їх кількості я використовую властивість length цієї колекції.

У другому прикладі я використовую метод childElementCount, який мені здається більш акуратним і потенційно більш підтримуваним способом (докладніше обговоримо це пізніше, я не думаю, що у вас виникнуть проблеми з розумінням того, що він робить).

Я міг би спробувати використовувати childNodes.length (замість children.length), але подивіться на результат:

Var myList \u003d document.getElementById ( "myList"); console.log (myList.childNodes.length); // 13

Він повертає 13, тому що childNodes це колекція всіх вузлів, включаючи пробіли - враховуйте це, якщо вам важлива різниця між дочірніми вузлами і дочірніми вузлами-елементами.

Перевірка існування дочірніх вузлів

Для перевірки наявності у елемента дочірніх вузлів я можу використовувати метод hasChildNodes (). Метод повертає логічне значення, що повідомляють про їх наявність чи відсутність:

Var myList \u003d document.getElementById ( "myList"); console.log (myList.hasChildNodes ()); // true

Я знаю, що в моєму списку є дочірні вузли, але я можу змінити HTML так, щоб їх не було; тепер розмітка виглядає так:



І ось результат нового запуску hasChildNodes ():

Console.log (myList.hasChildNodes ()); // true

Метод як і раніше повертає true. Хоча список не містить ніяких елементів, в ньому є прогалина, що є дійсним типом вузла. Даний метод враховує всі вузли, не тільки вузли-елементи. Щоб hasChildNodes () повернув false нам треба ще раз змінити розмітку:



І тепер в консоль виводиться очікуваний результат:

Console.log (myList.hasChildNodes ()); // false

Звичайно, якщо я знаю, що можу зіткнутися з пропуском, то спочатку я перевірю існування дочірніх вузлів, потім за допомогою властивості nodeType визначаю, чи є серед них вузли-елементи.

Додавання і видалення дочірніх елементів

Є техніка, які можна використовувати для додавання і видалення елементів з DOM. Найбільш відома з них заснована на поєднанні методів createElement () і appendChild ().

Var myEl \u003d document.createElement ( "div"); document.body.appendChild (myEl);

В даному випадку я створюю

за допомогою методу createElement () і потім додаю його до body. Дуже просто і ви напевно використовували цю техніку раніше.

Але замість вставки спеціально створюваного елемента, я також можу використовувати appendChild () і просто перемістити існуючий елемент. Припустимо, у нас наступна розмітка:

  • Example one
  • Example two
  • Example three
  • Example four
  • Example five
  • Example Six

Example text

Я можу змінити місце розташування списку за допомогою наступного коду:

Var myList \u003d document.getElementById ( "myList"), container \u003d document.getElementById ( "c"); container.appendChild (myList);

Підсумковий DOM буде виглядати наступним чином:

Example text

  • Example one
  • Example two
  • Example three
  • Example four
  • Example five
  • Example Six

Зверніть увагу, що весь список був видалений зі свого місця (над параграфом) і потім вставлений після нього перед закриває body. І хоча зазвичай метод appendChild () використовується для додавання елементів створених за допомогою createElement (), він також може використовуватися для переміщення існуючих елементів.

Я також можу повністю видалити дочірній елемент з DOM за допомогою removeChild (). Ось як віддаляється наш список з попереднього прикладу:

Var myList \u003d document.getElementById ( "myList"), container \u003d document.getElementById ( "c"); container.removeChild (myList);

Тепер елемент був знищений. Метод removeChild () повертає видалений елемент і я можу його зберегти на випадок, якщо він буде потрібно мені пізніше.

Var myOldChild \u003d document.body.removeChild (myList); document.body.appendChild (myOldChild);

Таке існує метод ChildNode.remove (), відносно недавно доданий в специфікацію:

Var myList \u003d document.getElementById ( "myList"); myList.remove ();

Цей метод не повертає віддалений об'єкт і не працює в IE (тільки в Edge). І обидва методи видаляють текстові вузли точно так же, як і вузли-елементи.

Заміна дочірніх елементів

Я можу замінити існуючий дочірній елемент новим, незалежно від того, чи існує цей новий елемент або я створив його з нуля. Ось розмітка:

Example Text

Var myPar \u003d document.getElementById ( "par"), myDiv \u003d document.createElement ( "div"); myDiv.className \u003d "example"; myDiv.appendChild (document.createTextNode ( "New element text")); document.body.replaceChild (myDiv, myPar);

New element text

Як бачите, метод replaceChild () приймає два аргументи: новий елемент і замінний їм старий елемент.

Я також можу використовувати це метод для переміщення існуючого елемента. Погляньте на наступний HTML:

Example text 1

Example text 2

Example text 3

Я можу замінити третій параграф першим параграфом за допомогою наступного коду:

Var myPar1 \u003d document.getElementById ( "par1"), myPar3 \u003d document.getElementById ( "par3"); document.body.replaceChild (myPar1, myPar3);

Тепер згенерований DOM виглядає так:

Example text 2

Example text 1

Вибірка конкретних дочірніх елементів

Існує кілька різних способів вибору конкретного елемента. Як показано раніше, я можу почати з використання колекції children або властивості childNodes. Але поглянемо на інші варіанти:

Властивості firstElementChild і lastElementChild роблять саме те, чого від них можна очікувати по своїй назві: вибирають перший і останній дочірні елементи. Повернемося до нашої розмітці:

  • Example one
  • Example two
  • Example three
  • Example four
  • Example five
  • Example Six


Я можу вибрати перший і останній елементи за допомогою цих властивостей:

Var myList \u003d document.getElementById ( "myList"); console.log (myList.firstElementChild.innerHTML); // "Example one" console.log (myList.lastElementChild.innerHTML); // "Example six"

Я також можу використовувати властивості previousElementSibling і nextElementSibling, якщо я хочу вибрати дочірні елементи, відмінні від першого або останнього. Це робиться поєднанням властивостей firstElementChild і lastElementChild:

Var myList \u003d document.getElementById ( "myList"); console.log (myList.firstElementChild.nextElementSibling.innerHTML); // "Example two" console.log (myList.lastElementChild.previousElementSibling.innerHTML); // "Example five"

Також є подібні властивості firstChild, lastChild, previousSibling, і nextSibling, але вони враховують всі типи вузлів, а не тільки елементи. Як правило, властивості, що враховують тільки вузли-елементи корисніше тих, які вибирають все вузли.

Вставка контенту в DOM

Я вже розглядав способи вставки елементів в DOM. Давайте перейдемо до схожою темі і поглянемо на нові можливості по вставці контенту.

По-перше, є простий метод insertBefore (), він багато в чому схожий на replaceChild (), приймає два аргументи і при цьому працює як з новими елементами, так і з існуючими. Ось розмітка:

  • Example one
  • Example two
  • Example three
  • Example four
  • Example five
  • Example Six

Example Paragraph

Зверніть увагу на параграф, я збираюся спочатку прибрати його, а потім вставити перед списком, все одним махом:

Var myList \u003d document.getElementById ( "myList"), container \u003d document.getElementBy ( "c"), myPar \u003d document.getElementById ( "par"); container.insertBefore (myPar, myList);

В отриманому HTML параграф буде перед списком і це ще один спосіб перенести елемент.

Example Paragraph

  • Example one
  • Example two
  • Example three
  • Example four
  • Example five
  • Example Six

Як і replaceChild (), insertBefore () приймає два аргументи: додавання і елемент, перед яким ми хочемо його вставити.

Цей метод простий. Спробуємо тепер більш потужний спосіб вставки: метод insertAdjacentHTML ().



псевдокод

$ ( ". RightArrow"). Click (function () (rightArrowParents \u003d this.dom (); //. Dom (); is the pseudo function ... it should show the whole alert (rightArrowParents);));

Повідомлення про тривогу буде:

body div.lol a.rightArrow

Як я можу отримати це за допомогою javascript / jquery?

Використання jQuery, як і це (за ним слід рішення, яке не використовує jQuery, за винятком події, набагато менше викликів функцій, якщо це важливо):

Приклад в реальному часі:

$ ( ". RightArrow"). Click (function () (var rightArrowParents \u003d; $ (this) .parents (). AddBack (). Not ( "html"). Each (function () (var entry \u003d this.tagName .toLowerCase (); if (this.className) (entry + \u003d "." + this.className.replace (/ / g, ".");) rightArrowParents.push (entry);)); alert (rightArrowParents.join ( "")); return false;));

(В живих прикладах я оновив атрибут class на div як lol multi до продемонструвати обробку декількох класів.)

Ось рішення для точної відповідності елемента.

Важливо розуміти, що селектор ( не є реальним), Які показують хром-інструменти, однозначно не ідентифікують елемент в DOM. ( , Наприклад, він не буде розрізняти список послідовних елементів span. Немає інформації про позиціонування / индексировании)

$ .Fn.fullSelector \u003d function () (var path \u003d this.parents (). AddBack (); var quickCss \u003d path.get (). Map (function (item) (var self \u003d $ (item), id \u003d item .id? "#" + item.id: "", clss \u003d item.classList.length? item.classList.toString (). split ( "") .map (function (c) (return "." + c; )). join ( ""): "", name \u003d item.nodeName.toLowerCase (), index \u003d self.siblings (name) .length? ": nth-child (" + (self.index () + 1) + ")": ""; if (name \u003d\u003d\u003d "html" || name \u003d\u003d\u003d "body") (return name;) return name + index + id + clss;)). join ( "\u003e") ; return quickCss;);

І ви можете використовувати його так:

Console.log ($ ( "some-selector"). FullSelector ());

Я перемістив фрагмент з T.J. Кроудер до крихітному плагіну jQuery. Я використовував версію jQuery, навіть якщо він має рацію, що це абсолютно зайві накладні витрати, але я використовую його тільки для цілей налагодження, тому мені все одно.

Використання:

Nested span
Simple span
Pre


// result (array): [ "body", "div.sampleClass"] $ ( "span"). GetDomPath (false) // result (string): body\u003e div.sampleClass $ ( "span"). GetDomPath ( ) // result (array): [ "body", "div # test"] $ ( "pre"). getDomPath (false) // result (string): body\u003e div # test $ ( "pre"). getDomPath ()

Var obj \u003d $ ( "# show-editor-button"), path \u003d ""; while (typeof obj.prop ( "tagName")! \u003d "undefined") (if (obj.attr ( "class")) (path \u003d "." + obj.attr ( "class"). replace (/ \\ s / g, ".") + path;) if (obj.attr ( "id")) (path \u003d "#" + obj.attr ( "id") + path;) path \u003d "" + obj.prop ( "tagName"). toLowerCase () + path; obj \u003d obj.parent ();) console.log (path);

hello ця функція дозволяє помилку, пов'язану з поточним елементом, що не відображуваним в дорозі

Перевірте це зараз

$ J ( ". Wrapper"). Click (function (event) (selectedElement \u003d $ j (event.target); var rightArrowParents \u003d; $ j (event.target) .parents (). Not ( "html, body") .each (function () (var entry \u003d this.tagName.toLowerCase (); if (this.className) (entry + \u003d "." + this.className.replace (/ / g, ".");) else if (this.id) (entry + \u003d "#" + this.id;) entry \u003d replaceAll (entry, "..", "."); rightArrowParents.push (entry);)); rightArrowParents.reverse (); //if(event.target.nodeName.toLowerCase()\u003d\u003d"a "|| event.target.nodeName.toLowerCase () \u003d\u003d" h1 ") (var entry \u003d event.target.nodeName.toLowerCase (); if (event.target.className) (entry + \u003d "." + event.target.className.replace (/ / g, ".");) else if (event.target.id) (entry + \u003d "#" + event.target.id;) rightArrowParents.push (entry); //)

Де $ j \u003d jQuery Variable

також вирішити проблему з .. в класі ім'я

тут функція заміни:

Function escapeRegExp (str) (return str.replace (/ ([. * +? ^ \u003d!: $ () () | \\ [\\] \\ / \\\\]) / g, "\\\\ $ 1");) function replaceAll (str, find, replace) (return str.replace (new RegExp (escapeRegExp (find), "g"), replace);)

Ось рідна версія JS, яка повертає шлях jQuery. Я також додаю ідентифікатори для елементів, якщо вони є. Це дасть вам можливість зробити найкоротший шлях, якщо ви побачите ідентифікатор в масиві.

Var path \u003d getDomPath (element); console.log (path.join ( "\u003e"));

Body\u003e section: eq (0)\u003e div: eq (3)\u003e section # content\u003e section # firehose\u003e div # firehoselist\u003e article # firehose-46813651\u003e header\u003e h2\u003e span # title-46813651

Ось функція.

Function getDomPath (el) (var stack \u003d; while (el.parentNode! \u003d Null) (console.log (el.nodeName); var sibCount \u003d 0; var sibIndex \u003d 0; for (var i \u003d 0; i< el.parentNode.childNodes.length; i++) { var sib = el.parentNode.childNodes[i]; if (sib.nodeName == el.nodeName) { if (sib === el) { sibIndex = sibCount; } sibCount++; } } if (el.hasAttribute("id") && el.id != "") { stack.unshift(el.nodeName.toLowerCase() + "#" + el.id); } else if (sibCount > 1) (stack.unshift (el.nodeName.toLowerCase () + ": eq (" + sibIndex + ")");) else (stack.unshift (el.nodeName.toLowerCase ());) el \u003d el.parentNode ; ) Return stack.slice (1); // removes the html element)