Напишу, как это делал я, заодно опишу некоторые подводные камни.
В общем-то, как выяснилось, прикрутить это дело не сложно, если бы не некоторые «Но». А, как известно, на каждое ваше «Но» есть наше «За то» 🙂 Начнем, пожалуй…
Сначала расскажу что мы имеем и почему это может вызвать трудности. У нас есть MODx Evolution, на котором сделан магазин (Shopkeeper), также есть «антидубль«, который показывает товары, указываемые в категориях в админке галочками. Вот как раз это-то и может несколько запутать, но обо всем по порядку.
Изображения
Что касается lazyload для изображений — тут все предельно просто и понятно, всего-то, подключить скрипт jquery.lazyload.js, добавить скрипт в <head> и немного поправить чанк с выводом картинки:
Подключить скрипт:
Вставляем сразу после jquery или jquery.min
<script type="text/javascript" src="/assets/templates/mytheme/js/jquery.lazyload.js"></script>
Добавить скрипт в <head>:
<script type="text/javascript" charset="utf-8"> //<![CDATA[ $(function() { $("img#lazy").show().lazyload(); }); //]]> </script>
Я добавил его сразу перед закрытием <head> после всех остальных скриптов.
Здесь img#lazy — это тег img c id=»lazy»
Поправить чанк:
Открываем чанк, который отвечает за вывод предварительного просмотра товара в категории, ищем там тег img и делаем вот что:
<img src="assets/templates/mytheme/images/grey1x1.png" data-original="[+image+]" id="lazy" />
то есть, в src ставим картинку-заглушку, которая будет показываться до тех пор, пока не началась загрузка настоящего изображения и добавляем data-original=»[+image+]» id=»lazy», т.е. [+image+] переносим из src в data-original.
Сохраняем и видим, что теперь у нас картинки подгружаются по мере появления товара в каталоге при прокрутке. Но это ни разу не цель. Кому-то, может быть, это пригодится, но мне этого было мало, поэтому — продолжаем.
Контейнер DIV при выводе напрямую через Ditto
Как я уже говорил, если с картинками все относительно безоблачно, то с контейнерами пришлось подумать-поискать. Вот что я узнал сам и теперь вам рассказываю 🙂
Чтобы завести lazyload в MODx — нужен оптимизированный под него скрипт jquery.infinitescroll.min.js, как выяснилось. И он есть в паблике вот тут. Качаем архив и подключаем из него jquery.infinitescroll.min.js так же, как и предыдущий jquery.lazyload.js. Кстати, если не планируется оставлять ленивые картинки, тот скрипт можно удалить и вернуть все обратно, как было. Сейчас будет круче.
Вообще, разработчики писали его для WordPress, но заточили его еще и под MODx (о чем говорит текст в ремарке в файле со скриптом), за что им большое, человеческое спасибо.
Теперь открываем все тот же чанк с товаром в категории и внимательно смотрим на div, который заворачивает все это дело. Если его нет (в чем, я лично, сильно сомневаюсь) — заворачиваем в div все это дело. Например, вот так:
<div class="lazyDIV"> <a href="[+url+]"><img src="assets/templates/mytheme/images/grey1x1.png" data-original="[+image+]" id="lazy" width="206" height="206" alt="[+custom-alt-Image+]" title="[+custom-title-Image+]" /></a> <p><a href="[+url+]">[[PageTitleWithoutTags?this_pagetitle=`[+pagetitle+]`]]</a></p> <form action="[~[*id*]~]" method="post"> <input type="hidden" name="id" value="[+id+]" /> <input type="hidden" name="name" value="[[PageTitleWithoutTags?this_pagetitle=`[+pagetitle+]`]]" /> <input type="hidden" name="count" value="1" size="2" maxlength="3" /> <div> [+price:shk_widget=`radio:price:wraptag:first_selected`+] <button type="submit"></button> </div> </form> </div>
это пример моего чанка. Тут много чего самописного, но обратить внимание надо на первую и последнюю строчки. class=»lazyDIV» — это нам еще пригодится 🙂
Теперь внимательно смотрим на шаблон, откуда вызывается сниппет, выводящий товары (пусть это будет Ditto, ибо речь сейчас пойдет о нем). Там наверняка есть примерно такой код:
<div id="productListWrap"> [[Ditto? &parents=`5` &depth=`1` &tpl=`stuff`]] </div>
Смысл в том, что в каком-то контейнере мы вызываем сниппет Ditto, который показывает нам товары.
Теперь изменим его:
<div id="productListWrap"> [[Ditto? &parents=`1` &depth=`1` &tpl=`stuff` &paginate=`1` &display=`20`]] <!-- &paginate=`1` - этим мы включили пагинацию, а этим &display=`20` - указали, по сколько товаров выводить на страницу --> [+next+] <!-- этим мы добавим кнопку "Далее" для пагинации --> </div> <script type="text/javascript"> $('#productListWrap).infinitescroll({ //речь идет о слое, в котором завернут весь вывод товаров в каталоге navSelector : "a.ditto_next_link", //это класс, в котором сидит кнопка "Далее" nextSelector : "a.ditto_next_link", //см. выше itemSelector : "div#lazyDIV" //слой, в который заворачивается вывод одного товара в списке }); </script>
Тут вот что важно — скрипт должен стоять после обертки списка товаров.
Вот, это самый простой пример. Документация по возможностям упирается в это, но этой инфы более чем достаточно.
Если сейчас все «сохранить и запустить» — получим ленивую загрузку для товаров. Но есть несколько «Увы», о них — дальше.
Контейнер DIV при выводе через сниппет, управляющий логикой вывода
Первое (и, пожалуй, самое важное) — это то, что если мы захотим в этом примере сделать ленивую загрузку «красиво», в шаблоне или через чанки — у нас ничего не получится. Выводиться товары будут, а вот подгружаться самостоятельно — не захотят. Для этого придется вкручивать обертку со списком товаров и скрипт в сниппете. Я не стал убирать существующую обертку, я добавил свою со своим ID, чтобы не искать потом в случае чего, откуда ноги отросли.
Выглядеть это будет как-то так (сниппет):
<?php if(!isset($tmplvarid)) $tmplvarid = 1; if(!isset($like)) $like = ''; if($like == '1') { $like = 15; $this_all_url = 1; } else { $this_all_url = $like; } $ids_arr = array(); $query = $modx->db->query(" SELECT `id`, `contentid` FROM `".$modx->db->config['table_prefix']."site_tmplvar_contentvalues` WHERE tmplvarid=".intval($tmplvarid)." AND value LIKE '%:$like:%' "); $result = $modx->db->makeArray($query); foreach ($result as $ar) { array_push($ids_arr,$ar['contentid']); } $ids_str = implode(',',$ids_arr); $res = $modx->runSnippet('Ditto', array( 'parents' => $like, 'tpl' => 'stuff', 'paginate' => '1', 'display' => '6', 'sortBy' => 'menuindex', 'sortDir' => 'ASC', 'documents' => $ids_str )); $divStart = "<div id=\"productListWrap\">"; $divEnd = " [+next+] </div> <script type=\"text/javascript\"> $('#productListWrap).infinitescroll({ navSelector : \"a.ditto_next_link\", nextSelector : \"a.ditto_next_link\", itemSelector : \"div#lazyDIV\" }); </script> "; return $divStart.$res.$divEnd; ?>
В принципе, этот код ничем от описанного в указанной статье не отличается, за исключением того, что добавился вывод слоя и скрипта.
Вывод сниппета такой же, как и был:
[!strDitto?tmplvarid=`23`&like=`[*id*]`!]
Еще один важный момент — это описания категорий снизу от списка товаров. Если описание находится снизу от списка товаров — придется крутить страницу до конца, чтобы сработал триггер и начали подгружаться товары. Для того, чтобы это «исправить» — нужно в файле jquery.infinitescroll.min.js найти строку с текстом (у меня она 22)
if(opts.container.nodeName=="HTML")
и заменить HTML на контейнер, в котором лежит список товаров (тот самый div#productListWrap), тогда крутить до конца страницы не придется — триггер будет срабатывать при скролле обертки, что уже гораздо раньше.
Ну, на этом вроде бы, все… Вопросы? Предложения?