Принудительно перезапустить все (скрипты / функции) на ajaxed-контенте

33

TL;DR

Я использую ajax для получения нового содержимого. Содержимое загружается и добавляется на страницу. Однако сценарии не перезапускаются, потому что их вызовы находятся за пределами ajaxed div.

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


Связанный:


Введение:

Я использую Ajaxify WordPress Site (AWS) на веб-сайте WordPress.

Плагин позволяет выбрать div по его идентификатору, а затем извлекает новый контент внутри этого div с помощью ajax

html markup

<html>

<head></head> 

<body>
  <div id="page"> 
    <header></header>
    <main id="ajax"> <!-- This is what I want to ajaxify -->
      <div class="container">
        main page content
      </div> 
    </main> <!-- end of ajaxfied content  -->
    <footer></footer>
  </div> <!-- #page -->
</body>

</html>

Проблема

Плагин работает, и я получаю свежий контент, загруженный и оформленный, но есть проблема. Некоторые мои страницы сценарии и вызовы функций находятся за пределами div, в которые я использую ajax. У меня есть два примера

1- Масонство загружается и вызывается в <footer>

<html>

<head></head> 

<body>
  <div id="page"> 
    <header></header>
    <main id="ajax"> <!-- This is what I want to ajaxify -->
      <div class="container">
        main page content
      </div>  
    </main>   <!-- end of ajaxfied content  -->
    <footer></footer> <!-- Masonry script is loaded and called here -->
  </div> <!-- #page -->
</body>

</html>

2- Google maps call находится в <head>

<html>

<head></head>  <!-- Google maps is called here -->

<body>
  <div id="page"> 
    <header></header>
    <main id="ajax"> <!-- This is what I want to ajaxify -->
      <div class="container">
        main page content
      </div>  
    </main>  <!-- end of ajaxfied content  -->
    <footer></footer> 
  </div> <!-- #page -->
</body>

</html>

Это всего лишь два примера. В других местах есть другие. Как вы можете сказать, такие сценарии не будут повторно вызваны как единственное, что перезагружается на странице <main id="ajax">. Хотя существует новое содержимое внутри <main>, некоторые скрипты, необходимые для его правильного отображения, не переименовываются и поэтому заканчиваются отсутствующими элементами/сломанным макетом.


Я не единственный, кто столкнулся с этой проблемой; быстрый просмотр плагина форума поддержки на wordpress.org показывает, что эта проблема распространена.

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

Официальным ответом является возможность перезагрузки/перезапуска сценариев, добавив следующее в файлы php плагина:

$.getScript(rootUrl + 'PATH TO SCRIPT');

Что работает. Это работает хорошо. например, если я добавлю

$.getScript(rootUrl + '/Assets/masonry.js');

Затем вызовы функции кладки возвращаются, когда аяксированный контент извлекается, даже если masonry.js загружается вне ajaxed div

Я отсылаю вас к файлам плагинов на github для большей ясности в отношении того, что на самом деле делает исправление (я не могу понять, что происходит когда используется $.getScript)


В заключение

Официальное исправление работает отлично, если у вас есть 3-4 скрипта, которые нужно перезапустить на ajaxed контенте.

Это не работает для моей цели, потому что

  • он слишком жесткий и жестко закодированный
  • Некоторые скрипты добавляются на страницу динамически через Приложения Cloudflare

Возможное решение может включать добавление события, имитирующего триггер, вызывающий запуск сценариев в нижней части ajaxed div

Вопрос:

Как заставить все сценарии - , включая динамически добавленные - перезаписать, когда только одна часть страницы была перезагружена через ajax?


Примечания:

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

    Я пытаюсь имитировать загрузку страницы и/или готовые к документу события, при которых запускаются большинство условных скриптов (исправить меня, если я ошибаюсь) - в конце <main> в мой html, когда добавляется новый добавленный контент, но не затрагивает документ, когда страница загружается с помощью URL напрямую... или любой другой подобный подход.

  • Только для небольшого контекста приведен список некоторых прослушивателей событий на странице , в то время как плагин выключен. Я знаю, что есть вещи, которые мне не придется запускать. Я просто добавил это для справки. Кроме того, обратите внимание, что это образец, взятый с одной из страниц. другие страницы могут отличаться.

DOMContentLoaded
beforeunload
blur
click
focus
focusin
focusout
hashchange
keydown
keyup
load
message
mousedown
mousemove
mouseout
mouseover
mouseup
mousewheel
orientationchange
readystatechange
resize
scroll
submit
touchscreen
unload
  • 0
    У всех ваших плагинов могут быть методы, позволяющие им делать то, что они делают при загрузке страницы, даже после загрузки страницы. Например, masonry (который я лично не знаю) предоставляет метод masonry() для объектов jQuery. Таким образом, вы можете использовать эти методы вместо сомнительных взломов.
  • 0
    Каково значение слова «tl; dr» в вашем вопросе? Верх вопроса ...
Показать ещё 2 комментария
Теги:
listener
dom-events

7 ответов

15

Решение, которое вы выбрали здесь, должно зависеть от того, как инициализируются сценарии. Есть пара общих возможностей:

  • Действия script вызываются сразу после загрузки script. В этом случае script может выглядеть примерно так:

    (function() {
         console.log('Running script...');
    })();
    
  • Действия script вызываются в ответ на какое-либо событие (например, события, готовые к документу (JQuery) или события onload (JavaScript)). В этом случае script может выглядеть примерно так:

    $(window).on('load', function() {
        console.log('Running script...');
    });
    

Некоторые варианты этих двух возможностей рассмотрены ниже.

Для скриптов, которые запускаются сразу при загрузке

Один из вариантов заключается в том, чтобы просто удалить элементы <script>, которые вы хотите вызвать из DOM, а затем снова подключить их. С помощью JQuery вы могли бы сделать

$('script').remove().appendTo('html');

Конечно, если приведенный выше фрагмент сам по себе находится в теге <script>, тогда вы создадите бесконечный цикл непрерывного отсоединения и повторного прикрепления всех скриптов. Кроме того, могут быть некоторые сценарии, которые вы не хотите повторно запускать. В этом случае вы можете добавлять классы в сценарии, чтобы выбрать их положительно или отрицательно. Например,

// Positively select scripts with class 'reload-on-ajax'
$('script.reload-on-ajax').remove().appendTo('html');

// Negatively select scripts with class 'no-reload'
$('script').not('no-reload').remove().appendTo('html')

В вашем случае вы разместите один из приведенных выше фрагментов в обработчике событий для завершения AJAX. В следующем примере используется щелчок кнопкой мыши вместо события завершения AJAX, но концепция одинаков (обратите внимание, что этот пример не работает в изолированной среде StackOverflow, поэтому попробуйте загрузить его как отдельную страницу для получения лучшего результата)

<html>
  <head></head>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

  <script class="reload-on-ajax">
    console.log('Script after head run.');
  </script>

  <body>
    <button id="reload-scripts-button">Reload Scripts</button>
  </body>

  <script class="reload-on-ajax">
    console.log('Script after body run.');
  </script>

  <script>
     $('#reload-scripts-button').click( () => $('script.reload-on-ajax').remove().appendTo('html') );
  </script>
</html>

Обратите внимание, что если скрипты не являются встроенными (например, с помощью атрибута src), они будут перезагружены с сервера или извлечены из кеша браузера, в зависимости от браузера и настроек. В этом случае лучший подход, вероятно, заключается в том, чтобы удалить все <script>, которые работают с загруженным AJAX контентом, и загрузить/запустить их через что-то вроде функции JQuery getScript() из обработчика события завершения AJAX. Таким образом, вы будете загружать или запускать сценарии только один раз в соответствующее время (т.е. После загрузки содержимого AJAX):

// In AJAX success event handler
$.getScript('script1.js');
$.getScript('script2.js');

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

Для сценариев, которые запускаются в ответ на событие

Если скрипты запускаются при загрузке окна, вы можете просто вызвать это событие:

   $(window).trigger('load');

К сожалению, если сами скрипты используют событие JQuery document ready, то я не знаю, как легко его запустить вручную. Также возможно, что сценарии выполняются в ответ на какое-то другое событие.

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

5

Здесь вы можете попробовать -

Большинство сценариев, таких как кладка или карта Google, настроены на повторное инициализацию при изменении размера окна. Таким образом, если вы запускаете событие изменения размера после завершения ajax, это поможет автоматически перезаписать эти сценарии.

Попробуйте использовать следующий код -

$( document ).ajaxComplete( function() {
    $( window ).trigger('resize');
} );

Это заставит сценарии перезапускаться после завершения ajax, поскольку теперь он будет запускать событие изменения размера после загрузки содержимого.

4

У меня была эта точная проблема при попытке использовать ajax для перезагрузки страницы с состояниями браузера и history.js в wordpress. Я непосредственно установил history.js, вместо того, чтобы использовать плагин для этого.

У меня было множество JS, которые нужно было "перезапускать" всякий раз, когда была нажата новая страница. Для этого я создал глобальную функцию в основном файле javascript под названием global_scripts();

Во-первых, убедитесь, что этот JS файл находится в очереди после всего остального, в вашем footer.php.

Это может выглядеть примерно так:

wp_enqueue_script('ajax_js', 'url/to/file.js', 'google-maps', 1, true);

Мой javascript, который я в очереди, ниже.

jQuery(document).ready(function($) {

    // scripts that need to be called on every page load
    window.global_scripts = function(reload) {


       // Below are samples of the javascript I ran that I needed to re run.
       // This includes lazy image loading, paragraph truncation.

       /* I would put your masonry and google maps code in here. */

        bLazy = new Blazy({
            selector: '.featured-image:not(.no-blazy), .blazy', // all images
            offset: 100
        });


        /* truncate meeeee */
        $('.post-wrapper .post-info .dotdotdot').dotdotdot({
            watch: 'window'
        });

    }

    // call the method on initial page load (optional)
    //global_scripts();

});

Затем я подключился к JS, который запускался всякий раз, когда страница была загружена с помощью history.js и вызвана global_scripts();

Кажется, что плагин, который вы используете, также использует history.js. Я не тестировал его с вашим плагином специально, но вы можете попробовать то, что я разместил ниже (это то, что я использую в своем приложении). Это пойдет в том же JS файле выше.

History.Adapter.bind(window, 'statechange', function() { 
     global_scripts();
}

Мой код немного сложнее, чем то, что просто вставлено выше. Я на самом деле проверяю событие, чтобы определить, какой контент загружается, и загружать на него определенные JS-функции. Это позволяет больше контролировать то, что перезагружается.

note: Я использую window.global_scripts при объявлении функции, потому что у меня есть отдельные JS файлы, содержащие этот код. Вы могли бы удалить это, если все они одинаковы.

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

4

Возможно, рискованно, но вы можете использовать DOMSubtreeModified для своего элемента <main> для этого.

$('#ajaxed').bind('DOMSubtreeModified', function(){
  //your reload code
});

Затем вы сможете просто добавить все свои скрипты в свою область перезагрузки

var scripts = document.getElementsByTagName('script');
for (var i=0;i<scripts.length;i++){
   var src = scripts[i].src;
   var sTag = document.createElement('script');
   sTag.type = 'text/javascript';
   sTag.src = src;
   $('head').append(sTag);

}

Другим вариантом может быть создание собственного прослушивателя событий и наличие в нем того же кода перезагрузки.

$(document).on('ajaxContentLoaded', function(){//same reload code});

Затем вы можете инициировать событие в плагине после обновления содержимого.

$(document).trigger('ajaxContentLoaded');

Или, возможно, комбинация редактирования плагина для запуска слушателя и добавления в вашу кодовую базу для повторного запуска всего, что вы считаете нужным, чтобы повторно запустить этот прослушиватель, а не перезагружать что-либо.

$(document).on('ajaxContentLoaded', function(){
   //Javascript object re-initialization
   myObj.init();
   //Just a portion of my Javascript?
   myObj.somePortion();
});
4

Решение может дублировать все скрипты...

$(document).ajaxSuccess((event, xhr, settings) => {
  // check if the request is your reload content
  if(settings.url !== "myReloadedContentCall") {
    return;
  }

  return window
    .setTimeout(rejs, 100)
  ;
});

function rejs() {
  const scripts = $('script[src]');
  // or, maybe alls but not child of #ajax
  // const scripts = $('*:not(#ajax) script[src]');

  Array
    .prototype
    .forEach
    .call(scripts, (script, index) => {
      const $script = $(script);

      const s = $('<script />', {
        src: $script.attr('src')
      });  
      // const s = $script.clone(); // should also work

      return s
        .insertAfter($script)
        .promise()
        .then(() => $script.remove()) // finally remove it
      ;
    })
  ;

}
4

Если вы можете идентифицировать глобальную функцию инициализации или кодовый блок во внешних сценариях, вы можете взглянуть на событие "ajaxComplete". Вы можете поместить этот код в свою страницу и поставить инициализирующие вызовы функций или кодовые блоки внутри обратного вызова ajaxComplete.

$(document).ajaxComplete(function(){
    module1.init();
    $('#my_id').module2_init({
        foo : 'bar',
        baz : 123
    });
});

Когда скрипты, о которых вы говорите, не имеют таких простых в использовании открытых инициализирующих функций, но инициализируют себя при загрузке скрипта, я думаю, что не будет метода "из коробки", который работает для всех скриптов.

3

Короткий ответ: Это невозможно для этого в общем виде. Как ваш script знает, какие события нужно уволить?

Более длинный ответ: Это скорее структурный вопрос, чем программный. Кто отвечает за желаемую функциональность? Давайте возьмем кладки в качестве примера:

Сам Masonry.js не слушает никаких событий. Вам нужно создать экземпляр кладки по своему усмотрению (что, скорее всего, сделано на domready в вашем плагине Wordpress). Если вы включите параметр изменения размера, он добавит слушателя к событию изменения размера. Но то, что вы на самом деле хотите, - это прослушиватель "Изменение содержимого DOM" (см. https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver для возможного решения). Поскольку masonry.js не предоставляет такую ​​функцию, у вас есть следующие возможности:

  • Подождите выполнения (или сделайте это самостоятельно) в masonry.js
  • Дождитесь выполнения (или сделайте это самостоятельно) в масонском плагине Wordpress.
  • Добавьте функциональность где-нибудь еще (ваш шаблон, ваш плагин ajax и т.д.).  4.

Как вы можете видеть, каждая опция включает в себя некоторую работу, которая должна быть выполнена, и эта работа должна выполняться для каждого script, который вы хотите прослушать внесенные AJAX изменения DOM.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню