Пользователь
0,0
рейтинг
9 августа 2012 в 21:46

Разработка → Про jQuery и велосипеды — мое дополнение из песочницы

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

Для затравки начнем с простого.

1. Пишите так, как вы говорите это вслух


Ваша задача перед другими программистами показать, что вы делаете, а не как вы это делаете.

Прежде чем написать очередную порцию кода, задумайтесь и проговорите вслух или про себя то, что вы собираетесь сделать. Например: «Нужно бы очистить этот элемент». Многие сразу, не задумываясь, лепят:
$(".info").html("")

потому что раньше использовали удобную фразу .innerHTML = "". Более же предпочтительный вариант:
$(".info").empty()


Эти же программисты часто очищают элемент перед тем, как поместить в него новую информацию:

$(".info").html("").html("<b>Ok</b>")


Но не забывайте – вы пишете на jQuery! А он заботится о вас, и .html() сам очистит элемент перед вставкой нового значения.

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

$(".info").remove()


Далее…

Вряд ли при написании вот такого:

$("#history").css("display", "none")
$("#description").css("display", "none")
$("#category").css("display", "none")
$("#contact").css("display", "none")


вы проговаривали: «Удалить историю, удалить описание, удалить категории, удалить контакты». Скорее всего, вы сказали: «Удалить историю, описание, категории и контакты». Так напишите же это:

$("#history, #description, #category, #contact").hide()


и не забывайте, что jQuery любит вас, и что для скрытия элемента есть .hide(), а для показа – .show().

2. mouseenter/mouseleave VS mouseover/mouseout


Вы, наверное, замечали неприятность: иногда, когда вы вешаете на элемент пару событий mouseover/mouseout для отображения всплывающей подсказки, происходит мелькание этой подсказки. А происходит это из-за того, что внутри элемента, на который вы навесили обработчики, находится другой элемент. При наведении на него курсора мышки браузер генерирует для вашего внешнего элемента событие mouseout, а для внутреннего – mouseover, после чего опять для внешнего – mouseover, что и приводит к передергиванию.

Но jQuery любит нас и предлагает нам другую пару событий — mouseenter/mouseleave, которые решают эту проблему следующим образом. Для переданных обработчиков делается некая обертка. В момент, когда курсор переходит на внутренний элемент, генерируется событие mouseout для внешнего элемента. При этом jQuery не настолько наивен и не ведется на козни браузера. Скептическая функция-обертка проверяет свойство event.relatedTarget – то, на что навели теперь курсор – и если оно находится внутри внешнего элемента, то не вызывает переданный вами обработчик mouseleave. И никаких мельканий.

В довесок к этому, не забываем про функцию .hover(handlerIn, handlerOut), которая принимает два обработчика и навешивает их как mouseenter и mouseleave:

$(selector).hover(function() {
    tooltip.show()
}, function() {
    tolltip.hide()
})

UPD: тов. Waxer напоминает нам, что начиная с версии 1.8 .hover() считается устаревшей функцией.

3. .parent() VS .closest()


Нередко натыкаюсь вот на такую конструкцию:

var person = $(".name").parent().parent().parent()


Понятно, что здесь представлена попытка добраться до важного родителя, у которого есть важная информация, или в котором находится другой нужный элемент. А что делать, если пока вы были в отпуске, ваш $(".name") приютился в другом месте, но в рамках все того же person? Многие умельцы циклически вызывают .parent() до нужного элемента.

Более прошаренные знают, что есть .parentsUntil(selector), который вернет всех родителей до указанного (исключая его). Но все равно решение громоздко:

var person = $(".name").parentsUntil(".person").last().parent()


Но есть более наглядный способ:

var person = $(".name").closest(".person ")


Если вспомнить, что мы пишем код таким, каким бы мы выразили его словами, то указанный вариант больше подходит своей прозрачностью и лаконичностью.

4. $.ajax() – лучше меньше, да лучше!


Все мы любим AJAX, особенно $.ajax(). А насколько сильно любите его вы? В предыдущей статье другого автора приводились полезности работы с этим компонентом. Приведу парочку своих…

Я вряд ли совру, если скажу, что вы часто – если не всегда – обращаетесь по AJAX к одному и тому же файлу, передавая ему для разных действий разные команды. Вы ведь не плодите кучу файлов для коротких действий, надеюсь? Возможно даже, что вы всегда передаете какой-то параметр, который не меняется; и, возможно, вы всегда возвращаете только json. Давайте слегка упростим себе жизнь, ведь jQuery любит нас:

$.ajaxSetup({
    dataType: "json",
    url: "ajax.php",
    data: {
        user_id: userId
    }
});


Теперь нам не придется каждый раз указывать эти параметры при запросах, достаточно только отличительных для данной операции:

$.ajax({
    data: {
        product_id: productId
    },
    success: function(json) {
        console.log(json)
    },
    alert: "Загрузка товара..." // Для чего? См. ниже
})

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

5. $.ajax() – оповести пользователя


Так… А может теперь оповестим пользователя о своих подпольных делах и будем выдавать ему сообщения при AJAX-запросах? Уверен, вы об этом задумывались, но избегали в силу того, что надо часто писать одно и то же.

Для простоты примера будем выдавать сообщения таким образом:

<div id="popup" class="по-центру-жирным-красным"></div>


И свяжем его с нашими запросами:

$("#popup").ajaxSend(function(event, xhr, options) {
    $(this).text(options.alert || "Подождите...").show()
}).ajaxStop(function() {
    $(this).fadeOut("fast")
})

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

.ajaxStop() вызывается в конце, после того, как все AJAX-запросы отработаны; т.е. если у вас параллельно обрабатывается несколько AJAX-запросов, то обработчик вызовется только один раз – после выполнения последнего запроса.

А если вы хотите выдавать каждый раз новое сообщение в отдельном попапе, то надо совсем немного изменить код:

$("body").ajaxSend(function(event, xhr, options) {
    $('<div class="popup" />')
        .appendTo(this)
        .css({/* координаты */})
        .text(options.alert || "Подождите...")
        .delay(2000) // убрать через 2 сек
        .fadeOut("fast", function() {
            $(this).remove()
        })
})


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

6. $.ajax() – оповести код


Давайте теперь посмотрим на наш попап из примера выше с другой стороны. Допустим, один человек является разработчиком некоторого сложного виджета, в котором отображается информация о текущих событиях на странице. А другой человек разрабатывает саму бизнес-логику и реализует AJAX-запросы. Очевидно, что потребность в виджете могла возникнуть уже после того, как было написано множество AJAX-запросов. Т.е. пока над проектом работает только один человек. Если бы мы любили наш код и своих будущих коллег так же, как любит нас jQuery, то мы бы предугадали потребность другого кода в оповещении его о завершении какого-нибудь нашего AJAX-действия. И сделали бы так:

$.ajax({
    data: {
        action: "search-name",
        name: $("#name").val()
    },
    beforeSend: function() {
        $(searchForm).trigger("start.search")
    },
    complete: function() {
        $(searchForm).trigger("finish.search")
    }
})


Теперь мы можем не беспокоиться, что кто-то в будущем ворвется в наши владения и попортит малину. Нам достаточно сообщить (в комментариях?), что все желающие могут подписаться, если им так будет угодно, на события start.search и finish.search:

$(searchForm)
    .on("start.search", function() {
        // Показать крутящийся прелоадер
    })
    .on("finish.search", function() {
        // Убрать крутящийся прелоадер
    })


Ведь нам дана возможность создавать свои типы событий, генерировать их и подписываться на них. Все просто! :) jQuery любит нас.
@LionMuzzle
карма
47,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (126)

  • –18
    Плюсанул бы, да не могу.
    Однозначно в избранное =)
    • +4
      Добрые люди исправят это.
      • +2
        Возможность комментировать?
        • 0
          Возможно, стоит написать статью, большими красными буквами с предупреждением, что фраза «плюсанул бы, да не могу» приводит к существенным утечкам кармы. А то чуть ли не в каждом топике…
          • 0
            Вообще-то есть «Кодекс авторов „Хабрахабра“»:

            • Я не клянчу карму, я отношусь к изменениям рейтинга спокойно, но вместе с тем стараюсь учесть конструктивную критику и пожелания.
            • 0
              Когда я писал этот комментарий. у меня даже мысли не было сделать подтекст: «Мне слили карму, не могу плюсовать, подкиньте мне кармы».
              • 0
                А сделали.
              • +3
                Думайте как будто вы парсер, есть соответствие строки — будут минуса.
  • +1
    Хорошая статья :)
  • +32
    Пора открывать церковь jQuery.
    • +3
      ведь jQuery вас
      • +6
        тьфу, ведь jQuery любит вас
      • +1
        your body is a jQuery temple
        • +2
          your document.body is a jQuery temple
    • +4
      jQuery любит вас!*

      * по-настоящему любит!
  • +7
    We need moar.
    Если серьёзно, то столько всего оказывается можно сделать лучше и удобнее…
  • +4
    Спасибо.
    Это хорошая идея публиковать статьи в стиле так называемых «best practice».
  • +5
    Дополнение удалось на славу, спасибо. На самом деле, jQuery от версии к версии становится все более удобным и интересным, но часто разработчики по привычке не используют «новшества».
  • +5
    Хм, а зачем parentsUntil, если можно просто .parents? Сразу указываем целевой селектор и вот он у нас.
    • +1
      Часто бывает, что мы находимся в глубине табличной верстки. И нужно добраться до ближайшей строки. Тогда получится что-то типа $(this).parents("tr"), хотя лучше, наверное, $(this).parents("tr:first"), потому что могут быть вложенные таблицы. Вариант с .closest() выглядит так же, как и проговаривается.
      • 0
        Так-то да, имею в виду, что совсем большой конструкцией пользоваться в принципе не обязательно, а так разница очень невелика (по крайней мере на практике — мне редко доводилось рыться в сильно вложенной табличной верстке)
        • +2
          После этой статьи перечитал что есть 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
  • +4
    Мне кажется, что людей, которые пишут
    $(".info").html("").html("<b>Ok</b>")
    

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

      До 100 итераций оба варианты одинаковы до милисекунды. Хотя, мне больше нравится второй вариант.
      • +2
        Многие задавались этим вопросом :) Советую взглянуть: jsperf.com/prop-vs-ischecked/5
        • 0
          Ничего себе какая разница. Так, а почему все не используют checked?
          • +1
            Потому что это хардкор, javascript-хардкор, это настоящий код из Мытищ!!!
            А на самом деле, легко понять, что при использовании checked мы просто получаем ссылку на DOM-элемент и запрашиваем его свойство, а во всех остальных случаях вызываем функции jQuery. Сам вызов функции уже «стоит» достаточно, а сколько проверок они таят внутри — я не смотрел и не знаю.
          • 0
            Потому что никому не нужно проверять состояние чекбокса 1000 раз в секунду?
            • 0
              Да все, туплю. Спасибо :)
      • 0
        jQuery кеширует запросы к DOM и вероятнее всего вы получаете данные именно из него, а не поиск в каждой итерации.
        • 0
          Ну, тут спрашивалось не столько про поиск элемента, сколько про проверку его чекнутости. Хотя да, надо было $("input") вынести за цикл… Пошел проверил… В общем, у меня на 10000 итераций второй вариант медленне на 10 милисекунд, на 1000 — 1 милисекунда. Думаю, можно смело выбирать любой вариант :)
          • 0
            В принципе да, тоже думаю любой. Встретить 10000 чекбоксов на странице это вам не хухры-мухры :)
    • +2
      if (elem.checked)
  • 0
    Безусловно, полезная статья.
    Однако мне кажется, что все эти ситуации возникают от того, что люди не читали документацию jQuery. Всего этого можно избежать с минимальным наличием мысли и знанием документации, которая очень даже хороша.
    • 0
      Мне кажется, все эти статьи призывают людей читать документацию. А под катом каждой статьи объясняется, насколько это бывает полезно. Ведь мы узнаем о всех прелестях jQuery, читая доки и сам код, разумеется.
  • 0
    Статья полезная.
    Понравился подход «сначала проговори — потом пиши».
  • 0
    Отличная статья! В ней как раз указаны некоторые пункты (например, про closest), которые я не стал описывать в habrahabr.ru/post/149237/ (чтобы статья не была слишком длинной)
  • +2
    В дополнение к первому пункту мне еще встречался следующий велосипед:
    $('.nav').removeClass('first').removeClass('second').removeClass('third');
    

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

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

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


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

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

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

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


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

          Но не всегда удобно бывает хардкодить стили. С IE и скрытием строк таблиц у меня были определенные проблемы.
          • НЛО прилетело и опубликовало эту надпись здесь
  • +2
    Хороший стиль изложения, да и по-делу говорите. Продолжайте, пожалуйста!
  • +1
    «jQuery любит вас»

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


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


          Тем более, что не так много экономят shorthand функции
          element.on('click', handler)
          element.click(handler)
          
        • –1
          Вы видать не дочитали или сделали это невнимательно, я вот про это «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.»
          • 0
            Видимо, плохо сформулировал мысль, но я выше как раз написал, что человек говорит то, что не подтверждается фактом. «The docs already call this name «strongly discouraged for new code»», но я не нашел об этом упоминания в доках. Может, я не там смотрю? :)
            • +1
              Вот вам факты blog.jquery.com/2012/08/09/jquery-1-8-released/ #11731: Deprecate «hover» pseudo-event
              • +1
                Спасибо, теперь вижу. Т.е. в 1.8 уже уже ховера не будет. Ну что ж, видимо многих он путает. Просто я никогда не рассматривал .hover() как событие, для меня это всегда была просто вспомогательная функция для навешивания двух событий.
              • 0
                Добавил в статью про deprecated в 1.8
  • –1
    Как я мог пропустить ".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);
    
    • 0
      Поидее, так даже быстрее — jquery объект только один раз создается.
      // Перед итерацией
      var optCreateSel = $("<option />"); // Создаем "selector" для jQuery
      // Непосредственно в самой итерации
      optCreateSel.prop('value', response[k].value).text(response[k].title).appendTo(parentElement);
      
      • 0
        И еще быстрее:

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

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

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

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

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

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

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

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

      «Особые люди не настолько особые, чтобы из за них нарушать правила.» (с)
  • 0
    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 % случаев признак спагетти-кода.
  • +1
    $(«вас»).love();
    • 0
      и мы любим $ за бесплатность
  • 0
    Стиль изложения хороший, статья продумана.

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


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

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

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

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

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

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

    hover() уже заслуженно deprecated.
    В пункте 6 window используется как глобальный склад идентификаторов событий. Когда смотришь на такой код — непонятно кто эти события вызывает. Для подобных вещей в 1.7 добавили $.Callbacks(). И уже пора писать не .bind(), а .on()
    • 0
      На сколько я понял у 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'); })
      })

      Только не нужно исправлять ошибки, говорить что я дублирую евенты, считайте это псевдокодом.
    • 0
      Да, конечно же нужно учить людей писать правильный код. Насколько правильно будет заменить сейчас в статье .bind() на .on()? Еще хочу контекст window заменить на что-то типа $("#seach-form").
  • 0
     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();
    
    

    • 0
      Вы изобралили частный случай, причем совершенно не использовали $.Callbacks =) Триггер событий работает по другому чем $.Callbacks. Если мне нужно много разных событий (отдельно на приложение, отдельно на работу самостоятельных модулей, отдельно на работу просто незначительных действий, то кучу эеземпляров $.Callbacks придется держать по экземпляру на каждый евент, а это уже очень плохая реализация. Евенты же жквери сам держит у себя и избавляет меня от лишней работы. $.Callbacks подходит для закрытых частей которые работают сами по себе.
      • 0
        Я сделал то, что меня попросили, и по-моему читаться оно стало лучше. $.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()
        • 0
          Она плоха реализацией, повторите этот пример, и поймете jsfiddle.net/5mPCR/, это ваш эскиз элегантен, когда дело дойдет до реализации все будет выглядеть более убого, а вот мой вариант перенял вашу элегантность + избавил от проблемы с которой столкнулись ваши коллеги =) Сделайте просто для интереса тоже что и я, но своим способом, только не теоретическую постановку а реально работающий кусочек, сравним элегантность и гибкость. Вот тогда можно будет понять что лучше а что хуже, на словах все по другому, вы ведь не работали с $.Callbacks правильно?:) лишь читали документацию по нему.
          • 0
            jsfiddle.net/MeX9P/

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

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

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

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

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

                Но я это я так делаю, а если не нравится, можно сделать trait с методами on() и trigger(), и добавлять его к классам по мере надобности. Одна сточка на каждый класс, и никаких проблем связанных с jQuery Events.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.