вернуться к уроку

Дерево: проверка клика на заголовке

важность: 3

Есть кликабельное JavaScript-дерево UL/LI (см. задачу Раскрывающееся дерево).

<ul>
  <li>Млекопитающие
    <ul>
      <li>Коровы</li>
      <li>Ослы</li>
      <li>Собаки</li>
      <li>Тигры</li>
    </ul>
  </li>
</ul>

При клике на заголовке его список его детей скрывается-раскрывается. Выглядит это так: (кликайте на заголовки)

Однако, проблема в том, что скрытие-раскрытие происходит даже при клике вне заголовка, на пустом пространстве справа от него.

Как скрывать/раскрывать детей только при клике на заголовок?

В задаче Раскрывающееся дерево это решено так: заголовки завёрнуты в элементы SPAN и проверяются клики только на них. Представим на минуту, что мы не хотим оборачивать текст в SPAN, а хотим оставить как есть. Например, по соображениям производительности, если дерево и так очень большое, ведь оборачивание всех заголовков в SPAN увеличит количество DOM-узлов в 2 раза.

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

Исходный документ содержит кликабельное дерево.

P.S. Задача – скорее на сообразительность, однако подход может быть полезен в реальной жизни.

Открыть песочницу для задачи.

Подсказка

У события клика есть координаты. Проверьте по ним, попал ли клик на заголовок.

Самый глубокий узел на координатах можно получить вызовом document.elementFromPoint(clientX, clientY).

…Но заголовок является текстовым узлом, поэтому эта функция для него работать не будет. Однако это, всё же, можно обойти. Как?

Подсказка 2

Можно при клике на LI сделать временный SPAN и переместить в него текстовый узел-заголовок.

После этого проверить, попал ли клик в него и вернуть всё как было.

// 1) заворачиваем текстовый узел в SPAN

// 2) проверяем
var elem = document.elementFromPoint(e.clientX, e.clientY);
var isClickOnTitle = (elem == span);

// 3) возвращаем текстовый узел обратно из SPAN

На шаге 3 текстовый узел вынимается обратно из SPAN, всё возвращается в исходное состояние.