Pull to refresh

Comments 58

Помоему, куда более правильное решение — оборачивать все «приложения» (будем придерживаться вашей терминологии) в конструкцию:
$('.my_app').each(function(){
    var $app = $(this);
});
Этим мы избавляемся от инициализации, формируем для каждого экземпляра свой контекст и получаем возможность использовать несколько приложений на странице.
вы не поняли… или я неправильно выразился… Попробую еще раз.

предположим, у вас есть «приложение», описанное в js-файле, который подключается ко всем страницам.
в этом «приложении» используется сотня-другая классов и айдишников (ну, условно)

а вот человек заходит на страницу, где этого «приложения» нету.
но тем не менее, скрипт будет упорно сканировать DOM на наличие всех узлов, что вызываются на window.onload в этом «приложении».

Чтобы избежать пустых сканирований, мы просто проверяем, а есть ли вообще на странице данное «приложение», путем проверки наличия его родительского контейнера в DOM. Если этого контейнера нет, то и все вложенные в проверку вызовы не будут обрабатываться.
… или вы не поняли.
$('.my_app').each(function(){
    var $app = $(this);
    var $button1 = $app.find('.button');
    var $slider = $app.find('.slider');

    // навешивание событий, другие вычисления.
});

а, да, действительно, я не понял.
если $('.my_app') не найдется, то и все внутри не будет выполняться.
подтормаживаю к ночи :)

да, это тоже вариант.
но:
1. редко когда встраивание сложных js-«приложений» обходится без наличия родительского контейнера с неким ID
2. $('.my_app').each() работает все же значительно медленней, чем if (document.getElementById('my_app')){}, а результат даст тот же (ну кроме контекста)

Т.е. тут уже зависит от рода «приложения», что лучше выбрать.
Но то, что подобные проверки делать стОит, думаю, сомнений не вызывает)
А зачем, по вашему, существует .live()? Как раз для таких задач. Прослушка ивентов байндится на document, в независимости от количества нодов. И Вы избавляетесь от всех вышеперечисленных головных болей.
То есть, я хотел сказать, что одним из плюсов, но не единственным, использования .live() является прослушивание document.
а если используется prototype или mootools?
UFO landed and left these words here
событие в любом случае всплывает вверх по дому, если этот процесс вручную не останосить.
UFO landed and left these words here
чукча не читатель, чукча писатель. это я о себе.
простите, не уловил контекст.
А есть «разработчики», которые делают по-другому?
Миллион их. Недавно разбирался в коде одного западного магазина, так там 500кб ужатых скриптов тянулись на каждой странице.
их много больше, чем тех, кто это делает.
да что там говорить, я и сам только недавно об этом стал задумываться. раньше про производительность вообще не думал, последнее время вот скорости захотелось )))
А зачем тут цикл? Предположим у меня есть функция, которая вешается на '.class' при DOM ready. Какой смысл мне это делать количество раз, равное количеству классов?
Потомучто у вас скорее всего есть еще «функции» (не знаю, как вы функции на элементы вешаете, обычно вешают на события), которые вешаются на ".class .class1" и ".class .class2" и они работают с общими данными, которые для разных ".class" свои.
Неверно выразился. Естественно на класс функцию не повесишь.
Код из песочницы(по методу автора):
$(document).ready(function() {

    if($('.element').length) {
      $('.element').myUserFunction(200,23);
    }

    if($('.element2').length) {
      $('.element2').myUser2Function(24332,123);
    }

});


* This source code was highlighted with Source Code Highlighter.

Лично я просто проверяю входящие параметры в функциях и пока не очень понимаю будет ли удобнее писать 20-30 конструкций if и тем самым засорять domready.js. И будет ли это быстрее. Как по-вашему должен выглядеть этот код?
Протупил. Про цикл, когда элементов==0 понял. Однако всё равно вижу этот вариант менее красивым, чем проверки в самих функциях, а какие-либо действия в DOM-ready неудобным.
Загрузили дерево->вызвали функцию->функция проверила существование всех необходимых структур и либо выполняется, либо нет.

Может быть для маленьких приложений код хранить в domready.js и допустимо, но когда мы имеем хоть сколько-нибудь большое приложение сопровождать 300-500 строк мешанины становится трудно.
Загрузили дерево->вызвали функцию->функция проверила существование всех необходимых структур и либо выполняется, либо нет.

ну так я практически то же самое в топике описал, только проверяю ДО вызова функции на наличие контейнера, к которому эта функция привязана — так быстрее же.
По мне так вызовы сжатых скриптов вполне себе корректно добавить на страницу в виде inline-тега, over 9000 вызовов document.getElementById(...) после загрузки страницы это тоже так себе решение.
ну, многие не любят inline-теги…
а так, если добавить проверки, то количество вызвов будет не over 9000, а сведется к количеству «приложений» которые в принципе описаны + то, что реально нужно вызвать
может лучше что-то вроде:

scriptsFor("/Home/", function() {
  // ...
});



Убиваются 2 зайца:
1) не грузится лишнее;
2)скрипты станицы исполняются в своем скопе, что исключает возможные ошибки мержинга скриптов.

function scriptsFor(pageName, initFn) {
  if (window.location.pathname == pageName)
    initFn();
}

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

$(function (){
  window.UI = (function() {  
  return {
    dom : {
      //Сюда складываем все элементы которые в большинстве своем есть на всех страницах.
      Menu : $('#menu'),
      cCont : $('#content-container')
    },
    init: function () { //Функция инициализации UI страницы. Приложения, как правило, я выношу отдельно, чуть ниже.
      var _dom = this.dom, argLength = arguments.length, i = 0, Content = $('.content', _dom.cCont);

      //Делаем что-нибудь с контентом\блоком, который есть на всех страницах
      Content.css({opacity:0.3, background:'red'});
      _dom.Menu.fadeOut();
      $('.active', _dom.Menu).css({"opacity":0.8});
      //Шерстим входные параметры и делаем строго то, что надо
      for ( ; i < argLength; i++ ) {
        switch(arguments[ i ]) {
          case 'index' : //К примеру, только главная страница - UI.init('index');
            $('.button', _dom.cCont).click(function(){
              location.href="/products/";
            });
          break;
          case 'content' : //Заголовок в блоке контент получит красную рамку - UI.init('content');
            $('h1', _dom.cCont).css({'border':'1px solid red'});
          break;
        }
      }
    }
   }
  })();
});


* This source code was highlighted with Source Code Highlighter.


Понятное дело что эта хм… паттерн срабатывает по дом реади и первым делом выхватывает из дома, только те элелменты которые есть.
Затем обработка каких-либо обязательных элементов для всех страниц — меню, подменю, и т.д. Затем частные случаи которые могут быть
разделены как по видам страниц, по приложениям и т.д. Естественно в теле страницы сперва идет ссылка на js, а затем вызов функции — UI.init('index', 'content');
запредельная конструкция) как-то так сразу не вкуривается…
//Несмотря на то, что весь этот паттерн лежит в js файле он обернут в стандартный jquery
//метод $(document).ready, $(function () { бла бла бла }) это краткая его запись.
$(function (){
 //Далее, мы создаем объект для всей страницы (window.UI) фактически это инкапсулированная функция которая возвращает объект.
 //Иными словами,когда случается документ реади запускается эта функция и возвращает вкусный и красивый объект по заданным параметрам.
 window.UI = (function() { 
  return {
    /*Этот элемент, это некий сторадж, где мы можем предварительно указать дефалтные значения обязательных блоков страниц,
    таких как меню, подменю, всякие холдеры, контэйнеры и т.д. Эти элементы будут найдены и закэшированны первым делом.
    В дальнейшем, эти элементы могут быть использованы как отправные точки для поиска более частных блоков. Кэш в элемент
    необходим для того чтобы можно было их использовать а) как внутри объекта this.dom.Menu, так и за его пределами
    UI.dom.Menu б) мы избегаем лишних запросов jquery на поиск элемента. вызывать 20 раз $('#menu') не айс.*/
    dom : {
      Menu : $('#menu'),
      cCont : $('#content-container')
    },
    /*Это основная фишка. Функция инициализации UI страницы. Аргументами мы передаем функции команды на то, что надо сделать со
    страницей. Например, вызываем этот паттер с параметром index - UI.init('index') и будьте уверены, что выполнится все что
    находится в кейсе index и все что находится в корне функции init. Параметров можно передавать несколько -
    UI.init('index', 'content', 'banner'); Я ее слегка запилил и она ничего не возвращает, но лучше бы она хоть что-нибудь
    да возвращала, например, еще один объект :). */
    init: function () {
     var _dom = this.dom, // тут все понятно, это наш сторадж
     argLength = arguments.length, //кол-во элементов переданных этой функции
     i = 0, //изюминка
     //Кэшируем блок контента (таки опять чтобы его не искать 300 раз) область поиска закешированный ранее content-container.
     //Обратите внимание что это приват.
     Content = $('.content', _dom.cCont);
     
     //Какие-либо манипуляции с элементами. Примеры внизу я написал от балды, главное то, что эти манипуляции лежат над циклом
     //перебора аргументов и будут выполнены независимо от того какие параметры были переданы.
     Content.css({opacity:0.3, background:'red'});
     _dom.Menu.fadeOut();
     $('.active', _dom.Menu).css({"opacity":0.8});
     //Этот цикл переберает переданные аргументы, ну, и, делает то, что сказано.
     for ( ; i < argLength; i++ ) {
      switch(arguments[ i ]) {
       //Собственно навешивание определенных действий на входной параметр.
       case 'index' : //К примеру, только главная страница - UI.init('index');
        /*Тут небольшой затык - если следовать правилам хорошего тона, то мы не можем тут объявлять переменные
        (var button = $('.button', _dom.cCont); ), но наверно, это не ООП, а его пародия :) так что можно и забить. По правилам
        хорошего тона var внутри функции должен быть 1 раз, и он несколько выше, но это сильно усложняет поддержку кода и
        вызывает путаницу. Короче, тут надо еще подумать :) */
        $('.button', _dom.cCont).click(function(){
         location.href="/products/";
        });
       break;
       //Команда на другую последовательность действий
       case 'content' : //Заголовок в блоке контент получит красную рамку - UI.init('content');
        $('h1', _dom.cCont).css({'border':'1px solid red'});
       break;
      }
     }
    }
  }
  //Вот как-то так. Тут используется инкапсуляция, замыкания во все поля, пародия на ооп. Если вы знаете что все это такое,
  //то разберетесь быстро, придумаете усовершенствования, найдете хлипкие места и предложите свои поправки в комментариях ниже
 })();
});


* This source code was highlighted with Source Code Highlighter.


<!--В html вызов всей этой фигни выглядит так:-->
  <!--Файлик с нашим хрен-разберешся-паттерном-->
  <script type="text/javascript" src="/j/index.js"></script>
  <script type="text/javascript">
    $(function (){ //$(document).ready
      UI.init('index'); //Вызов того, что надо сделать.
      //или так - UI.init('index', 'content'); т.е. выполнить и то, и это, и все общее.
    });
  </script>
</body>


* This source code was highlighted with Source Code Highlighter.


Плюсы очевидны — используя тотальное кэширование и замыкания мы уменьшаем скорость обработки скрипта в целом; входящими параметрами мы строго регламентируем действия над страницей — мы не делаем ничего лишнего; все собрано в одном месте; на выходе у нас получается вкусный объект, который можно использовать в других плагинах.
красиво, ничего не скажешь…
я с ооп пока никак не подружусь, поэтому это все пока с трудом перевариваю, но это вопрос времени)
единственная проблема, что мы опять же упираемся в то, что должны заранее знать, что есть на странице…
> должны заранее знать, что есть на странице
UI.init можно делать не дожидаясь дом-реди, а прописывать инлайном сразу после ui-компонентов, особенно если /j/index.js не подгружает скрипты для ui
сбор функционала в зависимости от требований к «страницам» с произвольным кодом к каждой. должно очень хорошо подходить для сайтов, где ui-компоненты не раскиданы по контенту в произвольном порядке. в общем, возьму на заметку, спасибо.
Я тут придумал ваще охрененный способ: подключать только тот javascript, который используется на странице.
Сейчас напишу об этом пост и начну его словами «Навеяно вот этим топиком»…
Не забудьте добавить про

$('#my_div').find('a').css('color','red')

и

$('a').each(function(){
  if ($(this).parents('div#my_div').length)
    $(this).css('color','red') 
});


Про это будет второй пост, навеянный вашим комментарием.
С удовольствием почитаю. Побльше бы таких топиков про очевидные вещи, сотни раз описанные даже в документации на официальном сайте.
1. плодятся запросы к серверу
2. не всегда можно указать каждой конкретной странице загрузку отдельных скриптов
3. не всегда известно, что именно будет на каждой конкретной странице
1. Какие запросы к серверу? Пихать ручками все скрипты в один файл не есть хорошо. Намного удобней хранить всё в отдельных файлах и мергить их автоматом.
2. Я всегда могу указать каждой странице какие скрипты она должна грузить. Опять же в идеале любая часть приложения должна указать какие скрипты ей нужны. Тогда и грузится будет только то, что надо.
3. У вас разработка сайтов превратилась в какую-то лотерею. Пора взять приложение в свои руки=)

И я полностью согласен с текстом вашего топика. НО. Вы описываете частный случай оптимизации селекторов в jQuery. Причём абсолютно однобоко, с позиции своих косяков, на которых вы обожглись. Господин Volshebnyi на три комментария выше привёл пример. Оптимизировать нужно всё и везде. И топиков на эту тему по всему инету целые пачки.
далеко не всегда можно указать каждой странице скрипты.
это если приложение полностью самописное — то да.
но если сидит на какой-нить CMS, то такой функционал реализовать проблематично. да и не желательно это делать («любая часть приложения должна указать какие скрипты ей нужны»©).
и если у клиента в админке есть кнопка «добавить приложение на страницу» — то вы совсем не знаете, что где будет.

один JS-файл со всеми скриптами сайта — это хорошо и правильно, т.к. делается всего один запрос к серверу, скрипт кешируется у клиента и все. с точки зрения оптимизации именно так и нужно.
дергать отдельные скрипты с отдельными функциями — плодит запросы.
генерировать для каждой страницы отдельный смерженый скрипт — тоже плодит запросы и трафик.
а использование проверки, как в топике, даст возможность спокойно собрать все скрипты в один файл и разгрузить js на странице, даже не зная заранее, что где находится.
Почему, когда клиент нажимает на кнопку «добавить приложение на страницу» нельзя добавить javascript для этого приложения?

«один JS-файл со всеми скриптами сайта — это хорошо и правильно»
молодец, похвалил себя. Если сайт на три страницы, то да, может так и лучше. А главное проще. Никому хуже не будет. Но мы же говорим про большие сайты. Где клиент может сам приложения добавлять. Ну и вот я гружу на телефоне через GPRS страницу для печати с вашего сайта с небольшим текстом, но бооольшим таким javascript файлом. Трафику хреново, но зато правильно. Хорошо ещё гугл не грузит javascript со всех своих сервисов одним файлов.
«Почему, когда клиент нажимает на кнопку «добавить приложение на страницу» нельзя добавить javascript для этого приложения?»
— можно. но если вставлять его inline, то это +к трафику (потому что скрипт не закешируется и будет заново загружаться на каждой странице, где есть это приложение.). А если вставлять тег скрипт в шаблон самого js-приложения, то он будет находиться внутри тела страницы, что является нарушением strict-а. А я очень не люблю спеки ломать)

«молодец, похвалил себя»
— не себя, а, например Yahoo! и webo.in

«Где клиент может сам приложения добавлять»
— если что, я имел ввиду клиента-заказчика сайта (т.е. он в админке может прицепить к странице ту же галерею, например), а не посетителей. когда речь о всяких «виджетах», которые посетитель может добавить себе на страницу, то там каждому отдельному посетителю в идеале генерировать свой яваскрипт. но часто ли вы делаете такие сайты?)

«Ну и вот я гружу на телефоне через GPRS страницу для печати с вашего сайта с небольшим текстом, но бооольшим таким javascript файлом»
— ну не надо утрировать. страница для печати должна отдаваться без скриптов и графического оформления. отдельный маленький цсс и никакого шуму.
И Yahoo и webo.in рекомендуют вставлять тэг скрипт перед закрывающим тэгом body. Так что там насчёт того, что нельзя вставлять javascript в тело страницы? Да и кто вам мешает вставлять его в head? Или вывести приложение в одном месте, а скрипт подключить в другом это очень сложно?

И почему вы так боитесь сделать доп. запрос к javascript файлу, но совершенно не боитесь за мой трафик?
И я не утрирую. А вот вы утрируете javascript в один файл.
что советуют вниз — знаю. но это нарушает стандарты, поэтому я скептически отношусь к этому. но и тот и другой советуют сливать файлы в один.

вы сейчас говорите о случае, когда и фронтенд и бэкенд самописные.
тогда все это можно.
но если вы пишете фронтенд, которые навешивается на CMS в роли бэкенда, то там столько свободы не будет и «вывести приложение в одном месте, а скрипт подключить в другом» скорее всего не удастся.
я говорю со второй точки зрения, т.к. бэкенд не пишу.

и, кстати, совсем забыл, если уже не экономить на запросах, то необходимые яваскрипты для работы некоторых «приложений» можно догружать прямо из основного скрипта, хоть тем же document.write…
Я прямо засомневался про стандарты и решил проверить. Создал HTML файл, ставил в body тэг скрипт и протестил в w3c валидаторе с разными доктайпами. Файл прошёл валидацию. Стандарты не нарушены.

Я не говорю про случай, когда бэкэнд и фронтэнд самописные. Если CMS или CMF или Framework не помогают в разработке, а ограничивают её, то в топку. А скорее всего вы просто не хотите или не можете что-то дописывать или прикручивать.

«и, кстати, совсем забыл, если уже не экономить на запросах, то необходимые яваскрипты для работы некоторых «приложений» можно догружать прямо из основного скрипта, хоть тем же document.write…»
что-то сплошные крайности. В этом случае запрос точно не закэшируется.
*поперхнулся, не поверил, пошел в валидатор, проверил, уронил челюсть*
всю жизнь валидатор ругался на скрипты, находящиеся не в head.
опа, нате, перестал.
нуу… ну ладно, это многое меняет.
> один JS-файл со всеми скриптами сайта — это хорошо и правильно
лишний запрос, яспень, плохо, но прогрузка лишнего кода в больших объемах ничем не лучше.
у меня был сайт, где для супер-калькулятора было ~400 кило скрипта, потребного исключительно на этой странице — вот его точно не к чему засовывать в файл, подключаемый ко всем другим страницам.
ну да, тут, что называется, нужно искать баланс
и да, это работает совсем не только с джейквери, а с чем угодно, будь то даже голый яваскрипт.
просто проверка перед выполнением чего-либо, есть ли на странице то, чем нужно оперировать
Либо я вас плохо понял, либо

$(function(){
  if (!$('#my_app').length) 
    return false;

  /*Продолжаем*/
});
ровно то же самое, только я проверяю перед вызовом функции, а не после. так быстрее.
Ну это jquery так можно делать — описывать действия над чем то, не узнав есть ли он. А например в Prototype такое действие просто вызовет ошибку.
ну вот, тем более нужно проверять)
Неплохо бы еще проверять тег элемента, а то вдруг он изменился (поменялся через другую функцию):
if(document.getElementById("results_table") && 
   document.getElementById("results_table").tagName=="TABLE")) { 
 foobar(); 
} 
Тогда уж так :):

var t = document.getElementById("results_table") ;
if(t && t.tagName=="TABLE")) { 
 foobar(); 
} 
Это слишком просто.
Пишите посты, пожалуйста, которые могут быть полезны людям, уровень которых повыше, чем «первый день программирую».
ну, тут все-таки не только маститые профи собрались.
к тому же, есть контингент верстальщиков, которые хорошо знают html/css, но в яваскрипте только начинают.

и да, простые решения это всегда хорошо, имхо.

да и 21 добавление поста в избранное говорит о том, что кому-то он был полезен :)
А вы сами любили Хабр, если бы тут были такие статьи?
Если использовать селекторы типа '#id .class ' — то в движке Sizzle исходным множеством и так будет document.getElementById и все его чайлды. И если такого элемента нет, то и шерстить в поиске .class не будет :)
Sign up to leave a comment.

Articles