pjax的js重载

由于通过 Pjax 切换的页面并没有完全刷新,浏览器不会将网页从头执行一遍,因此有些 JS 将不会生效。重载 JS 脚本大致分为三种:一种是重载 JS 函数,一种是重载整个 JS 文件,另一种是重载内联的 script 标签。

重载 JS 函数

这种重载一般适用于用户自己编写的一些 JS 函数。但是具体情况比较复杂,函数要不要重载还得具体分析。下面举一些例子:

  1. 页面不变部分对应的事件

    比如,页面顶部栏有一个搜索按钮,点击之后会弹出搜索框:

    1
    2
    3
    document.querySelector(".search-button").onclick = function () {
    // ...
    };

    由于页面顶部栏不变,所以其中的元素上绑定的事件仍然可以使用,这部分元素对应的 JS 事件不需要重载。

  2. 页面可变部分对应的事件

    比如,页面主体部分是变化的,懒加载其中的图片:

    1
    2
    3
    var imgs = document.querySelectorAll("#main img.lazyload");

    lazyload(imgs);

    当通过 Pjax 切换页面后,由于主体部分改变,上述代码已经失效,因此需要进行重载。为了方便使用,我们使用函数封装一下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function pjax_reload() {
    var imgs = document.querySelectorAll("#main img.lazyload");

    lazyload(imgs);
    }

    // Pjax 完成后,重新加载上面的函数
    document.addEventListener("pjax:complete", function () {
    pjax_reload();
    });

重载整个 JS 文件

这种情况多数用于第三方文件,比如,卜算子统计的脚本、谷歌/百度/腾讯分析的脚本等,这些脚本在每一次页面加载后都需要执行。

我的做法是,在引入这些文件的标签上添加 data-pjax 属性,然后将具有这个属性的标签重新添加在页面中。有时候不方便在这些标签上添加额外的属性,那么你可以在这些标签外套一层标签,如< div class=”.pjax-reload”></ div>,然后将 .pjax-reload 里的元素全部重新添加到页面中即可。代码示例如下:

1
2
3
4
5
6
7
8
<script
data-pjax
src="https://cdn.jsdelivr.net/gh/sukkaw/busuanzi/bsz.pure.mini.js"
></script>

<div class=".pjax-reload">
<script src="https://www.google-analytics.com/analytics.js"></script>
</div>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
   // jQuery 写法
$("script[data-pjax], .pjax-reload script").each(function () {
$(this).parent().append($(this).remove());
});

// JS 写法
document
.querySelector("script[data-pjax], .pjax-reload script")
.forEach(function (elem) {
var id = element.id || "";
var src = element.src || "";
var code = element.text || element.textContent || element.innerHTML || "";
var parent = element.parentNode;
var script = document.createElement("script");

parent.removeChild(element);

if (id !== "") {
script.id = element.id;
}

if (src !== "") {
script.src = src;
script.async = false;
}

if (code !== "") {
script.appendChild(document.createTextNode(code));
}

parent.appendChild(script);
});

重载整个script标签

这种情况和前面类似,如果一些 JS 脚本写在 script 标签中,并且需要重载,可以选择直接重载整个 script 标签。具体做法和上一步相同,在标签上添加 data-pjax 属性,然后将具有这个属性的标签重新添加到页面中。

实际操作

初级方案

在一些主题中如果作者有在pjax.ejs或者pjax.pug等类似文件中有类似如下代码的适配:

1
2
3
4
5
6
7
document.querySelectorAll('script[data-pjax]').forEach(item => {
const newScript = document.createElement('script')
const content = item.text || item.textContent || item.innerHTML || ''
Array.from(item.attributes).forEach(attr => newScript.setAttribute(attr.name, attr.value))
newScript.appendChild(document.createTextNode(content))
item.parentNode.replaceChild(newScript, item)
})

其中可以看到选择器中有包含data-pjax的script标签会被重新加载,可以在需要重载的script标签加data-pjax属性。

也可以自己加选择器在querySelectorAll()里,比如butterfly主题中加入了名为“js-pjax”的类选择器。

终极方案

对于集成在插件里的js或者一些使用初级方案后仍旧无法重载的部分,可以在new pjax()中选择相应的标签对应的选择器。

博主本人并不十分了解pjax,这里这是给出自己的一些解决办法。

参考文章