无需刷新即可检测 JavaScript 中的 URL 更改

如何为 Flarum 论坛添加 medium-zoom(用于缩放图像的 JavaScript 库,如 Medium)呢?本篇文章,在于分析借助 MutationObserver 函数,从而为 SPA 应用,添加些特殊处理,如添加 medium-zoom 动画。

故事背景

在今年年初,有基于 Flarum 搭建了悠然宜想亭这个站(具体由来,在关于“悠然宜想亭”的由来,以及未来一文中,有做阐述)。Flarum 是一款优雅简洁论坛软件,让在线交流变得更加轻松愉快。它那极简的部署方式,强大的后台管理,都令人欢喜不已;当然,前端页面也有些美中不足,如帖子的图片,不能放大化,像 Ghost 或 medium 那样。

medium-zoom:用于缩放图像的 JavaScript 库,如 Medium(A JavaScript library for zooming images like Medium);它具有:响应式、 高性能和轻量级、 高清支持、 灵活性、 自定义、 可插拔、 鼠标、键盘和手势友好等优点;此外,medium-zoom 的安装和使用,也都极其简单,如下示例:

// CSS selector
mediumZoom("[data-zoomable]");

// HTMLElement
mediumZoom(document.querySelector("#cover"));

// NodeList
mediumZoom(document.querySelectorAll("[data-zoomable]"));

// Array
const images = [
  document.querySelector("#cover"),
  ...document.querySelectorAll("[data-zoomable]"),
];

mediumZoom(images);

但,在 Flarum 项目中,直接使用 medium-zoom 会存在点小问题;Flarum 是单页(SPA)应用,即更改 URL,整个页面内容不会全部刷新(仅限于主题内容部分)。这也就使得在后台 Custom Header 插入的代码,无法在切换页面时也重新运行;按找正常逻辑,要处理这个问题,就须对 URL 做下监听,当检测到变化,则重新运行 mediumZoom 相关代码,使得「图像缩放」功能,可以在页面切换后,依然可以正常工作。

MutationObserver 函数

MutationObserver()  函数用于检测或观察对 DOM 树所做的更改。它只是检测 DOM 元素更改以及单页网站(如 React JS 和 Angular JS)上的 URL 更改。欲了解更详细内容,可参见 MutationObserver | MDN Web Docs。

无需页面刷新即可检测 JavaScript 中 URL 更改的源代码

let lastUrl = location.href;
new MutationObserver(() => {
  const url = location.href;
  if (url !== lastUrl) {
    lastUrl = url;
    onUrlChange();
  }
}).observe(document, { subtree: true, childList: true });

function onUrlChange() {
  alert("URL changed!", location.href);
}

代码说明:

  • 首先在上面的源代码中,我们使用页面的当前 URL location.href;
  • 之后,我们使用 MutationObserver 方法 observe() 配置 MutationObserver 回调以开始接收与给定选项匹配的DOM 更改通知
    • observe(document, {subtree: true, childList: true});
  • 最后,我们比较 URL,然后调用提示新位置 URL 的函数。

您可以复制完整的代码,以便在现有程序上检查或使用。

为 Flarum 论坛添加 medium-zoom 库

只需,在 Flarum 后台 Appearance => Custom Header,添加如下代码,即可使得 Flarum 帖子中的图片,在点击后可「放大」以方便查看。

<script>
  let lastUrl = location.href;
  new MutationObserver(() => {
    const url = location.href;
    if (url !== lastUrl) {
      lastUrl = url;
      onUrlChange();
    }
  }).observe(document, {subtree: true, childList: true});

  function onUrlChange() {
    setTimeout(function () {
       mediumZoom(document.querySelectorAll('.Post-body img'));
    }, 500)
  }
</script>