делегирование событий против прямой привязки при добавлении сложных элементов на страницу - PullRequest
17 голосов
/ 12 января 2012

У меня есть такая разметка (классы только для объяснения):

<ol id="root" class="sortable">
  <li>
    <header class="show-after-collapse">Top-Line Info</header>
    <section class="hide-after-collapse">
      <ol class="sortable-connected">
        <li>
          <header class="show-after-collapse">Top-Line Info</header>
          <section class="hide-after-collapse">
            <div>Content A</div>
          </section>
        </li>
      </ol>
    </section>
  </li>
  <li>
    <header/>
    <section class="hide-after-collapse">
      <ol class="sortable-connected">
        <li>
          <header/>
          <section class="hide-after-collapse">
            <div>Content B</div>
          </section>
        </li>
      </ol>
    </section>
  </li>
</ol>

То есть вложенные сортируемые списки.Однако сортируемого плагина достаточно, так как каждый li (в дальнейшем «item») сохраняет свой уровень, хотя внутренние списки связаны между собой.Элементы имеют всегда видимый заголовок и раздел, видимый в развернутом состоянии, переключаемый при нажатии на заголовок.Пользователь может добавлять и удалять элементы с любого уровня по желанию;добавление элемента верхнего уровня будет включать в себя пустой список гнезд.Мой вопрос касается JS-инициализации вновь созданного элемента: хотя они будут использовать некоторые общие функции, которые я могу охватить с помощью

$("#root").on("click", "li > header", function() {
  $(this).parent().toggleClass("collapsed");
});

и

li.collapsed section {
  display: none;
}

(Дополнительный вопрос:будет ли это подходящим местом для использования подробных / сводных тегов HTML5? Похоже, сомнительно, превратятся ли они в окончательную спецификацию, и мне нужен скользящий переход, поэтому мне кажется, что для этого мне понадобится JSв любом случае. Но я бросаю вопрос в массы. Привет, массы.)

Если корневой список является единственным (релевантным) элементом, который гарантированно существует при загрузке страницы, чтобы .on () работал эффективноЯ должен связать все события с этим элементом и прописать точный селектор для каждого, насколько я понимаю.Так, например, чтобы связать отдельные функции с двумя кнопками, расположенными рядом друг с другом, мне бы приходилось каждый раз указывать полный селектор, а-ля

$("#root").on("change", "li > section button.b1", function() {
  b1Function();
}).on("change", "li > section button.b2", function() {
  b2Function();
});

Это точно?В таком случае, имеет ли смысл отказываться от .on () и связывать мои события во время добавления нового элемента на страницу?Общее количество предметов, вероятно, будет не более десятков, если это повлияет на ответ.

Ответы [ 2 ]

40 голосов
/ 12 января 2012

Вы будете создавать меньшую нагрузку на ЦП при привязке событий, используя $(<root-element>).on(<event>, <selector>), так как вы будете привязываться к одному «корневому» элементу вместо потенциально большего количества отдельных элементов-потомков (каждое связывание занимает время ...).

При этом вы будете нести больше ресурсов ЦП, когда происходят реальные события, так как они должны поднять DOM до "корневого" элемента.

Краткий рассказ: делегат сохраняет ЦП при привязке обработчики событий ; bind сохраняет процессор, когда события вызывают (например, пользователь что-то щелкает).

Так что вам решать, какая точка важнее для производительности. Есть ли у вас доступный процессор при добавлении новых элементов? Если это так, то привязка непосредственно к новым элементам была бы наилучшей для общей производительности, однако, если добавление элементов является ресурсоемкой операцией, вы, вероятно, захотите делегировать привязку события и позволить инициирующему событию создать дополнительную нагрузку на ЦП из всех пузырьков.

Обратите внимание, что:

$(<root-element>).on(<event>, <selector>, <event-handler>)

совпадает с:

$(<root-element>).delegate(<selector>, <event>, <event-handler>)

и это:

$(<selector>).on(<event>, <event-handler>)

совпадает с:

$(<selector>).bind(<event>, <event-handler>)

.on() является новым в jQuery 1.7, и если вы используете 1.7+, то .delegate(<selector>, <event>, <event-handler>) - это просто сокращение для .on(<event>, <selector>, <event-handler>).

UPDATE

Вот тест производительности, показывающий, что быстрее делегировать привязку события, чем отдельную привязку к каждому элементу: http://jsperf.com/bind-vs-click/29. К сожалению, этот тест производительности был удален.

UPDATE

Вот тест производительности, показывающий, что запуск события происходит быстрее, когда вы привязываете напрямую к элементам, а не делегируете привязку: http://jsperf.com/jquery-delegate-vs-bind-triggering (Обратите внимание, что это не идеальный тест производительности, поскольку методы привязки включены в тест, но поскольку delegate работает быстрее при связывании, это просто означает, что bind еще быстрее относительно разговора)

9 голосов
/ 25 декабря 2015

Поскольку принятый ответ имеет неточные тесты (кстати: тестируйте свой код, измеряйте производительность, а не просто слепо следуйте некоторым "правилам" - это не так, как выполняется оптимизация!) И он просто неверен Я публикую фиксированные тесты: https://jsperf.com/jquery-delegate-vs-bind-triggering/49

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

Единственные случаи, когда делегирование всегда плохое, - это такие события, как перемещение мыши и прокрутка, которые запускаются x раз в секунду. В этом вы заметите разницу в производительности.

Если у вас разница в 1 мс (не произойдет, но это только пример) для одного события - например, щелчка - вы этого не заметите. Если у вас разница в 1 мс на событие, которое происходит 100 раз в секунду, вы заметите потребление ресурсов процессора.

Наличие тысячи элементов не окажет негативного влияния на вашу производительность при делегировании - на самом деле - это тот случай, когда их следует использовать - чтобы не перегружать процессор при подключении тысячи обработчиков событий.

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

...