Сравнение использования памяти браузера: встроенный onClick и JQuery .bind()

У меня есть ~ 400 элементов на странице, к которым привязаны события щелчка (4 разных типа кнопок по 100 экземпляров каждого, события щелчка каждого типа выполняют одну и ту же функцию, но с разными параметрами).

Мне нужно свести к минимуму любое влияние на производительность, которое это может иметь. Какой удар по производительности я получаю (память и т. д.), привязывая события кликов к каждому из них по отдельности (используя JQuery bind())? Было бы более эффективно вместо этого иметь встроенный onclick, вызывающий функцию для каждой кнопки?

Изменить для уточнения :):
На самом деле у меня есть таблица (сгенерированная с помощью JQGrid), и каждая строка имеет столбцы данных, за которыми следуют 4 столбца «кнопка» значка — удаление и три другие бизнес-функции, которые делают AJAX обращается к серверу:

|id|description|__more data_|_X__|_+__|____|____|
-------------------------------------------------
| 1|___data____|____data____|icon|icon|icon|icon|  
| 2|___data____|____data____|icon|icon|icon|icon|   
| 3|___data____|____data____|icon|icon|icon|icon|   
| 4|___data____|____data____|icon|icon|icon|icon|    

Я использую пользовательский форматировщик JQGrid (http://www.trirand.com/jqgridwsiki/doku.php?id=wiki:custom_formatter), чтобы создать «кнопки» значка в каждой строке (я не могу получить HTML-код кнопки с сервера).

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

    function removeFormatter(cellvalue, options, rowObject) {       
        return "<img src='img/favoritesAdd.gif' onclick='remove(\"" + options.rowId + "\")' title='Remove' style='cursor:pointer' />";
    }

Итак, я могу придумать два варианта:
1) встроенный onclick, как я объяснил выше
--или--
2) delegate() (как указано в ответах ниже (большое спасибо!))

  1. Создайте изображение значка (каждый тип значка имеет собственное имя класса), используя пользовательский форматтер.
  2. Установите для значка data() его параметры в событии afterInsertRow JQGrid.
  3. Примените обработчик delegate() к кнопкам определенных классов (как сказал @KenRedler ниже)
>    $('#container').delegate('.your_buttons','click',function(e){  
>      e.preventDefault();  
>      var your_param = $(this).data('something'); // store your params in data, perhaps  
>      do_something_with( your_param );  
>    }); //(code snippet via @KenRedler)

Я не уверен, насколько интенсивным для браузера является вариант № 2, я думаю... но мне нравится держать Javascript подальше от моих элементов DOM :)


person icats    schedule 14.03.2011    source источник
comment
Делегирование событий здесь — единственное разумное решение.   -  person Felix Kling    schedule 14.03.2011


Ответы (3)


Поскольку вам нужно не только общее решение с некоторыми объектами-контейнерами, но и решение для jqGrid, я могу предложить вам еще один способ.

Проблема в том, что jqGrid уже делает несколько привязок onClick. Таким образом, вы не потратите больше ресурсов, если просто воспользуетесь существующим в jqGrid обработчиком событий. Вам могут пригодиться два обработчика событий: onCellSelect и beforeSelectRow. Чтобы иметь поведение, максимально близкое к тому, что у вас есть сейчас, я предлагаю вам использовать beforeSelectRow событие. Преимущество заключается в том, что если пользователь нажмет одну из ваших пользовательских кнопок, выбор строки может остаться неизменным. С помощью onCellSelect сначала будет выбрана строка, затем вызывается обработчик события onCellSelect.

Вы можете определить столбцы с помощью таких кнопок, как следующие

{ name: 'add', width: 18, sortable: false, search: false,
  formatter:function(){
      return "<span class='ui-icon ui-icon-plus'></span>"
  }}

В приведенном выше коде я использую пользовательский форматировщик jqGrid, но без привязки событий. Код

beforeSelectRow: function (rowid, e) {
    var iCol = $.jgrid.getCellIndex(e.target);
    if (iCol >= firstButtonColumnIndex) {
        alert("rowid="+rowid+"\nButton name: "+buttonNames[iCol]);
    }

    // prevent row selection if one click on the button
    return (iCol >= firstButtonColumnIndex)? false: true;
}

где firstButtonColumnIndex = 8 и buttonNames = {8:'Add',9:'Edit',10:'Remove',11:'Details'}. В вашем коде вы можете заменить alert на соответствующий вызов функции.

Если вы хотите всегда выбирать строку при нажатии кнопки, вы можете упростить код до следующего

onCellSelect: function (rowid,iCol/*,cellcontent,e*/) {
    if (iCol >= firstButtonColumnIndex) {
        alert("rowid="+rowid+"\nButton name: "+buttonNames[iCol]);
    }
}

При использовании одного существующего обработчика событий click, привязанного ко всей таблице (см. исходный код) и просто скажите jqGrid, какой дескриптор вы хотите использовать.

Я рекомендую вам дополнительно всегда использовать gridview:true, которые ускоряют сборку jqGrid, но которые нельзя использовать, если вы используете функцию afterInsertRow, которую вы рассматривали как вариант.

Демонстрацию можно посмотреть здесь.

ОБНОВЛЕНО. Еще один вариант, который у вас есть, — использовать formatter:'actions' см. демонстрация подготовлена ​​для ответа. Если вы посмотрите на код средства форматирования «действий», он будет работать в основном так же, как ваш текущий код, если вы посмотрите на него со стороны привязки событий.

ОБНОВЛЕНО 2: обновленную версию кода вы можете увидеть сюда.

person Oleg    schedule 14.03.2011
comment
@icats: я тоже так думаю и написал предложение после написания ответа на ваш вопрос. - person Oleg; 15.03.2011
comment
Спасибо огромное!!! Ваш ответ невероятно лаконичен и полезен. Я очень ценю демо!!! Как вы упомянули в своем ответе на вопрос, указанный выше, похоже, что настраиваемый тип формата «действия» не совсем полностью завершен/хорошо задокументирован: средство форматирования является просто экспериментальным и будет описано в следующих выпусках. Я с нетерпением жду следующего выпуска JQGrid! Итак, я собираюсь использовать метод обработчика событий «beforeSelectRow», который вы описали выше. Это прекрасно и довольно просто! Я думаю, что я слишком много думал внутри коробки :). - person icats; 15.03.2011
comment
Кроме того, ваше предложение команде JQGrid идеально. Это было бы очень кстати! - person icats; 15.03.2011

Вы должны использовать метод .delegate() для привязки обработчика одного клика для всех элементов через jQuery к родительскому элементу. элемент всех кнопок.

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

person Gabriele Petrioli    schedule 14.03.2011
comment
+1 Но наиболее интенсивной частью этого будет создание и вставка всех этих кнопок в DOM, и разве он не должен привязывать обработчик кликов к кнопкам до того, как они будут вставлены в DOM? - person mattsven; 14.03.2011
comment
@motionman, я предполагаю, что элементы создаются на стороне сервера, а не на лету с помощью javascript. Что касается привязки обработчика, нет. Весь смысл делегата заключается не в том, чтобы привязываться к каждому элементу, а в родителях тех, которые также получат событие из-за всплытия. Таким образом, одна привязка будет обрабатывать все клики (даже если они добавлены после привязки, поскольку привязка происходит к другому элементу). - person Gabriele Petrioli; 14.03.2011
comment
@motionman - я уточнил свой вопрос выше, большое спасибо за ваш вклад! Я создаю элементы на лету с помощью Javascript. - person icats; 15.03.2011
comment
@icats вы должны посмотреть на @oleg ответ. - person Gabriele Petrioli; 15.03.2011

Рассматривали ли вы возможность использования delegate()? У вас будет один обработчик для элемента контейнера, а не сотни. Что-то вроде этого:

$('#container').delegate('.your_buttons','click',function(e){
  e.preventDefault();
  var your_param = $(this).data('something'); // store your params in data, perhaps
  do_something_with( your_param );
});

Предполагая общую компоновку следующим образом:

<div id="container">
  <!--- stuff here --->
  <a class="your_buttons" href="#" data-something="foo">Alpha</a>
  <a class="your_buttons" href="#" data-something="bar">Beta</a>
  <a class="your_buttons" href="#" data-something="baz">Gamma</a>
  <a class="something-else" href="#" data-something="baz">Omega</a>
  <!--- hundreds more --->
</div>
person Ken Redler    schedule 14.03.2011
comment
Большое спасибо @KenRedler! delegate() и data()- Я чувствую себя довольно глупо, не зная об этом. Я добавил уточнение к моему вопросу выше - delegete() похоже, что это сработает для меня, если я все настрою правильно, но я не совсем уверен в наиболее эффективном способе реализации того, что мне нужно сделать. - person icats; 15.03.2011