LazyLoad для товаров MODx Evolution

Напишу, как это делал я, заодно опишу некоторые подводные камни.

В общем-то, как выяснилось, прикрутить это дело не сложно, если бы не некоторые «Но». А, как известно, на каждое ваше «Но» есть наше «За то» 🙂 Начнем, пожалуй…

Сначала расскажу что мы имеем и почему это может вызвать трудности. У нас есть 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), тогда крутить до конца страницы не придется — триггер будет срабатывать при скролле обертки, что уже гораздо раньше.

Ну, на этом вроде бы, все… Вопросы? Предложения?

0

Добавить комментарий