首页 文章

强制重新激活ajaxed内容上的所有(脚本/函数)

提问于
浏览
32

tl;博士

我使用ajax来获取新内容 . 提取内容并将其添加到页面中 . 但是,脚本不会"re-fire"因为它们的调用在ajaxed div 之外 .

脚本加载和触发初始页面加载没有任何问题,但是当我通过ajax添加新内容时没有 . 我没有控制台错误,如果我直接访问URL,则没有问题 .


相关:


简介:

我在WordPress网站上使用Ajaxify WordPress Site(AWS) .

该插件允许您通过其id选择 div ,然后使用ajax在 div 内部获取新内容

HTML标记

<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>

问题

插件工作,我得到新鲜的内容加载和样式,但有一个问题 . 我的一些页面 scripts and function calls 在我使用ajax的div之外 . 我有两个例子

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-谷歌 Map 呼叫在 <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>

这些只是两个 examples . 其他地方还有其他人 . 正如您所知,这些脚本不会被重新调用,因为在页面上重新加载的唯一内容是 <main id="ajax"> . 虽然 the new content inside <main> is there ,正确渲染它所需的一些脚本不会被重新调用,因此我最终会丢失元素/破坏布局 .


我不是唯一一个遇到这个问题的人;快速浏览一下插件的support forum on wordpress.org表明这个问题很常见 .

注意:如果插件有许多其他问题,我不会尝试修复此问题 . 它对我有用我只需要脚本重新启动 .

官方的回答是,可以通过在插件的php文件中添加以下内容来重新加载/重新激活脚本:

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

哪个有效 . 它运作良好 . 例如,如果我添加

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

然后,即使在ajaxed div 之外加载了masonry.js,也会在获取ajaxed内容时重新触发砌体函数调用

我推荐你plugin's files on github以更清楚地了解修复实际做了什么(我无法理解当使用 $.getScript 时会发生什么)


总结

如果您有3-4个需要在ajaxed内容上重新触发的脚本,那么官方修复工作正常 .

这对我的目标不起作用,因为

  • 它过于严格且硬编码

  • 一些脚本通过Cloudflare apps动态添加到页面中


一个可能的解决方案可能涉及添加一个事件模仿触发器,导致脚本在ajaxed div 的底部触发

问题:

当只通过ajax重新加载页面的某个部分时,如何强制所有脚本 - including dynamically added ones 重新触发?


笔记:

  • 我试图避免逐个调出脚本,因为这需要事先知道他们的调用 . 我可能 talking way over my head 但......

我试图模仿页面加载和/或文档就绪事件 - 大多数条件脚本被触发( correct me if I'm wrong ) - 在我的html结尾 <main> 时添加新的ajaxed内容但在页面加载时不影响文档通过直接使用网址...或任何其他类似的方法 .

  • Just for a bit of context, 这是页面 while the plugin is off 上的一些事件监听器的列表 . 我知道那里有些东西我不必触发 . 我刚刚添加了这个作为参考 . 另请注意,这是从其中一个页面中提取的样本 . 其他页面可能不同 .
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

7 回答

  • 4

    您在此处选择的解决方案必须依赖于脚本的初始化方式 . 有几种常见的可能性:

    • 加载脚本后立即激活脚本的操作 . 在这种情况下,脚本可能如下所示:
    (function() {
         console.log('Running script...');
    })();
    
    • 响应某些事件(例如文档就绪(JQuery)或窗口载入(JavaScript)事件)而引发脚本的操作 . 在这种情况下,脚本可能如下所示:
    $(window).on('load', function() {
        console.log('Running script...');
    });
    

    下面考虑这两种可能性的一些选择 .

    对于在加载时立即运行的脚本

    一种选择是从DOM中删除要触发的 <script> 元素,然后重新附加它们 . 使用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 属性获取),则它们将从服务器重新加载或从浏览器缓存中检索,具体取决于浏览器和设置 . 在这种情况下,最好的方法可能是删除所有在加载AJAX的内容上运行的 <script> ,并通过AJAX完成事件处理程序中的JQuery的 getScript() 函数加载/运行它们 . 这样,您只需在适当的时间(即加载AJAX内容后)加载/运行脚本一次:

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

    这种方法的两种变体的潜在问题是脚本的异步加载受到跨源限制 . 因此,如果脚本托管在不同的域上,并且不允许跨源请求,则它将不起作用 .

    对于响应事件而运行的脚本

    如果脚本是在窗口加载时触发的,那么您可以触发此事件:

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

    不幸的是,如果脚本本身使用JQuery的文档就绪事件,那么我不知道一种简单的手动触发它的方法 . 脚本也可能响应其他事件而运行 .

    显然,您可以根据需要组合上述方法 . 并且,正如其他人所提到的,如果脚本中有一些初始化函数可以调用,那么这是最干净的方法 .

  • 5

    这是你可以尝试的 -

    大多数脚本(如砌体或Google Map)都设置为在窗口大小调整时重新初始化 . 因此,如果在ajax完成后触发resize事件,将有助于自动重新触发这些脚本 .

    请尝试以下代码 -

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

    这将强制脚本在ajax完成后重新初始化,因为它现在将在加载内容后触发resize事件 .

  • 4

    如果您可以在外部脚本中识别全局初始化函数或代码块,则可以查看“ajaxComplete”事件 . 您可以将此代码放在页头中,并将初始化函数调用或代码块放在ajaxComplete回调中 .

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

    当您谈论的脚本没有这样易于使用的公开初始化函数,但是在scriptload上初始化时,我认为没有适用于所有脚本的开箱即用方法 .

  • 3

    也许有风险,但你应该能够在你的 <main> 元素上使用 DOMSubtreeModified .

    $('#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

    在尝试使用ajax在wordpress中重新加载具有浏览器状态和history.js的页面时,我遇到了这个问题 . 我直接将history.js排队,而不是使用插件为我做这件事 .

    每当点击一个新页面时,我就有大量的JS需要"re-ran" . 为此,我在我的主要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();
    
    });
    

    然后我连接到每当一个页面加载了history.js并调用global_scripts()时运行的JS;

    好像您使用的插件也使用history.js . 我没有专门用你的插件测试它,但你可以尝试我在下面发布的内容(这是我在我的应用程序中使用的) . 这将在上面的相同JS文件中 .

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

    我的代码比上面简单粘贴的要复杂一点 . 我实际上检查事件以确定正在加载什么内容,并基于此加载特定的JS函数 . 它允许更多地控制重新加载的内容 .

    注意:我在声明函数时使用window.global_scripts,因为我有单独的JS文件来保存这段代码 . 如果它们全部相同,您可以选择删除它 .

    注意2:我意识到这个答案需要确切地知道需要重新加载什么,所以它忽略了你的最后一个注释,它要求这不需要这样的知识 . 它仍然可以帮助您找到答案 .

  • 15

    简短的回答:这是不可能的通用方式 . 您的脚本应该如何知道需要触发哪些事件?

    更长的答案:它更像是结构性问题,而不是程序性问题 . 谁负责所需的功能?让我们以砖石为例:

    Masonry.js本身不会听任何事件 . 您需要自己创建一个砌体实例(这很可能是在您的Wordpress插件中完成的) . 如果启用resize选项,它将为resize事件添加一个侦听器 . 但你真正想要的是"DOM content change"上的一些听众(请参阅https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver以获得可能的解决方案) . 由于masonry.js不提供此类功能,因此您有以下选项:

    • 等待在masonry.js中实施(或自己动手)

    • 等待在砌筑Wordpress插件中实现(或自己动手) .

    • 在其他地方添加功能(您的模板,您的ajax插件等)
      4 .

    正如您所看到的,每个选项都包含一些要完成的工作,并且需要为要监听AJAX调用的DOM更改的每个脚本执行此工作 .

相关问题