WedX - журнал о программировании и компьютерных науках

Как оптимизировать такой код, как this.parentNode.parentNode.parentNode?

var topClick = function() {
  child = this.parentNode.parentNode.parentNode.parentNode;
  child.parentNode.removeChild(child);
  var theFirstChild = document.querySelector(".m-ctt .slt");
  data[child.getAttribute("data-id")].rank = 5;
  insertBlogs();
};

Как видите, в моем коде есть такая часть:

this.parentNode.parentNode.parentNode.parentNode;  

Есть ли другой способ оптимизировать код (без использования jQuery)?

01.02.2016

  • Вы пробовали петлю? 01.02.2016
  • Или вы можете сделать $(elem).parents(parentElementSelector), который вы ищете. 01.02.2016
  • @Bergi - цикл был бы менее эффективным. ОП знает, как далеко они хотят зайти. Кажется, что объект меньше печатает, чем быстро ищет элемент, соответствующий правилу. 01.02.2016
  • вы можете использовать document.querySelector('parentElement selector') для этого 01.02.2016
  • @kpsingh — поиск не в том направлении. Вопрос заключается в том, чтобы смотреть вверх по дереву, а не вниз. 01.02.2016
  • В поддерживаемых браузерах есть нативный .closest() метод. (чистый JavaScript), который работает аналогично методу jQuery .closest(). 01.02.2016
  • Вы также можете подумать о том, как вы называете свои переменные. 01.02.2016
  • Каков ваш вариант использования здесь? Искренне любопытно. 01.02.2016
  • Есть список блогов. В каждом блоге есть кнопки редактирования и удаления. Когда я нажимаю такие кнопки, я хочу найти элемент блога. @Monkpit 02.02.2016

Ответы:


1

Альтернативный подход


Ваша цель

Согласно вашим комментариям к исходному вопросу, ваша общая цель такова:

Есть список блогов. В каждом блоге есть кнопки типа "редактировать" и "удалить". Когда я нажимаю такие кнопки, я хочу найти элемент блога.

Я считаю, что лучший подход к решению проблемы, с которой вы столкнулись (в отличие от ответа на вопрос, который вы задали - извините!) - это подойти к проблеме по-другому.

Из ваших комментариев вы сказали, что у вас есть что-то вроде этого:

<ul id="blog-list">
  <li class="blog-item">
    <a href="blog1.com">
      Boats galore!
    </a>
    <span class="blog-description">
      This is a blog about some of the best boats from Instagram.
    </span>
    <span class="blog-button delete-button">
      Delete
    </span>
    <span class="blog-button edit-button">
      Edit
    </span>
  </li>
  <li class="blog-item">
    <a href="blog2.com">
      Goats galore!
    </a>
    <span class="blog-description">
      A blog about the everyday adventures of goats in South Africa.
    </span>
    <span class="blog-button delete-button">
      Delete
    </span>
    <span class="blog-button edit-button">
      Edit
    </span>
  </li>
  <li class="blog-item">
    <a class="blog-link" href="blog3.com">
      Totes galore!
    </a>
    <span class="blog-description">
      A blog about containers and bags, and the owners who love them.
    </span>
    <span class="blog-button delete-button">
      Delete
    </span>
    <span class="blog-button edit-button">
      Edit
    </span>
  </li>
</ul>

И ваша цель — добавить click обработчиков событий к button элементам для каждого blog-link элемента.

Итак, давайте просто переведем эту цель с простого английского на псевдокод:

for each `blog-link` `b`
  delete_button.onclick = delete_handler(`b`);
  edit_button.onclick = edit_handler(`b`);

Пример скрипта

Рабочий пример: https://jsfiddle.net/vbt6bjwy/10/.

<script>
  function deleteClickHandler(event, parent) {
    event.stopPropagation();
    parent.remove();
  }

  function editClickHandler(event, parent) {
    event.stopPropagation();
    var description = parent.getElementsByClassName("blog-description")[0];
    description.innerHTML = prompt("Enter a new description:");
  }

  function addClickHandlers() {
    var bloglistElement = document.getElementById("blog-list");
    var blogitems = bloglistElement.getElementsByClassName("blog-item");

    blogitems.forEach(function(blogitem){
      var deleteButtons = blogitem.getElementsByClassName("delete-button");

      deleteButtons.forEach(function(deleteButton){
        deleteButton.onclick = function(event) {
          return deleteClickHandler(event, blogitem);
        }
      });

      var editButtons = blogitem.getElementsByClassName("edit-button");

      editButtons.forEach(function(editButton){
        editButton.onclick = function(event) {
          return editClickHandler(event, blogitem);
        }
      });
    });
  }

  HTMLCollection.prototype.forEach = Array.prototype.forEach;
  addClickHandlers();
</script>

Объяснение

Способ, который вы выбрали для реализации решения, действителен, но я подумал, что дам вам другой взгляд на ту же проблему.

На мой взгляд, внутренние теги сущности blog не должны знать структуру или свойства окружающего HTML, чтобы ваши кнопки edit и delete работали.

Ваше исходное решение должно работать в обратном направлении от каждого button вверх по цепочке родительских элементов, пока не найдет то, что, по его предположению, является правильным родительским элементом, основанным на хрупком методе, таком как жесткое кодирование, перемещающееся вверх N раз в цепочке родительских элементов. Разве не было бы лучше, если бы мы могли использовать обычный выбор элементов JavaScript, чтобы быть абсолютно уверенными в том, что мы выбираем? Таким образом, независимо от того, как может измениться структура HTML, наш JavaScript не сломается, пока классы и идентификаторы остаются согласованными.

Это решение перебирает каждый blog-item в элементе #blog-list:

blogitems.forEach(function(blogitem){ ... });

В цикле forEach мы захватываем массивы, содержащие элементы .delete-button и .edit-button. Для каждого из этих элементов мы добавляем соответствующий обработчик событий в свойство onclick:

deleteButtons.forEach(function(deleteButton){
  deleteButton.onclick = function(event) {
    return deleteClickHandler(event, blogitem);
  }
});

Для каждого элемента deleteButton в массиве мы назначаем анонимную функцию обработчику событий onclick. Создание этой анонимной функции позволяет нам создать замыкание.

Это означает, что каждая функция deleteButton.onclick будет индивидуально «запоминать», к какому blogitem она принадлежит.

Ознакомьтесь с этим вопросом/ответом SO о замыканиях для получения дополнительной информации: Как работают замыкания JavaScript?

И мы добавляем строку HTMLCollection.prototype.forEach..., чтобы обеспечить функциональность forEach для всех HTMLCollection объектов. Такие функции, как getElementsByClassName, возвращают объект HTMLCollection. По большей части он действует точно так же, как массив, но не позволяет нам использовать forEach по умолчанию. Примечание о совместимости: вы, безусловно, можете использовать стандартный цикл for, так как forEach доступен не во всех браузерах (в основном виноваты старые браузеры IE). Я предпочитаю идиому forEach.

Конечный результат

Конечным результатом является немного более длинный код, поскольку масштаб проблемы немного шире, чем вопрос, который вы на самом деле задали. Преимущество заключается в том, что этот код гораздо более гибкий и устойчивый к нарушениям при изменении структуры HTML.

02.02.2016
  • Я так тронут тем, что ты учил меня с энтузиазмом. И я действительно многому научился, зная, как решить ее лучше, а также глубже понимая замыкание. Я буду читать его несколько раз, чтобы приобрести этот метод. Большое спасибо! 03.02.2016
  • @yaochiqkl Я рад, что вам был полезен мой ответ. Если вы интересуетесь JavaScript и читаете больше о таких вещах, как замыкания, я предлагаю Javascript от Crockford: хорошие стороны. В частности, в главе 4 рассматриваются функции, область видимости в JS и замыкания в JS. Вся книга достаточно короткая, чтобы ее можно было быстро прочитать, но в ней много полезной информации. Он был опубликован в 2008 году, но до сих пор актуален для современного JS. 03.02.2016

  • 2

    Вы можете использовать нерекурсивную вспомогательную функцию, например:

    function nthParent(element, n) {
      while(n-- && element)  
        element = element.parentNode;
      return element;
    }
    
    01.02.2016

    3

    Вы можете использовать рекурсивную вспомогательную функцию, например:

    function getNthParent(elem, n) {
        return n === 0 ? elem : getNthParent(elem.parentNode, n - 1);
    }
    
    var child = getNthParent(someElement, 4);
    
    01.02.2016
  • Благодарю вас! Наконец я принимаю ваш метод. Но когда для определения этого используется переменная, инициализированная функциональным выражением, она говорит Uncaught ReferenceError: getNthParent is not defined, поэтому я немного изменил return n === 0 ? elem : arguments.callee(elem.parentNode, n - 1); 01.02.2016
  • @yaochiqkl arguments.callee выглядит немного громоздко для меня. Вместо этого я бы использовал выражение именованной функции. var myFn = function getNthParent(elem, n) { ... } 01.02.2016

  • 4
    var topClick = function(event){
        console.log(event);
        child = this.parentNode.parentNode.parentNode.parentNode;
        child.parentNode.removeChild(child);
        var theFirstChild = document.querySelector(".m-ctt .slt");
        data[child.getAttribute("data-id")].rank = 5;
        insertBlogs();
    };
    

    Сюрприз! Я печатаю событие через console.log. И найдите такой массив внутри события: введите здесь описание изображения

    И я нахожу нужный мне элемент:

    function(event){
    console.log(event.path[4]);
    child = event.path[4];
    }
    

    и это работает! Как волшебно! Может, агент события другой!
    Все равно спасибо за ответы!
    В следующий раз, прежде чем задавать вопросы, сначала подумаю! :D

    01.02.2016
  • Это не стандарт и может не поддерживаться в каждом браузере. См. stackoverflow.com/a/30021867/2011448. 01.02.2016
  • Это предполагает информацию, которая не была указана в вопросе. (Я знаю, что это был ваш вопрос, но все же...) 01.02.2016
  • Новые материалы

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

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

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..


    Для любых предложений по сайту: [email protected]