给自己的博客添加无刷新跳转

2021-12-31 02:51:53js

给自己的博客添加无刷新跳转

注意事项:innerHTML之后的代码,是不会运行其中的js的。需要自己解析里面的js

// 处理浏览器历史记录
let oldUrl = location.href;
window.addEventListener('popstate', function (e) {
  let href = location.href;
  if (href.replace(oldUrl, '').startsWith('#') || oldUrl.replace(href, '').startsWith('#')) {
    oldUrl = href;
  } else {
    if (href.startsWith(location.origin)) {
      noRefreshJump(href, 'change');
    }
  }
});
document.addEventListener('click', e => {
  // 禁止链接跳转行为
  e.preventDefault();
  let href = e.target.href;
  let target = e.target.target;
  // 处理被点击的标签非标链接本身的情况
  if (!href) {
    href = e.target.closest('a')?.href;
    target = e.target.closest('a')?.target;
  }
  // 只处理本站链接。
  if (href && href.startsWith(location.origin)) {
    if (target === '_blank') {
      window.open(location.origin + href, '_blank');
    } else {
      noRefreshJump(href, 'click');
    }
  }
});

function noRefreshJump(href, type) {
  let oldMain = document.getElementById('main');
  let body = document.body;

  // 隐藏侧边栏
  document.querySelector('.mdui-overlay-show')?.click();

  // 请求页面数据
  loading();
  fetch(href).then(res => res.text()).then(res => {
    // 识别标题
    let title = res.match(/<title>([^<>]+?)<\/title>/)?.[1];
    // 识别正文
    let mainString = res.match(/<main[^<>]*>([\s\S]+?)<\/main>/)?.[1];
    if (mainString) {
      let scripts = mainString.match(/<script[^<>]*>[\s\S]*?<\/script>/ig) || [];
      scripts.forEach((item, index) => {
        mainString = mainString.replace(item, '');
      });
      oldMain.innerHTML = mainString;

      // 处理滚动条
      if (document.scrollingElement.scrollTop) {
        document.scrollingElement.scrollTop = 0;
      }

      // 可能需要重新调用一下页面用到的功能
      // other

      runScript(scripts);

      // 无刷新跳转
      if (type === 'click') {
        window.history.pushState({ href }, title, href);
      } else {
        oldUrl = href;
      }
    } else {
      // 错误页面处理
      oldMain.innerHTML = res.match(/<body>([\s\S]+?)<\/body>/i)?.[1]?.trim();
    }
  }).catch(e => {
    // 错误页面处理
    oldMain.innerHTML = `<pre>${e.toString()}</pre>`;
  }).finally(() => {
    closeLoading();
  });
}

function runScript(list) {
  let srcList = [];
  let scriptList = [];
  list.forEach(item => {
    let src = item.match(/<script[^<>]+?src="([^\s<>"]+)"\s?([^<>]+)?>/)?.[1];
    if (src) {
      let oldScript = document.querySelector(`script[src*="${src}"]`);
      if (!oldScript) {
        srcList.push(src);
      }
    } else {
      let script = item.match(/<script[^<>]*>([\s\S]+?)<\/script>/)?.[1];
      scriptList.push(script);
    }
  });

  let scriptBox = document.getElementById('scriptBox');
  if (!scriptBox) {
    scriptBox = document.createElement('div');
    scriptBox.id = 'scriptBox';
    document.body.appendChild(scriptBox);
  } else {
    scriptBox.innerHTML = '';
  }

  load();

  function load() {
    if (srcList.length === 0) {
      runJs();
      return;
    }
    let item = srcList.shift();
    let script = document.createElement('script');
    script.src = item;
    document.head.appendChild(script);
    script.addEventListener('load', () => {
      load();
    });
  }

  function runJs() {
    if (scriptList.length === 0) {
      return;
    }
    let item = scriptList.shift();
    let script = document.createElement('script');
    script.innerHTML = item;
    scriptBox.appendChild(script);
    runJs();
  }
}

function loading() {
  let loading = document.getElementById('loading');
  if (loading) {
    loading.style.display = 'flex';
  } else {
    loading = document.createElement('div');
    loading.id = 'loading';
    loading.style.display = 'flex';
    loading.style.justifyContent = 'center';
    loading.style.position = 'fixed';
    loading.style.top = '0';
    loading.style.left = '0';
    loading.style.right = '0';
    loading.style.bottom = '0';
    loading.style.paddingTop = '3rem';
    loading.style.zIndex = '999999999';
    loading.innerHTML = '<div class="mdui-spinner mdui-spinner-colorful"></div>';
    document.body.appendChild(loading);
    mdui.mutation();
  }
}

function closeLoading() {
  let loading = document.getElementById('loading');
  if (loading) {
    loading.style.display = 'none';
  }
}