Что нового

Кнопка "загрузить еще" DLE 18.1-19.0

Oleksii Panchenko

Посетитель
Регистрация
18 Фев 2024
Сообщения
34
Реакции
8
Стили были адаптированы для шаблона Green (стандартный)
Инструкция:
Green/navigation.tpl добавить сюда после всех ваших кодов.
Код:
<div class="nav-load" id="btn">[next-link]Загрузить еще[/next-link]</div>

<style>
  #btn{
    display:flex;
    justify-content:center;
    align-items:center;
    margin:18px 0;
    text-align:center;
  }
  #btn a{ text-decoration:none; }
  #btn a.is-loading{
    pointer-events:none;
    opacity:.75;
    cursor:default;
  }
  #btn.is-empty{ opacity:.75; }
  #btn .nav-load-text{ display:inline-block; }
</style>

<script>
$(function () {

  const ITEM_SEL = 'article.block.story.shortstory';

  // ✅ ОПЦИЯ: что делать на последней странице
  // 'hide'    -> полностью убрать блок (самый чистый UX)
  // 'message' -> показать текст
  // 'disabled'-> оставить “задизейбленную” кнопку
  const END_BEHAVIOR = 'hide';

  const END_MESSAGE = 'Больше нечего загружать';
  const DISABLED_TEXT = 'Нет новых записей';

  function parseDoc(html) {
    return $('<div>').append($.parseHTML(html));
  }

  function hasNextLink($wrap) {
    const $a = $wrap.find('a').first();
    return !!($a.length && $a.attr('href'));
  }

  function applyEndBehavior($wrap) {
    if (END_BEHAVIOR === 'hide') {
      $wrap.remove();
      return;
    }
    if (END_BEHAVIOR === 'message') {
      $wrap.addClass('is-empty').html('<span class="nav-load-text">' + END_MESSAGE + '</span>');
      return;
    }
    if (END_BEHAVIOR === 'disabled') {
      // оставляем кнопку, но делаем “неактивной”
      let $a = $wrap.find('a').first();
      if (!$a.length) {
        $wrap.html('<a href="#" class="btn nav-load-btn is-loading" aria-disabled="true"></a>');
        $a = $wrap.find('a').first();
      }
      $wrap.addClass('is-empty');
      $a.addClass('btn nav-load-btn is-loading')
        .attr('aria-disabled','true')
        .text(DISABLED_TEXT);
      return;
    }

    // fallback -> hide
    $wrap.remove();
  }

  function skinLoadMoreBtn() {
    const $wrap = $('#btn');
    if (!$wrap.length) return;

    const $a = $wrap.find('a').first();

    // Последняя страница: next-link отсутствует
    if (!$a.length || !$a.attr('href')) {
      applyEndBehavior($wrap);
      return;
    }

    // Есть next-link
    $wrap.removeClass('is-empty');

    if (!$a.hasClass('nav-load-btn')) $a.addClass('btn nav-load-btn');
    $a.text('Загрузить еще');
  }

  function setBtnLoading(isLoading) {
    const $a = $('#btn a').first();
    if (!$a.length) return;

    if (isLoading) {
      if (!$a.data('old-text')) $a.data('old-text', $a.text());
      $a.text('Загрузка...').addClass('is-loading');
    } else {
      $a.text($a.data('old-text') || 'Загрузить еще').removeClass('is-loading');
      skinLoadMoreBtn();
    }
  }

  // init
  skinLoadMoreBtn();

  $('body').on('click', '#btn a', function (e) {
    e.preventDefault();

    // если режим disabled/message и ссылки нет — просто выходим
    const urlNext = $(this).attr('href');
    if (!urlNext || urlNext === '#') return false;

    const $firstItem = $(ITEM_SEL).first();
    if (!$firstItem.length) return false;

    const $list = $firstItem.parent();

    $.ajax({
      url: urlNext,
      beforeSend: function () {
        setBtnLoading(true);
        if (typeof ShowLoading === 'function') ShowLoading('');
      },
      success: function (data) {
        const $doc = parseDoc(data);

        // 1) Подгружаем ниже только карточки новостей
        const $newItems = $doc.find(ITEM_SEL);
        if ($newItems.length) $list.append($newItems);

        // 2) Обновляем навигацию/кнопку
        $('.navigation').remove();
        $('#btn').remove();

        const $newNav  = $doc.find('.navigation').first();
        const $newLoad = $doc.find('#btn').first();

        if ($newNav.length)  $list.after($newNav);
        if ($newLoad.length) $list.after($newLoad);

        // 3) Применяем поведение кнопки (в т.ч. конец)
        skinLoadMoreBtn();

        // 4) URL (опционально)
        if (history.pushState) history.pushState({}, '', urlNext);
      },
      error: function () {
        alert('что-то пошло не так');
      },
      complete: function () {
        if (typeof HideLoading === 'function') HideLoading('');
        setBtnLoading(false);
      }
    });

    return false;
  });

});
</script>

Как переключать “что делать в конце”​


Вверху JS меняешь одну строку:
Код:
const END_BEHAVIOR = 'hide';     // убрать полностью
// const END_BEHAVIOR = 'message';  // показать текст
// const END_BEHAVIOR = 'disabled'; // оставить неактивную кнопку
Опционально можно добавить:
1) Автоподгрузка (Infinite Scroll) 🔄
Варианты UX
Pure Infinite — грузим автоматически, кнопки нет.
Hybrid — первые N страниц грузим автоматически, дальше показываем кнопку (контроль + меньше нагрузки).
Infinite + “стоп” — автоподгрузка, но с переключателем “Авто/Ручной”.
Рекомендуемая реализация (не scroll, а IntersectionObserver)
Почему: меньше лагов, меньше событий, лучше на мобилках.
Логика
наблюдаем за #btn (или “sentinel” div)
когда он попадает в viewport → триггерим тот же loadNext()
Опции
rootMargin: "300px" — начинать загрузку заранее (не в самый низ).
защита от повторных запросов: флаг isLoading.
лимит авто-страниц: AUTO_PAGES_LIMIT = 3 (гибрид).
2) Счётчик “показано N из M” 📊
Что реально возможно
В DLE “из коробки” нет универсальной переменной “всего постов” на страницу/категорию в DOM (зависит от шаблона). Поэтому 2 стратегии:
Стратегия A (быстро, без бэка): “Показано N” ✅
N считаем просто: document.querySelectorAll(ITEM_SEL).length
показываем “Показано: N”
Плюсы: всегда работает, везде.
Минусы: нет “из M”.
Стратегия B (точно “N из M”): нужен источник M ✅✅
M можно взять из:
заголовка категории/страницы, если там есть “(123)” или “найдено 123”
скрытого тега в шаблоне (лучший вариант):
добавить в шаблон один <meta>/data-total="123" (если DLE предоставляет число)
отдельного эндпоинта/хелпера (серверная доработка)
Итоговый UX
Показано 40 (минимум)
либо Показано 40 из 123 (если есть total)
 
Что-то мне подсказывает, что если подключение jquery перенесено вниз, это работать не будет.
Не вставляйте js в шаблоны, делайте это используя систему плагинов.
 
Что-то мне подсказывает, что если подключение jquery перенесено вниз, это работать не будет.
Не вставляйте js в шаблоны, делайте это используя систему плагинов.
Не совсем понятен ваш комент.
Это же только пример естественно нужно логику
JS подключать в Green/js/
Стили в Green/css/styles.css
HTML там где вам нужно получить кнопку!
 
Стили были адаптированы для шаблона Green (стандартный)
Инструкция:
Green/navigation.tpl добавить сюда после всех ваших кодов.
Код:
<div class="nav-load" id="btn">[next-link]Загрузить еще[/next-link]</div>

<style>
  #btn{
    display:flex;
    justify-content:center;
    align-items:center;
    margin:18px 0;
    text-align:center;
  }
  #btn a{ text-decoration:none; }
  #btn a.is-loading{
    pointer-events:none;
    opacity:.75;
    cursor:default;
  }
  #btn.is-empty{ opacity:.75; }
  #btn .nav-load-text{ display:inline-block; }
</style>

<script>
$(function () {

  const ITEM_SEL = 'article.block.story.shortstory';

  // ✅ ОПЦИЯ: что делать на последней странице
  // 'hide'    -> полностью убрать блок (самый чистый UX)
  // 'message' -> показать текст
  // 'disabled'-> оставить “задизейбленную” кнопку
  const END_BEHAVIOR = 'hide';

  const END_MESSAGE = 'Больше нечего загружать';
  const DISABLED_TEXT = 'Нет новых записей';

  function parseDoc(html) {
    return $('<div>').append($.parseHTML(html));
  }

  function hasNextLink($wrap) {
    const $a = $wrap.find('a').first();
    return !!($a.length && $a.attr('href'));
  }

  function applyEndBehavior($wrap) {
    if (END_BEHAVIOR === 'hide') {
      $wrap.remove();
      return;
    }
    if (END_BEHAVIOR === 'message') {
      $wrap.addClass('is-empty').html('<span class="nav-load-text">' + END_MESSAGE + '</span>');
      return;
    }
    if (END_BEHAVIOR === 'disabled') {
      // оставляем кнопку, но делаем “неактивной”
      let $a = $wrap.find('a').first();
      if (!$a.length) {
        $wrap.html('<a href="#" class="btn nav-load-btn is-loading" aria-disabled="true"></a>');
        $a = $wrap.find('a').first();
      }
      $wrap.addClass('is-empty');
      $a.addClass('btn nav-load-btn is-loading')
        .attr('aria-disabled','true')
        .text(DISABLED_TEXT);
      return;
    }

    // fallback -> hide
    $wrap.remove();
  }

  function skinLoadMoreBtn() {
    const $wrap = $('#btn');
    if (!$wrap.length) return;

    const $a = $wrap.find('a').first();

    // Последняя страница: next-link отсутствует
    if (!$a.length || !$a.attr('href')) {
      applyEndBehavior($wrap);
      return;
    }

    // Есть next-link
    $wrap.removeClass('is-empty');

    if (!$a.hasClass('nav-load-btn')) $a.addClass('btn nav-load-btn');
    $a.text('Загрузить еще');
  }

  function setBtnLoading(isLoading) {
    const $a = $('#btn a').first();
    if (!$a.length) return;

    if (isLoading) {
      if (!$a.data('old-text')) $a.data('old-text', $a.text());
      $a.text('Загрузка...').addClass('is-loading');
    } else {
      $a.text($a.data('old-text') || 'Загрузить еще').removeClass('is-loading');
      skinLoadMoreBtn();
    }
  }

  // init
  skinLoadMoreBtn();

  $('body').on('click', '#btn a', function (e) {
    e.preventDefault();

    // если режим disabled/message и ссылки нет — просто выходим
    const urlNext = $(this).attr('href');
    if (!urlNext || urlNext === '#') return false;

    const $firstItem = $(ITEM_SEL).first();
    if (!$firstItem.length) return false;

    const $list = $firstItem.parent();

    $.ajax({
      url: urlNext,
      beforeSend: function () {
        setBtnLoading(true);
        if (typeof ShowLoading === 'function') ShowLoading('');
      },
      success: function (data) {
        const $doc = parseDoc(data);

        // 1) Подгружаем ниже только карточки новостей
        const $newItems = $doc.find(ITEM_SEL);
        if ($newItems.length) $list.append($newItems);

        // 2) Обновляем навигацию/кнопку
        $('.navigation').remove();
        $('#btn').remove();

        const $newNav  = $doc.find('.navigation').first();
        const $newLoad = $doc.find('#btn').first();

        if ($newNav.length)  $list.after($newNav);
        if ($newLoad.length) $list.after($newLoad);

        // 3) Применяем поведение кнопки (в т.ч. конец)
        skinLoadMoreBtn();

        // 4) URL (опционально)
        if (history.pushState) history.pushState({}, '', urlNext);
      },
      error: function () {
        alert('что-то пошло не так');
      },
      complete: function () {
        if (typeof HideLoading === 'function') HideLoading('');
        setBtnLoading(false);
      }
    });

    return false;
  });

});
</script>

Как переключать “что делать в конце”​


Вверху JS меняешь одну строку:
Код:
const END_BEHAVIOR = 'hide';     // убрать полностью
// const END_BEHAVIOR = 'message';  // показать текст
// const END_BEHAVIOR = 'disabled'; // оставить неактивную кнопку
Опционально можно добавить:
1) Автоподгрузка (Infinite Scroll) 🔄
Варианты UX
Pure Infinite — грузим автоматически, кнопки нет.
Hybrid — первые N страниц грузим автоматически, дальше показываем кнопку (контроль + меньше нагрузки).
Infinite + “стоп” — автоподгрузка, но с переключателем “Авто/Ручной”.
Рекомендуемая реализация (не scroll, а IntersectionObserver)
Почему: меньше лагов, меньше событий, лучше на мобилках.
Логика
наблюдаем за #btn (или “sentinel” div)
когда он попадает в viewport → триггерим тот же loadNext()
Опции
rootMargin: "300px" — начинать загрузку заранее (не в самый низ).
защита от повторных запросов: флаг isLoading.
лимит авто-страниц: AUTO_PAGES_LIMIT = 3 (гибрид).
2) Счётчик “показано N из M” 📊
Что реально возможно
В DLE “из коробки” нет универсальной переменной “всего постов” на страницу/категорию в DOM (зависит от шаблона). Поэтому 2 стратегии:
Стратегия A (быстро, без бэка): “Показано N” ✅
N считаем просто: document.querySelectorAll(ITEM_SEL).length
показываем “Показано: N”
Плюсы: всегда работает, везде.
Минусы: нет “из M”.
Стратегия B (точно “N из M”): нужен источник M ✅✅
M можно взять из:
заголовка категории/страницы, если там есть “(123)” или “найдено 123”
скрытого тега в шаблоне (лучший вариант):
добавить в шаблон один <meta>/data-total="123" (если DLE предоставляет число)
отдельного эндпоинта/хелпера (серверная доработка)
Итоговый UX
Показано 40 (минимум)
либо Показано 40 из 123 (если есть total)
На 15 версию не пойдет?
 
в 15 если неизменяет память еще присутсвует обертка dle-content тогда не подойдет!
и под DLE 15 можно адаптировать у меня например такой код прекрасно работает и не такой громоздкий
Код:
$(document).on('click', '.pagination__btn-loader a', function() {
    var urlNext = $(this).attr('href'), scrollNext = $(this).offset().top;
    $.ajax({
        url: urlNext,
        beforeSend: function() { ShowLoading('<p class="bolder">Загрузка</p>Пожалуйста, подождите...', 'right', 'top'); },
        success: function(data) {
            $('#pagination').remove();
            $('#content-holder').append($('#content-holder', data).html());
          
            $('.pagination__btn-loader span').text('Больше нет записей');
            HideLoading('');
        },
        error: function() { HideLoading(''); alert('что-то пошло не так'); }
    });
    return false;
});
 
Верх