Featured image of post 添加 Turbolinks 支持

添加 Turbolinks 支持

前言

​ 单页应用刷新,速度很快,不刷新,比预加载还快

正文

​ 问题是原来的功能都没有考虑这一点,缝缝补补一下午了,也就把灯箱、锚点目录,跳转页加上去了,还有很多js功能都没有,回退了,不过还是把代码放这记录一下

themes\hugo-magic\layouts\_default\baseof.html

​ 下面的是完整代码

<!DOCTYPE html>
<html lang="{{ .Site.LanguageCode }}" dir="{{ default `ltr` .Language.LanguageDirection }}">
    <head>
        <!-- 立即执行主题初始化,必须放在最前面 -->
        <script>
            (function() {
                // 防止重复初始化和闪烁
                if (document.documentElement.dataset.scheme) return;
                
                const colorSchemeKey = 'StackColorScheme';
                const colorScheme = localStorage.getItem(colorSchemeKey) || 'dark';
                const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
                
                // 立即设置主题,防止闪烁
                document.documentElement.dataset.scheme = 
                    colorScheme === 'dark' || (colorScheme === 'auto' && prefersDark) 
                    ? 'dark' 
                    : 'light';
            })();
        </script>
        <style>
            /* 预设主题样式,防止闪烁 */
            :root[data-scheme="light"] {
                --theme-bg: #ffffff;
                --theme-text: #000000;
                background-color: #ffffff;
                color: #000000;
            }
            :root[data-scheme="dark"] {
                --theme-bg: #1a1a1a;
                --theme-text: #ffffff;
                background-color: #1a1a1a;
                color: #ffffff;
            }
            /* 添加渐变效果,但仅在初始加载后 */
            .theme-loaded {
                transition: background-color 0.3s ease, color 0.3s ease;
            }
            
            /* 添加页面切换动画 */
            .turbolinks-progress-bar {
                height: 3px;
                background-color: var(--theme-text);
            }
        </style>
        <!-- 添加Turbolinks支持 -->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/turbolinks/5.2.0/turbolinks.js"></script>
        <script>
            // 配置Turbolinks
            window.Turbolinks = Turbolinks;
            
            // 定义外部链接处理函数
            function handleExternalLinks() {
                function checkParent(element, classNames) {
                    while (element) {
                        if (element.classList && classNames.some(cn => element.classList.contains(cn))) {
                            return true;
                        }
                        element = element.parentElement;
                    }
                    return false;
                }
                
                // 需要排除的类名
                var excludedClasses = ['talks_comments', 'tiaozhuan-button', 'search-result'];
                
                // 需要排除的页面路径
                var excludedPaths = ['/search/', '/search'];
                
                document.body.addEventListener('click', function(e) {
                    // 如果当前页面是搜索页面,不处理链接
                    if (excludedPaths.some(path => window.location.pathname.startsWith(path))) {
                        return;
                    }
                    
                    let target = e.target;
                    while (target && target.nodeName !== 'A') {
                        target = target.parentNode;
                    }
                    
                    if (target && target.nodeName === 'A' &&
                        !checkParent(target, excludedClasses) &&
                        target.hostname !== window.location.hostname) {
                        e.preventDefault();
                        let encodedUrl = btoa(target.href);
                        let url = '/tiaozhuan?target=' + encodedUrl;
                        window.open(url, '_blank');
                    }
                });
            }
            
            // 在页面加载和Turbolinks加载时都初始化外部链接处理
            document.addEventListener('DOMContentLoaded', handleExternalLinks);
            document.addEventListener('turbolinks:load', handleExternalLinks);
            
            // 处理目录锚点点击
            function handleTocClick(e) {
                const link = e.target.closest('a');
                if (!link) return;
                
                const href = link.getAttribute('href');
                if (!href || !href.startsWith('#')) return;
                
                e.preventDefault();
                const targetId = href.slice(1);
                const targetElement = document.getElementById(targetId);
                
                if (targetElement) {
                    e.preventDefault();
                    // 平滑滚动到目标位置
                    targetElement.scrollIntoView({
                        behavior: 'smooth',
                        block: 'start'
                    });
                    // 更新 URL,但不触发滚动
                    history.pushState(null, null, href);
                }
            }
            
            // 在页面加载和Turbolinks加载时绑定目录点击事件
            function initTocClickHandler() {
                const toc = document.querySelector('#TableOfContents');
                if (toc) {
                    toc.removeEventListener('click', handleTocClick);
                    toc.addEventListener('click', handleTocClick);
                }
            }
            
            document.addEventListener('DOMContentLoaded', initTocClickHandler);
            document.addEventListener('turbolinks:load', initTocClickHandler);
            
            // 处理 Turbolinks 页面加载后的锚点跳转
            document.addEventListener('turbolinks:load', function() {
                const hash = window.location.hash;
                if (hash) {
                    const targetElement = document.querySelector(hash);
                    if (targetElement) {
                        setTimeout(() => {
                            targetElement.scrollIntoView({
                                behavior: 'smooth',
                                block: 'start'
                            });
                        }, 100);
                    }
                }
            });
            
            // 处理Turbolinks的外部链接
            document.addEventListener('turbolinks:before-visit', function(event) {
                const url = new URL(event.data.url);
                const isExternal = url.hostname !== window.location.hostname;
                
                if (isExternal) {
                    event.preventDefault();
                }
            });

            // 加载目录滚动监听脚本
            function loadScrollspy() {
                return new Promise((resolve, reject) => {
                    if (window.setupScrollspy) {
                        resolve(window.setupScrollspy);
                        return;
                    }

                    const script = document.createElement('script');
                    script.src = '/ts/scrollspy.js';
                    script.onload = () => resolve(window.setupScrollspy);
                    script.onerror = reject;
                    document.head.appendChild(script);
                });
            }

            // 初始化目录滚动监听
            async function initScrollspy() {
                try {
                    const setupScrollspy = await loadScrollspy();
                    setupScrollspy();
                } catch (error) {
                    console.error('Failed to initialize scrollspy:', error);
                }
            }

            // 在页面加载和Turbolinks加载时初始化目录滚动监听
            document.addEventListener('DOMContentLoaded', initScrollspy);
            document.addEventListener('turbolinks:load', initScrollspy);

            // 加载Twikoo脚本
            function loadTwikoo() {
                return new Promise((resolve, reject) => {
                    if (window.twikoo) {
                        resolve(window.twikoo);
                        return;
                    }

                    const script = document.createElement('script');
                    script.src = '//cdn.jsdelivr.net/npm/twikoo@1.6.40/dist/twikoo.all.min.js';
                    script.onload = () => resolve(window.twikoo);
                    script.onerror = reject;
                    document.head.appendChild(script);
                });
            }

            // 初始化Twikoo评论
            async function initTwikoo() {
                const tcomment = document.getElementById('tcomment');
                if (!tcomment) return;

                try {
                    const twikoo = await loadTwikoo();
                    // 清空评论区内容
                    tcomment.innerHTML = '';
                    // 重新初始化Twikoo
                    await twikoo.init({
                        envId: '{{ .Site.Params.comments.twikoo.envId }}',
                        el: '#tcomment'
                    });
                } catch (error) {
                    console.error('Failed to initialize Twikoo:', error);
                }
            }

            // 在Turbolinks加载完成后初始化Twikoo
            document.addEventListener('turbolinks:load', initTwikoo);
        </script>
        {{- partial "head/colorScheme" . -}}
        {{- partial "head/head.html" . -}}
        {{- block "head" . -}}{{ end }}
    </head>
    <body class="{{ block `body-class` . }}{{ end }}">
        <script>
            // 添加主题加载完成标记,启用过渡效果
            document.documentElement.classList.add('theme-loaded');
            
            // 启动Turbolinks
            if (typeof Turbolinks !== 'undefined') {
                Turbolinks.start();
            }
        </script>
        {{/* The container is wider when there's any activated widget */}}
        {{- $hasWidget := false -}}
        {{- range .Site.Params.widgets -}}
            {{- if gt (len .) 0 -}}
                {{- $hasWidget = true -}}
            {{- end -}}
        {{- end -}}
        <div class="container main-container flex on-phone--column {{ if $hasWidget }}extended{{ else }}compact{{ end }}">
            {{- block "left-sidebar" . -}}
                {{ partial "sidebar/left.html" . }}
            {{- end -}}
            {{- block "right-sidebar" . -}}{{ end }}
            <main class="main full-width">
                {{- block "main" . }}{{- end }}
            </main>
        </div>
        {{ partial "footer/include.html" . }}
        
        <!-- 添加 medium-zoom 支持 -->
        <script src="https://s4.zstatic.net/npm/medium-zoom/dist/medium-zoom.min.js"></script>
        <style>
            /* 自定义 medium-zoom 样式 */
            .medium-zoom-overlay {
                background-color: rgba(0, 0, 0, 0.95) !important;
                z-index: 999;
            }
            .medium-zoom-image--opened {
                z-index: 1000;
            }
        </style>
        <script>
            // 全局变量存储zoom实例
            let zoomInstance = null;

            // 初始化图片缩放
            function initMediumZoom() {
                // 如果已经初始化过,先清理
                if (zoomInstance) {
                    zoomInstance.detach();
                }

                if (typeof mediumZoom !== 'undefined') {
                    // 创建新的实例
                    zoomInstance = mediumZoom('article.main-article img:not(.nozoom):not(.site-logo):not(.link-card .article-image img):not(.article-list--compact.links .article-image img):not(.article-header .article-image img)', {
                        margin: 24,
                        background: 'rgba(0, 0, 0, 0.95)',
                        scrollOffset: 0,
                    });
                }
            }

            // 初始化
            initMediumZoom();

            // Turbolinks 页面切换后重新初始化
            document.addEventListener('turbolinks:load', initMediumZoom);
        </script>
    </body>
</html>
CC BY-NC-SA 4.0 创意的非商业派对入场券
最后更新于 2025-01-19 00:07
晚来天欲雪,能饮一杯无