Pull to refresh

Comments 126

Плюсанул бы, да не могу.
Однозначно в избранное =)
Добрые люди исправят это.
UFO just landed and posted this here
Возможно, стоит написать статью, большими красными буквами с предупреждением, что фраза «плюсанул бы, да не могу» приводит к существенным утечкам кармы. А то чуть ли не в каждом топике…
UFO just landed and posted this here
Когда я писал этот комментарий. у меня даже мысли не было сделать подтекст: «Мне слили карму, не могу плюсовать, подкиньте мне кармы».
UFO just landed and posted this here
Думайте как будто вы парсер, есть соответствие строки — будут минуса.
your document.body is a jQuery temple
jQuery любит вас!*

* по-настоящему любит!
We need moar.
Если серьёзно, то столько всего оказывается можно сделать лучше и удобнее…
Спасибо.
Это хорошая идея публиковать статьи в стиле так называемых «best practice».
Дополнение удалось на славу, спасибо. На самом деле, jQuery от версии к версии становится все более удобным и интересным, но часто разработчики по привычке не используют «новшества».
Хм, а зачем parentsUntil, если можно просто .parents? Сразу указываем целевой селектор и вот он у нас.
Часто бывает, что мы находимся в глубине табличной верстки. И нужно добраться до ближайшей строки. Тогда получится что-то типа $(this).parents("tr"), хотя лучше, наверное, $(this).parents("tr:first"), потому что могут быть вложенные таблицы. Вариант с .closest() выглядит так же, как и проговаривается.
Так-то да, имею в виду, что совсем большой конструкцией пользоваться в принципе не обязательно, а так разница очень невелика (по крайней мере на практике — мне редко доводилось рыться в сильно вложенной табличной верстке)
После этой статьи перечитал что есть parents и closest и везде в проекте запенил parents -> closest. На сколько я понял последний метод менее ресурсоемкий:

The .parents() and .closest() methods are similar in that they both traverse up the DOM tree. The differences between the two, though subtle, are significant:

.closest()
-Begins with the current element
-Travels up the DOM tree until it finds a match for the supplied selector
-The returned jQuery object contains zero or one element

.parents()
-Begins with the parent element
-Travels up the DOM tree to the document's root element, adding each ancestor element to a temporary collection; it then filters that collection based on a selector if one is supplied
-The returned jQuery object contains zero, one, or multiple elements
Мне кажется, что людей, которые пишут
$(".info").html("").html("<b>Ok</b>")

очень сложно встретить. Они живут только в вольерах нет.
А вот и есть.
Видимо они из своих вольеров делали один проект, которому мне пришлось переписывать всю клиентскую часть.
Разделяю вашу горечь и жду интересных постов на govnokod.ru
— Видишь суслика?
— Нет.
— А он есть…
Круто! Спасибо! Не могу плюсануть, к сожалению :(
UFO just landed and posted this here
С точки зрения производительности разницы особой нет. Делал замеры в мозиле — различия заметны только при 10000 итераций. Второй вариант дороже на 20-30 милисекунд.
for (i = 0; i < 100; ++i) {
    if ($("input").prop("checked")) {}
    if ($("input").is(":checked")) {}
}

До 100 итераций оба варианты одинаковы до милисекунды. Хотя, мне больше нравится второй вариант.
UFO just landed and posted this here
Потому что это хардкор, javascript-хардкор, это настоящий код из Мытищ!!!
А на самом деле, легко понять, что при использовании checked мы просто получаем ссылку на DOM-элемент и запрашиваем его свойство, а во всех остальных случаях вызываем функции jQuery. Сам вызов функции уже «стоит» достаточно, а сколько проверок они таят внутри — я не смотрел и не знаю.
Потому что никому не нужно проверять состояние чекбокса 1000 раз в секунду?
UFO just landed and posted this here
jQuery кеширует запросы к DOM и вероятнее всего вы получаете данные именно из него, а не поиск в каждой итерации.
Ну, тут спрашивалось не столько про поиск элемента, сколько про проверку его чекнутости. Хотя да, надо было $("input") вынести за цикл… Пошел проверил… В общем, у меня на 10000 итераций второй вариант медленне на 10 милисекунд, на 1000 — 1 милисекунда. Думаю, можно смело выбирать любой вариант :)
В принципе да, тоже думаю любой. Встретить 10000 чекбоксов на странице это вам не хухры-мухры :)
Безусловно, полезная статья.
Однако мне кажется, что все эти ситуации возникают от того, что люди не читали документацию jQuery. Всего этого можно избежать с минимальным наличием мысли и знанием документации, которая очень даже хороша.
Мне кажется, все эти статьи призывают людей читать документацию. А под катом каждой статьи объясняется, насколько это бывает полезно. Ведь мы узнаем о всех прелестях jQuery, читая доки и сам код, разумеется.
Статья полезная.
Понравился подход «сначала проговори — потом пиши».
Отличная статья! В ней как раз указаны некоторые пункты (например, про closest), которые я не стал описывать в habrahabr.ru/post/149237/ (чтобы статья не была слишком длинной)
В дополнение к первому пункту мне еще встречался следующий велосипед:
$('.nav').removeClass('first').removeClass('second').removeClass('third');

вместо лаконичного
$('.nav').removeClass('first second third');
туда же, для смены одного класса на другой:

<ul class="menu expanded">...</ul>

$('.menu').toggleClass('expanded collapsed');


P.S. Вы программируете на PHP?
Я бы еще добавил, что не стоит забывать о такой замечательной вещи, как Deferred Object при работе с ajax и анимациями.

А статья отличная :)
Опишите подробности. Я конечно поищу, но было бы удобно понять суть в комментариях.
Для себя я отметил, что крайне удобно с их помощью обрабатывать все виды асинхронных операций.

Вот тут товарищ довольно хорошо объясняет, в чем соль.
Если я правильно понимаю принцип работы ajaxSetup, то указанный пример ломает параметры для всего кода, который может идти после нашего и не знать, что мы меняли параметры. Нужно как-нибудь восстанавливать стандартные.
Настройки по умолчанию, т.е. стандартные настройки, заданные с помощью .ajaxSetup, не затираются одноименными настройками индивидуального AJAX-запроса. Но их можно изменить вызовом еще одного .ajaxSetup.
Только одно замечание: $.ajaxSetup({data: { new_prop: new_val }}) не сотрет предыдущие параметры data, а только обновит (или создаст) указанные. Во всяком случае, в последней версии jQuery.
Особо спасибо за 4 пункт, очень полезно.
Не останавливайтесь, пожалуйста.
Код типа
$(".info").html("")
может порождать утечки памяти. В jQuery.cache хранятся ссылки на DOM узлы (например на те, на которые были навешаны обработчики). Когда вы затираете узлы innerHTML-ем или html(''), удаленные узлы из кеша не убираются, в отличии от empty().
Собственно, в статье как раз затронут вопрос утечки. Хотя, действительно, я не акцентировал внимание на том, что .empty() предпочтительнее как раз тем, что занимается smart-очисткой элемента.
Но в вашем примере — .html("") — не будет утечек! Потому как jQuery вызовет .empty() перед вставкой нового значения (в данном случае у вас пустая строка).
Я люблю jquery, но ее взаимность порой огрошивает, когда после серии hide/show вместо изначального display:inline-block я получаю display:block.
использую для этого классы — в одном display: none, в другом display: inline-block и меняю их.
Это нарушение основ верстки. HTML код не должен содержать данных о визуальном отображении контента. В идеальном мире.
C каких это пор применение классов для html элементов стало нарушением основ?
Класс html не должен нести информации об отображении элемента. Это теоретически позволяет менять внешний вид сверстанной страницы, не трогая html-код. На практике, разумеется, встречаются всякие плавающие элементы, для которых важно, какой идет за каким прочая, прочая…
Но все-таки вы должны видеть разницу между
<div class="important-info-block"></div>

и
<div class="left inline-block black-border medium-border-radius"></div>


К тому же, конкретно в вашем примере нарушается еще один акт модульности — разделение отображения (CSS) и логики (JS).
Прошу заметить, что я не использую данные классы непосредственно в «статической» верстке для управления видимостью. Класс вводится исключительно для оперирования в скрипте.
UFO just landed and posted this here
У метода show() есть особенность, из-за которой он считается не очень быстрым. Дело в том, что этот метод, перед тем как сделать элемент видимым пытается узнать значение его свойства display, чтобы проставить его правильно. И при проявлении большого количества элементов за раз, show() может вызвать приличные тормоза. Так что для оптимизации производительности в огромных программах лучше все же использовать css(«display»,«block»).
Интересно, никогда об этом не задумывался. jsperf.com/showhide-vs-css/4

Но не всегда удобно бывает хардкодить стили. С IE и скрытием строк таблиц у меня были определенные проблемы.
UFO just landed and posted this here
Хороший стиль изложения, да и по-делу говорите. Продолжайте, пожалуйста!
«jQuery любит вас»

Спасибо, день начался с улыбки ;) Буду теперь весь день это проговаривать
$("#history, #description, #category, #contact").hide()


Мне кажется, это скорее вредный совет в свете наличия все еще ощутимого количества IE младше 9-ой версии. Если я правильно помню, Sizzle при отсутствии querySelectorAll выполняет этот селектор через getElementsByTagName('*') и последующую фильтрацию результата в javascript'е, что трагически сказывается на производительности.
Это справедливо только для элементов к которым обращаются не по id.
UFO just landed and posted this here
Ну например селектор div#id он довольно умно не разберет и не поймёт, что это поиск по id.
По крайней мере в той версии Sizzle, которую я мучал где-то полгода назад, он тупил и выбирал не самые оптимальные планы селекторов, если можно так выразиться.
Конечно, ведь div#id это просто жесть. На то #id и уникальный элемент, что не требует ничего больше. И Sizzle справедливо не пытается оптимизировать разбор этой конструкции.
Раз вы пишите такие статьи, то вам неплохо было бы ознакомиться вот с этим это по поводу второго пункта.
Спасибо. Почитал по вашей ссылке. Насколько я понял, человеку не понравилось, что .hover() выглядит для него как событие, а в обработчике подсовывается тип mouseenter или mouseleave, и что нельзя вызвать событие вручную. Но в документации ясно написано, что .hover() является сокращением для последовательного навешивания указанных обработчиков. Поправьте меня, если я не прав — самому интересно, потому как у тикета стоит статус — closed enhancement: fixed, при этом человек сослался на документацию, в которой, мол, не рекомендуется использование ховера, но на данный момент там об этом ничего не сказано.
Кстати, лично я всегда использую on для прикрепления событий, чтобы явно показать, что я прикрепляю событие, а не делаю trigger события. Позволяет избежать неочевидных проблем и улучшает читабельность.
Пример, который приведет непонятно к чему:
var fn = function(){}
...
//где то в другом месте fn = undefined;
...
$('.spend_the_money').click(fn);


Тем более, что не так много экономят shorthand функции
element.on('click', handler)
element.click(handler)
Вы видать не дочитали или сделали это невнимательно, я вот про это «The docs already call this name „strongly discouraged for new code“, I'd like to deprecate it officially for 1.8 and eventually remove it.»
Видимо, плохо сформулировал мысль, но я выше как раз написал, что человек говорит то, что не подтверждается фактом. «The docs already call this name «strongly discouraged for new code»», но я не нашел об этом упоминания в доках. Может, я не там смотрю? :)
Спасибо, теперь вижу. Т.е. в 1.8 уже уже ховера не будет. Ну что ж, видимо многих он путает. Просто я никогда не рассматривал .hover() как событие, для меня это всегда была просто вспомогательная функция для навешивания двух событий.
Добавил в статью про deprecated в 1.8
Как я мог пропустить ".closest()" во время изучения документации jQuery? LionMuzzle, спасибо, довольно часто мне его не хватало во время работы с DOM.

От себя хочу добавить небольшой protip для jQuery.
Порой нужно добавить элементы в текущую разметку, скажем после ajax запроса, и обычно это делают через:

$('<option />').prop('value', response[k].value).text(response[k].title).appendTo(parentElement);

Но когда таких элементов много, или очень много (представьте подгрузку нескольких тысяч клиентов в список) — это сказывается на производительности. Все дело в селекторе, который создает <option>. К сожалению сейчас не могу привести результаты бенчмарков, но есть очень простое решение проблемы:

// Перед итерацией
var optCreateSel = document.createElement('option'); // Создаем "selector" для jQuery
// Непосредственно в самой итерации
$(optCreateSel).prop('value', response[k].value).text(response[k].title).appendTo(parentElement);
Поидее, так даже быстрее — jquery объект только один раз создается.
// Перед итерацией
var optCreateSel = $("<option />"); // Создаем "selector" для jQuery
// Непосредственно в самой итерации
optCreateSel.prop('value', response[k].value).text(response[k].title).appendTo(parentElement);
И еще быстрее:

// Перед итерацией
var optCreateSel = $(document.createElement('option')); // Создаем "selector" для jQuery
// Непосредственно в самой итерации
optCreateSel.prop('value', response[k].value).text(response[k].title).appendTo(parentElement);
Вообщем не сработает. Ни мой ни ваш пример. Надо добавлять clone()
learn.javascript.ru/play/W4BiPb
можно по отдельности посчитать
Какой чудный protip. Мало того, что неправильно работает, так еще в 5 раз медленнее обычной склейки всех value в строку.
И даже непонятно почему бы не использовать более простую и быструю склейку всех <option .../> в одну строку и делать одну вставку в DOM.
Нужно только не забыть фильтровать данные при таком подходе
Небольшое дополнение к первому пункту. Если нужно показать/спрятать что-то по условию, то вместо
if (someValue)
	$j(selector).show;
else
	$j(selector).hide();

более короткая запись:
$j(selector).toggle(someValue);
не совсем так:
if (true) {
    $j(selector).show();
} else {
    $j(selector).hide();
}

не эквивалентно

$j(selector).toggle(someValue);
а нет, все верно — не разглядел someValue в toggle
Только нужно учесть, что должно быть boolean значение
$('a').toggle(1000) — спрячет все. поэтому запись выше скорее эквивалентна такому выражению:
$j(selector).toggle(!!someValue);
Не согласен с половиной написанного:

1. $(".info").html("") знают все, а за empty — часть людей полезет в доки. «Особые случаи не настолько особые, чтобы нарушать правила.» (с) zen.py

2. $("#history, #description, #category, #contact").hide() — удобнее отлаживать код (комментировать отдельные hide) когда они идут списком.

3. $.ajaxSetup — вообще непонятно какая либа его переопределит, чтобы на него полагаться. Проще написать обычную js функцию делающую запрос с нужными дефолтными параметрами.
$(".info").html("") знают все, а за empty — часть людей полезет в доки.

Ну и пусть лезут, пополнят свои знания.

«Особые люди не настолько особые, чтобы из за них нарушать правила.» (с)
var person = $(".name").closest(".person ")
Способ хороший, но на мой взгляд, когда возникает необходимость искать предка таким способом это говорит о том что что-то не так в королевстве.

Из кода следует, что некая сущность person взывает к своему предку person:
<div class="person">
  <div class="name">Name</div>
</div>

Поправьте меня, но почему код не исполняется в контексте person?
var methods = {
  _getName: function() {
    $(this).find('.name');
    // Do smth.
  }
}

$.fn.my_plugin = function() {
  // Init, etc.
}

$('.person').my_plugin();

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

Но меня почему-то пугает такое количество плюсов к статье.
По-моему не упомянули:
$('.element', '.container')


вместо:
$('.container').find('.element')

Второй вариант более читабельный.
А что насчет производительности?
Может, конечно не в тему, но все же. Интернет у меня нестабильный и пользоваться документацией через браузер плохой вариант. На официальном сайте jQuery я нашел доки в формате chm, но они только для 1.4. Отсюда вопрос: сильно ли изменился jQuery с этой версии и можно ли ими пользоваться? Есть ли где ни будь доки для 1.7?
думаю, можно скачать всю документацию чем-то вроде wget-а.
Вместо $.ajax часто можно использовать более короткую запись: $.get или $.post
Суть этого поста и поста указанного в самом начале в том, что использовать их не круто.
Я бы назвал этот пост «jQuery любит вас» :))

Не знал про ajaxSetup, спасибо!

>Обратите внимание, что параметры data из $.ajaxSetup и $.ajax склеиваются, и серверу посылается их сумма (параметры из $.ajax перезатирают одноименные из $.ajaxSetup).

Все-таки, не перезатирают, а склеиваются.
Под «перезатирают одноименные» я имел ввиду то, что если, скажем, в $.ajaxSetup у вас (мы же про data говорим?) есть параметр user_id, и в $.ajax есть user_id, то серверу отправится второй вариант.
А, я невнимательно прочитал, прощу прощения.
Надо будет, что то аналогичное подобрать но для других библиотек.
Для своих проектов я использую MochiKit, а по работе Dojo.
Может, вы имели ввиду обратное? .html никак не может быть быстрее, так как внутри него вызывается .empty. Опять же: тест1, тест2, тест3.

Вообще, в малых дозах разницы нет, какой способ использовать. Но статья призывает писать более понятным языком, говорящим, что вы делаете, а не как, если это уместно и разумно.
Смотрите исходники внимательней.
github.com/jquery/jquery/blob/master/src/manipulation.js#L249
github.com/jquery/jquery/blob/master/src/manipulation.js#L249
empty используется в html только при вызове исключений, а это не происходит на обычных элементах.
А по тестам да, empty быстрей, но это опять же, из-за очистки элементов поодному из ноды =) Я не заметил сразу что оба метода очищаются одинаково.
Если бы не эта строка было бы все наоборот :( Не знаю зачем они так делают, может избавляются от возможных утечек, но это явно лишняя работа очищать то что на следующей строке очистится само.
В 1.7 лучше использовать .on() вместо .bind()
Плохо когда статьи пишут те, кто не очень разбирается.

hover() уже заслуженно deprecated.
В пункте 6 window используется как глобальный склад идентификаторов событий. Когда смотришь на такой код — непонятно кто эти события вызывает. Для подобных вещей в 1.7 добавили $.Callbacks(). И уже пора писать не .bind(), а .on()
На сколько я понял у trigger и $.Callbacks немного разный функционал.
У первого больше возможностей:
1. можнов ешать на любые элементы
2. можно группировать по евентам
Второе же — утпо свалка калбаков на один евент. Или я что-то не так понял?
Можете повторить мой код с помощю $.Callbacks?

$(document).bind('app.start', function(){
    alert('started!)
})
$(document).bind('app.finish', function(){
    alert('finished!)
})
$(document).bind('app.action.run', function(){
    alert('start action...!)
})

$( function(){
    $(document).trigger('app.start);
    $('#actionButton').bind('click', function(){ $(document).trigger('app.action.run'); })
    $(document).bind('unload', function(){ $(document).trigger('app.finish'); })
})

Только не нужно исправлять ошибки, говорить что я дублирую евенты, считайте это псевдокодом.
Да, конечно же нужно учить людей писать правильный код. Насколько правильно будет заменить сейчас в статье .bind() на .on()? Еще хочу контекст window заменить на что-то типа $("#seach-form").
 var App = { /* magic */ };

 App.on('start', function() { alert('start') })
    .on('stop',  function() { alert('stop')  })
    .on('run',   function() { alert('run')   });
    
 // По хорошему, все что ниже входит в обязанности App
 $(function() {
     App.start();
     $('#actionButton').on('click', $.proxy(App, 'run'));
 });

 $(window).on('beforeunload', $.proxy(App, 'stop'));


Логичнее, не? Функция App.on реализуется на базе Callbacks. Точно так же на базе Callbacks реализованы события в jQuery, но чистый Callbacks более гибкий.

В принципе, события jQuery работают с произвольными объектами, но это не задокументировано, так что лучше не злоупотреблять:

 $(Math).click(function() { alert(this.PI) });
 // ...
 $(Math).click();


Вы изобралили частный случай, причем совершенно не использовали $.Callbacks =) Триггер событий работает по другому чем $.Callbacks. Если мне нужно много разных событий (отдельно на приложение, отдельно на работу самостоятельных модулей, отдельно на работу просто незначительных действий, то кучу эеземпляров $.Callbacks придется держать по экземпляру на каждый евент, а это уже очень плохая реализация. Евенты же жквери сам держит у себя и избавляет меня от лишней работы. $.Callbacks подходит для закрытых частей которые работают сами по себе.
Я сделал то, что меня попросили, и по-моему читаться оно стало лучше. $.Callbacks() используется внутри функции App.on(), я просто не стал разжевывать. Без сахара можно так:
App = {onStart: $.Callbacks};
App.onStart.add(function() { alert('start') });


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

Не понимаю, чем она плоха. Сейчас сотрудники столкнулись с проблемой: есть у нас такое подобие $(document).bind('app.finish'), только без притянутого за уши document. Вызывается оно PubSub.subscribe('app.finish', callback). В один прекрасный день объектов app стало несколько, и все они бродкастят app.finish. А если обработчик надо навесить на какой-то один? Приходится идти на извраты.

$.Callbacks позволяют все изначально сделать правильно, а не складывать все в одну глобальную помойку. В любом UI фреймворке обработчики вешаются на конкретные экземпляры объектов, а не на глобальный диспатчер, как получается в варианте с $(document).bind()
Она плоха реализацией, повторите этот пример, и поймете jsfiddle.net/5mPCR/, это ваш эскиз элегантен, когда дело дойдет до реализации все будет выглядеть более убого, а вот мой вариант перенял вашу элегантность + избавил от проблемы с которой столкнулись ваши коллеги =) Сделайте просто для интереса тоже что и я, но своим способом, только не теоретическую постановку а реально работающий кусочек, сравним элегантность и гибкость. Вот тогда можно будет понять что лучше а что хуже, на словах все по другому, вы ведь не работали с $.Callbacks правильно?:) лишь читали документацию по нему.
jsfiddle.net/MeX9P/

В вашем примере используется недокументированная фича, я бы такое не стал делать в проекте, который планируется поддерживать.
У меня всего два замечания:

1. Ваш app нихрена не выполняет своего кода, в отличии от моего, мой сперва выполняет некие дейсвия, затем информирует об этом всех подписавшихся, у вас же тупо болванка которая ниче не делает но только всех информирует о сделанном, это недоработка, мне бы нормальный код, рабочий.

2. jsfiddle.net/MeX9P/1/ По вашему это нормальный класс виджета? У меня будут лишь методы. Обычный класс, простой и понятный.

ЗЫ: Вместо «недокументированной фичи» я могу использовать имена и неймспейсы, евенты будут вида:
$(element).trigger('app.start.namespace');
namespase — имя app (new App('namespace') );
1. Некие действия — это строка коммента что ли? Да, строку коммента мой код не выполняет. Досадное упущение.
2. Не понимаю что это.
3. Да, пошли извраты. $(element) появился, а там может класс вообще не взаимодействует с DOM, куда ему этот element? Уникальный неймспейс для каждого экземпляра, который не несет смысловой нагрузки, и нужен только чтобы заюзать события jQuery. В каждый колбек первым аргументом приходит jQuery Event, как повидло в борщ. А че, зато бесплатно.

Претензия к Callbacks только в том, что нужно перечислять все обработчики? Я в любом случае всегда явно описываю интерфейс класса, чтобы потом не скролить 5 экранов в поисках того, что может делать класс. Сразу понятно какие у него есть методы, свойства, и на какие события можно подписаться.

Но я это я так делаю, а если не нравится, можно сделать trait с методами on() и trigger(), и добавлять его к классам по мере надобности. Одна сточка на каждый класс, и никаких проблем связанных с jQuery Events.
Only those users with full accounts are able to leave comments. Log in, please.

Articles