跳转到内容

安全跳转页面·重制版

更新于: 2024-04-21
LiuShen
3 分钟
1,134 字
PV --
UV --

这里是清羽AI,这篇文章介绍了安全跳转页面的重制版,推荐使用插件版实现更全面的安全跳转功能,解决了原版兼容性差的问题,包括设置替换白名单、页面白名单、元素白名单和跳转白名单等功能,适用于Hexo架构博客,需要一定的前端水平。

碎碎念

更新说明

安全跳转页面目前已经更新了插件版,强烈推荐使用插件版实现,功能更加全面且实现效果更好,插件会替换静态文件中的外链,实现真正的安全跳转。

点击跳转

原本的安全跳转页面糟糕的一塌糊涂,因为当时水平有限,所以只能在别人的基础上修改,导致很多地方都不兼容,比如最简单的fancybox我都没有办法排除,会导致无法点击图片进行放大查看,除此之外无法排除友链页面,也无法排除友情链接的跳转卡片,兼容性也很差,群友想要使用我也没法提供解决方案,很是头疼,所以经过整整一个月的酝酿,我胡汉三又回来啦!此次重构大大简化了代码结构,并解决了前面的问题,为了测试稳定性,我还特意悄悄地上线了六天,好像也没人提出什么bug(也有可能是我的人气太少了呜呜呜),这才正式写出该重制版教程,给予需要的朋友以启发。

本站链接 · 来自本站,本站可确保其安全性,请放心点击跳转原文章(已弃用)LiuShen's Blog

功能介绍

  1. 设置替换白名单:按照揽星给出的建议,可以自定义替换白名单匹配,如友链文章引用,好友引用等无需替换,其他链接替换;
  2. 设置页面白名单:如仅匹配文章页面的链接;
  3. 设置元素白名单:如仅匹配id="article-container"的内容;
  4. 设置跳转白名单:如知乎等,使用跳转页面,但显示为安全,可以自动跳转;
  • 注:该教程理论上适用于全部Hexo架构博客,请按照要求修改代码即可,该教程需要有一定的前端水平,如果有问题可以发到评论区,我会尽量解答。

功能实现

这里我还是使用原廿壴博客提供的跳转页模板,但是相关跳转页逻辑完全重构,模板原链接如下:

站外引用 · 引用站外地址,不保证站点的可用性和安全性HEXO个人博客优化魔改非插件实现为博客增加安全跳转中台页面廿壴博客

页面模板

首先需要在source文件夹下创建go.html,写入以下内容:

---
layout: false
---

<!DOCTYPE html>
<html data-user-color-scheme="light">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, shrink-to-fit=no"
    />
    <title>安全中心 | LiuShen's Blog</title>
    <link rel="icon" class="icon-favicon" href="/" />
    <link
      rel="stylesheet"
      href="https://lib.baomitu.com/twitter-bootstrap/4.6.1/css/bootstrap.min.css"
    />
    <link
      rel="stylesheet"
      href="https://at.alicdn.com/t/font_1736178_lbnruvf0jn.css"
    />
    <style type="text/css">
      /* // 向上渐隐显示(主内容使用) */
      @-webkit-keyframes fadeInUp {
        0% {
          opacity: 0;
          transform: translateY(24px);
        }

        100% {
          opacity: 1;
          transform: translateY(-80px);
        }
      }

      @keyframes fadeInUp {
        0% {
          opacity: 0;
          -webkit-transform: translateY(24px);
          -ms-transform: translateY(24px);
          transform: translateY(24px);
        }

        100% {
          opacity: 1;
          -webkit-transform: translateY(-80px);
          -ms-transform: translateY(-80px);
          transform: translateY(-80px);
        }
      }

      /* // 向上渐隐显示(成功错误提示) */
      @-webkit-keyframes alertFadeInUp {
        0% {
          opacity: 0;
          transform: translateY(24px);
        }

        75% {
          opacity: 1;
          transform: translateY(0);
        }

        100% {
          opacity: 0;
        }
      }

      @keyframes alertFadeInUp {
        0% {
          opacity: 0;
          -webkit-transform: translateY(24px);
          -ms-transform: translateY(24px);
          transform: translateY(24px);
        }

        75% {
          opacity: 1;
          -webkit-transform: translateY(0);
          -ms-transform: translateY(0);
          transform: translateY(0);
        }

        100% {
          opacity: 0;
        }
      }

      @-webkit-keyframes fadeOutUp {
        0% {
          opacity: 1;
        }

        to {
          opacity: 0;
          transform: translate3d(0, -350%, 0);
        }
      }

      @keyframes fadeOutUp {
        0% {
          opacity: 1;
        }

        to {
          opacity: 0;
          -webkit-transform: translate3d(0, -350%, 0);
          transform: translate3d(0, -350%, 0);
        }
      }

      :root {
        --blue: #007bff;
        --indigo: #6610f2;
        --purple: #6f42c1;
        --pink: #e83e8c;
        --red: #dc3545;
        --orange: #fd7e14;
        --yellow: #ffc107;
        --green: #28a745;
        --teal: #20c997;
        --cyan: #17a2b8;
        --white: #fff;
        --gray: #6c757d;
        --gray-dark: #343a40;
        --primary: #007bff;
        --secondary: #6c757d;
        --success: #28a745;
        --info: #17a2b8;
        --warning: #ffc107;
        --danger: #dc3545;
        --light: #f8f9fa;
        --dark: #343a40;
        --breakpoint-xs: 0;
        --breakpoint-sm: 576px;
        --breakpoint-md: 768px;
        --breakpoint-lg: 992px;
        --breakpoint-xl: 1200px;
        --font-family-sans-serif:
          -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", Roboto,
          "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif,
          "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
          "Noto Color Emoji";
        --font-family-monospace:
          SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
          "Courier New", monospace;
      }

      [data-user-color-scheme="dark"] {
        --body-bg-color: #22272e;
        --board-bg-color: #2b313a;
        --text-color: #adbac7;
        --sec-text-color: #b3bac1;
        --post-text-color: #adbac7;
        --post-heading-color: #adbac7;
        --post-link-color: #34a3ff;
        --link-hover-color: #30a9de;
        --link-hover-bg-color: #22272e;
        --line-color: #adbac7;
        --navbar-bg-color: #22272e;
        --navbar-text-color: #cbd4dc;
        --subtitle-color: #cbd4dc;
        --scrollbar-color: #30a9de;
        --scrollbar-hover-color: #34a3ff;
        --button-bg-color: transparent;
        --button-hover-bg-color: #46647e;
        --highlight-bg-color: #2d333b;
        --inlinecode-bg-color: rgba(99, 110, 123, 0.4);
      }

      ::-webkit-scrollbar {
        width: 6px;
        height: 6px;
      }

      ::-webkit-scrollbar-corner {
        background-color: transparent;
      }

      ::-webkit-scrollbar-thumb {
        background-color: var(--scrollbar-color);
        border-radius: 6px;
      }

      html {
        -webkit-text-size-adjust: 100%;
        -webkit-tap-highlight-color: transparent;
      }

      html,
      body {
        /* background: #f3f4f5; */
        /* font-family: PingFang SC, Hiragino Sans GB, Arial, Microsoft YaHei,
          Verdana, Roboto, Noto, Helvetica Neue, sans-serif; */
        font-family: var(--font-family-sans-serif);
        padding: 0;
        margin: 0;
        background-color: var(--body-bg-color);
        color: var(--text-color);
        transition:
          color 0.2s ease-in-out,
          background-color 0.2s ease-in-out;
        height: 100%;
      }

      body {
        font-size: 1rem;
      }

      p,
      div {
        padding: 0;
        margin: 0;
      }

      a {
        text-decoration: none;
        transition:
          color 0.2s ease-in-out,
          background-color 0.2s ease-in-out;
      }

      body a:hover {
        color: var(--link-hover-color);
        text-decoration: none;
      }

      .go-page {
        height: 100%;
      }

      .content {
        /* padding-top: 220px; */
        width: 450px;
        margin: auto;
        word-break: break-all;
        height: 100%;
      }

      .content .logo-img {
        margin-bottom: 20px;
        text-align: center;
        padding-top: 220px;
      }

      .content .logo-img p:first-child {
        font-size: 22px;
      }

      .content .logo-img img {
        display: block;
        width: 175px;
        height: 48px;
        margin: auto;
        margin-bottom: 16px;
      }

      .content .loading-item {
        background: #fff;
        padding: 24px;
        border-radius: 12px;
        border: 1px solid #e1e1e1;
        margin-bottom: 10px;
      }

      /* 绿色 */
      .content .tip1 {
        background: #f0f9ea;
      }

      /* 黄色 */
      .content .tip2 {
        background: #fdf5e6;
      }

      /* 红色 */
      .content .tip3 {
        background: #fef0f0;
      }

      .content .icon-snapchat-fill {
        font-size: 20px;
        color: #fc5531;
        border: 1px solid #fc5531;
        border-radius: 50%;
        width: 32px;
        text-align: center;
        margin-right: 5px;
      }

      .content .tip1 .icon-snapchat-fill {
        color: var(--post-link-color);
        border-color: var(--post-link-color);
      }

      .content .loading-text {
        font-size: 16px;
        font-weight: 600;
        color: #222226;
        line-height: 22px;
        /* margin-left: 12px; */
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      .content .flex {
        display: flex;
        align-items: center;
      }

      .content .flex-end {
        display: flex;
        justify-content: flex-end;
      }

      /* #267dcc 蓝色 */
      .content .loading-color1 {
        color: var(--post-link-color);
      }

      .content .loading-color2 {
        color: #fc5531;
      }

      .content .loading-tip {
        padding: 12px;
        margin-bottom: 16px;
        border-radius: 4px;
      }

      .content .loading-topic {
        font-size: 14px;
        color: #222226;
        line-height: 24px;
        margin-bottom: 24px;
      }

      .loading-topic .flex {
        flex-direction: column;
      }

      .content .loading-img {
        width: 24px;
        height: 24px;
      }

      /* #fc5531; #fc5531*/
      .content .loading-btn {
        font-size: 14px;
        color: var(--post-link-color);
        border: 1px solid var(--post-link-color);
        display: inline-block;
        box-sizing: border-box;
        padding: 6px 18px;
        border-radius: 18px;
        margin-left: 8px;
      }

      .content .loading-btn:hover {
        color: var(--link-hover-color);
        border-color: var(--link-hover-color);
      }

      .content .loading-btn-github {
        width: 121px;
        background: #fc5531;
        color: #fff;
      }

      .hidden {
        display: none;
      }

      .form-control.hidden {
        display: none !important;
      }

      .mp-img-box {
        text-align: center;
        margin-bottom: 10px;
      }

      .mp-img {
        max-width: 400px;
        width: 100%;
        box-shadow: 5px 5px 15px rgb(0 0 0 / 8%);
        margin-bottom: 5px;
      }

      .fadeInUp {
        -webkit-animation-name: fadeInUp;
        animation-name: fadeInUp;
      }

      .alertFadeInUp {
        -webkit-animation-name: alertFadeInUp;
        animation-name: alertFadeInUp;
        -webkit-animation-duration: 3s;
        animation-duration: 3s;
        -webkit-animation-fill-mode: both;
        animation-fill-mode: both;
      }

      .fadeOutUp {
        -webkit-animation-name: fadeOutUp;
        animation-name: fadeOutUp;
      }

      .fade-animate {
        -webkit-animation-duration: 1s;
        animation-duration: 1s;
        -webkit-animation-fill-mode: both;
        animation-fill-mode: both;
        -webkit-animation-delay: 1s;
        animation-delay: 1s;
      }

      .go-alert {
        margin: 0 auto;
        width: 110px;
        position: absolute;
        left: 46%;
        top: 5%;
        opacity: 0;
        text-align: center;
      }

      .footer {
        text-align: center;
        position: relative;
        margin-bottom: 20px;
      }

      .footer a {
        color: var(--text-color);
      }

      .flex-box {
        display: flex;
        height: 100vh;
        flex-direction: column;
      }

      .flex-contain {
        flex: 1;
      }

      .flex-footer {
        height: 24px;
      }

      @media (max-width: 767.98px) {
        .content {
          width: 94%;
        }

        .content .logo-img {
          padding-top: 120px;
        }
      }
    </style>
  </head>

  <body class="web-font">
    <div id="goPage" class="go-page">
      <div class="alert alert-danger go-alert hidden" role="alert">
        验证失败
      </div>

      <div class="content">
        <div class="flex-box">
          <div class="flex-contain">
            <div class="logo-img">
              <p class="blog-name">LiuShen's Blog</p>
              <p class="blog-description"></p>
            </div>

            <!-- 加载ing... -->
            <div class="loading-item loading-safe flex">
              <i class="iconfont icon-snapchat-fill"></i>
              <div class="loading-text">链接安全性检验中 请稍后...</div>
            </div>

            <div class="go-box"></div>
          </div>
          <div class="footer flex-footer">
            ©2021-2024
            <a href="https://www.qyliu.top" class="blog-name"
              ><span>LiuShen's Blog</span></a
            >
            版权所有
          </div>
        </div>
      </div>
    </div>
    <!-- goPage end -->

    <script src="https://lib.baomitu.com/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://lib.baomitu.com/twitter-bootstrap/4.6.1/js/bootstrap.min.js"></script>
    <script type="module">
      // 请根据自己博客修改
      const config = {
        // 标题
        title: "安全中心 | LiuShen's Blog",
        // 地址栏图标
        iconFavicon: "https://p.liiiu.cn/i/2024/03/21/65fc56832e37d.png",
        // 二维码地址
        // mpImgSrc: "/img/wxgzh.webp",
        // 博客名称
        blogName: "LiuShen's Blog",
        // 博客描述
        blogDescription: "柳影曳曳,清酒孤灯,扬笔撒墨,心境如霜",
        // 白名单
        safeUrl: [
          // 平台 常用平台不用改哈
          "github.com",
          "gitee.com",
          "csdn.net",
          "zhihu.com",
          "pan.baidu.com",
          "baike.baidu.com",
          "hexo.io",
          "leancloud.cn",
          "nodejs.cn",
          "jsdelivr.com",
          "ohmyposh.dev",
          "nerdfonts.com",
          "douban.com",
          "waline.js.org",
          "developer.mozilla.org",
          "qyliu.top",

          // 好友博客 增加自己的博客友链
        ],
        tipsTextError: "链接错误,关闭页面返回本站",
        // tipsTextDownload:
        // "从廿壴(ganxb2)微信公众号获取暗号≖‿≖✧ o‿≖✧(๑•̀ㅂ•́)و✧",
        //   "(๑•̀ㅂ•́)و✧“博客”微信公众号关注走一波o‿≖✧",
        tipsTextDanger: "该网址未在确认的安全范围内",
        tipsTextSuccess: "该网址在确认的安全范围内",
        textDanger:
          "您即将离开博客去往如下网址,请注意您的账号隐私安全和财产安全:",
        textSuccess: "您即将离开博客去往如下网址",
        // 后续改成leancloud获取(下载验证码)
        // wpValidate: "9498",
      };
      // 获取地址
      const getQueryString = (name, type) => {
        // 构造一个含有目标参数的正则表达式对象
        let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"),
          regDown = new RegExp("&type=" + type),
          // 匹配地址参数
          r = window.location.search.substr(1).match(reg),
          d = window.location.search.substr(1).match(regDown),
          isDownload = false;

        // 反编译回原地址 取第3个值,不然就返回 Null
        if (r !== null) {
          // 如果d不为空,则显示下载提示
          if (d !== null) {
            isDownload = true;
          }
          return { url: decodeURIComponent(r[2]), isDownload: isDownload };
        }
        return null;
      };

      // xss攻击(绑定值时使用)
      const xssCheck = (str, reg) => {
        return str
          ? str.replace(
              reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp|#\d+);)?/g,
              function (a, b) {
                if (b) {
                  return a;
                } else {
                  return {
                    "<": "&lt;",
                    "&": "&amp;",
                    '"': "&quot;",
                    ">": "&gt;",
                    "'": "&#39;",
                  }[a];
                }
              }
            )
          : "";
      };

      // 其他地址校验白名单
      const othersValidate = (config, getLinkUrl) => {
        let isSafeUrl = false,
          safeUrl = config.safeUrl,
          url = xssCheck(getLinkUrl.url);
        console.log("shuchuchuchcu", safeUrl);
        console.log("shuchuchuchcu", url);

        if (safeUrl.length !== 0) {
          for (let i = 0; i < safeUrl.length; i++) {
            const ele = safeUrl[i];
            if (
              url.includes(ele) ||
              url.includes(ele + "/") ||
              url.includes("https://" + ele) ||
              url.includes("https://" + ele + "/") ||
              url.includes("http://" + ele) ||
              url.includes("http://" + ele + "/")
            ) {
              isSafeUrl = true;
              break;
            }
          }
        }
        return isSafeUrl;
      };

      // 模版基础配置初始
      const goInit = config => {
        // $(function () {
        const tplConfig = {
            loadingType: "loading-error",
            tipType: "tip3",
            tipsText: config.tipsTextError,
            loadingTopicText: config.textDanger,
            loadingColorType: "loading-color2",
            goUrl: "/",
          },
          getLinkUrl = getQueryString("goUrl", "goDown"),
          loadingSafe = document.querySelector(".loading-safe"),
          goBox = document.querySelector(".go-box"),
          title = document.querySelector("title"),
          iconFavicon = document.querySelector(".icon-favicon"),
          blogName = document.querySelectorAll(".blog-name"),
          blogDescription = document.querySelector(".blog-description");

        // 初始化:标题,favicon,博客名称,博客描述
        title.textContent = config.title;
        iconFavicon.setAttribute("href", config.iconFavicon);
        blogName.forEach(element => {
          element.textContent = config.blogName;
        });
        blogDescription.textContent = config.blogDescription;

        // 根据地址栏参数判断是下载地址还是纯外链,外链则直接修改a标签按钮url,用户点击跳转
        if (getLinkUrl) {
          // 可参考csdn加入后端请求验证地址是否白名单再进一步给出不同场景状态:是白名单,则绿+蓝,否则黄+红
          const isSafeUrl = othersValidate(config, getLinkUrl);
          tplConfig.loadingType = "loading-others";
          tplConfig.goUrl = xssCheck(getLinkUrl.url);

          if (isSafeUrl) {
            tplConfig.tipType = "tip1";
            tplConfig.tipsText = config.tipsTextSuccess;
            tplConfig.loadingTopicText = config.textSuccess;
            tplConfig.loadingColorType = "loading-color1";
            // 白名单链接直接跳转
            setTimeout(() => {
              const goUrlBtn = document.querySelector(".go-url-btn");
              goUrlBtn.click();
            }, 2000);
          } else {
            tplConfig.tipType = "tip2";
            tplConfig.tipsText = config.tipsTextDanger;
            tplConfig.loadingTopicText = config.textDanger;
            tplConfig.loadingColorType = "loading-color2";
          }
        } else {
          // 错误
          tplConfig.tipType = "tip2";
          tplConfig.tipsText = config.tipsTextError;
        }

        const othersTpl = `
          <div class="loading-topic">
            <span
              >${tplConfig.loadingTopicText}</span
            >
            <a class="${tplConfig.loadingColorType} go-url">${tplConfig.goUrl}</a>
          </div>
          <div class="flex-end">
            <a rel="noopener external nofollow noreferrer" class="loading-btn go-url-btn" href="${tplConfig.goUrl}" target="_self">继续</a>
          </div>
        `;
        const tpl = `
          <div class="loading-item ${tplConfig.loadingType} hidden">
            <div class="flex loading-tip ${tplConfig.tipType}">
              <i class="iconfont icon-snapchat-fill ${
                tplConfig.loadingType === "loading-download" && "hidden"
              }"></i>
              <div class="loading-text">
                ${tplConfig.tipsText}
              </div>
            </div>
            ${
              tplConfig.loadingType === "loading-others"
                ? othersTpl
                : // : tplConfig.loadingType === "loading-download"
                  //   ? downloadTpl
                  ""
            }
          </div>
        `;

        // tpl渲染
        goBox.innerHTML = tpl;
        const loadingItem = document.querySelector(".go-box .loading-item");
        loadingSafe.classList.add("fadeOutUp", "fade-animate");
        loadingItem.classList.remove("hidden");
        loadingItem.classList.add("fadeInUp", "fade-animate");
      };
      goInit(config);
    </script>
  </body>
</html>

以上代码可能需要修改的部分只有一个地方,白名单,不过这里的白名单都是通用的,可以不进行修改,这里的白名单为跳转白名单,详情请看功能介绍,下面是页面展示:

跳转白名单展示

JS链接替换

下面就是我重构的内容,使用JS脚本,将能匹配上的链接进行替换,请在自定义JS代码部分添加以下内容:

function updateLinks() {
    // 定义白名单数组
    var whitelist = [
		'qyliu.top', // 添加您不想替换链接的域名或路径片段
		'zouht.com',
		'akilar.top',
		……
    ];

    var containerArticle = document.getElementById("article-container");
    if (containerArticle) {
        var links = containerArticle.getElementsByTagName("a");
        for (var i = 0; i < links.length; i++) {
            var link = links[i];
            var hasFancybox = link.hasAttribute("data-fancybox");
            var isSafeGo = link.href.startsWith('/go.html');

            // 使用 Array.prototype.some() 来检查链接的 href 是否包含白名单中的某个元素
            var isWhitelisted = whitelist.some(function(whitelistedItem) {
                return link.href.includes(whitelistedItem);
            });

            // 如果没有特定属性且链接没有安全跳转,且链接不在白名单中
            if (!hasFancybox && !isSafeGo && !isWhitelisted) {
                var originalUrl = link.href;
                link.href = "/go.html?goUrl=" + encodeURIComponent(originalUrl) + "&type=goDown";
            }
        }
    }
}

// 在 PJAX 完成时调用函数
document.addEventListener('pjax:complete', function() {
//     检查当前路径是否以 "/posts/" 开头
    if (window.location.pathname.startsWith('/posts/')) {
        updateLinks();
        console.log('pjax||文章页面,准备替换安全链接');
    } else {
        console.log('pjax||非文章页面无需替换安全链接');
    }
});

// 在页面加载完成后调用函数
window.addEventListener('load', function() {
//     检查当前路径是否以 "/posts/" 开头
    if (window.location.pathname.startsWith('/posts/')) {
        updateLinks();
        console.log('load||文章页面,准备替换安全链接');
    } else {
        console.log('load||非文章页面无需替换安全链接');
    }
});

这里需要修改的部分主要有:

  1. 第三行替换白名单,这些网站将默认为安全网站,不会被重定向到安全跳转页面,可以将友链的根域名放到这里,如果在链接中匹配到元素,将不进行替换。

  2. 第十行元素白名单:填写你想替换的页面的某个部分的ID或者类名,查找方式如下:

    元素白名单查找方式

  3. 第十六行元素黑名单:比如fancybox,fancybox是图片点击后放大预览的插件,如果链接替换了的话会导致无法正常放大,显示图片异常,这个应该都一样,确定的方式如下:

    确认fancybox

  4. 下面两个执行函数,由于我的页面开了pjax,不需要的删除即可,也可以保留,需要替换其中的posts路径,该路径在随便的文章页链接中就能找到:

    页面白名单

此时,功能基本实现了,你的文章页的外链卡片应该已经被替换为了安全链接。

评论区

每个评论系统基本上都会有一个回调函数,比如butterfly主题我们定位到文件:[blogroot]themes\butterfly\layout\includes\third-party\comments\twikoo.pug,修改其中的代码:

 - const { envId, region, option } = theme.twikoo
 - const { use, lazyload, count } = theme.comments

script.
  (() => {
    const getCount = () => {
      const countELement = document.getElementById('twikoo-count')
      if(!countELement) return
      twikoo.getCommentsCount({
        envId: '!{envId}',
        region: '!{region}',
        urls: [window.location.pathname],
        includeReply: true
      }).then(res => {
        countELement.textContent = res[0].count
      }).catch(err => {
        console.error(err)
      })
    }

    const init = () => {
      twikoo.init(Object.assign({
        el: '#twikoo-wrap',
        envId: '!{envId}',
        region: '!{region}',
        onCommentLoaded: () => {
          btf.loadLightbox(document.querySelectorAll('#twikoo .tk-content img:not(.tk-owo-emotion)'))
+          document.querySelectorAll('#twikoo .tk-comments-container a').forEach(function(aEl){
+            if (!aEl.hasAttribute('data-fancybox')) {
+              if (!aEl.href.startsWith(window.location.origin)) {
+                aEl.href = '/go.html?goUrl=' + encodeURIComponent(aEl.href) + '&type=goDown';
+              }
+            }
+          });
        }
      }, !{JSON.stringify(option)}))

      !{count ? 'GLOBAL_CONFIG_SITE.isPost && getCount()' : ''}
    }

    const loadTwikoo = () => {
      if (typeof twikoo === 'object') setTimeout(init,0)
      else getScript('!{url_for(theme.asset.twikoo)}').then(init)
    }

    if ('!{use[0]}' === 'Twikoo' || !!{lazyload}) {
      if (!{lazyload}) btf.loadComment(document.getElementById('twikoo-wrap'), loadTwikoo)
      else loadTwikoo()
    } else {
      window.loadOtherComment = loadTwikoo
    }
  })()

去掉加号即为正常缩进,注意第一二行,去掉前面的一格空格,可以看到我加了一些限制条件,和上面同理,这里我就不多说了,有什么问题可以在评论区交流。

最后功能实现。

--- 柳影曳曳,清酒孤灯,扬笔撒墨,心境如霜

Previous
Shynet:极为轻量化的访客监控系统
Next
twikoo仿段落评论,实现快速评论功能