AI摘要
文章介绍了如何修复Typecho博客评论插件Emoticon无法显示表情的问题。问题出在插件的emo.js文件未能正确加载,修复步骤包括确保emo.js在jQuery之后加载,并提供了详细的代码示例。同时,还提供了针对PJAX网站的优化方案,包括避免重复加载、精准触发、事件清理和动态加载。最后,还提供了修改后的footer.php代码,以供参考。

评论没有表情,总感觉少点什么,于是找到了 Emoticon

下载链接:https://github.com/s-Ruthless/Typecho-Plugin-Emoticon

但是将 <?php Emoticon_Plugin::emoOut(); ?> 添加到 comments.php 文件后出现了问题:

文章内点击图标没反应

通过F12查询,发现插件的 emo.js 未能加载

于是尝试修复 JS 加载顺序,若 emo.js 加载在 jQuery 之前,会导致方法定义失败或事件绑定失效,且网络波动会让加载顺序随机变化

解决步骤1

确保 emo.jsjQuery 之后加载,在 footer.php 中的引入:

<!-- 美团 CDN 加载 jQuery 3.6.0(比本地快,无需额外下载) -->
<script type="text/javascript" src="https://awp-assets.meituan.net/thh/thh_feb_web_portal/js/jquery-3.6.0.min.js"></script>
<!-- 本地加载 emo.js(已修复事件和插入逻辑) -->
<script type="text/javascript" src="<?php echo $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST']; ?>/usr/plugins/Emoticon/assets/emo.js" defer></script>

这里 jQuery 也可以使用其他的,如:
[阿里]https://cdn.staticfile.org/jquery/3.6.0/jquery.min.js
[百度]https://apps.bdimg.com/libs/jquery/3.6.0/jquery.min.js
[奇虎360]https://s.ssl.qhres2.com/baomitu/jquery/3.3.1/jquery.min.js
[BootCDN]https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js

解决步骤2

以上在网站没有开启 PJAX 情况下,正常加载。如果开启了 PJAX ,就会导致打开文章后跳转下一篇或返回首页,加载特别缓慢。

于是提供了以下优化方案

关键优化点说明

  • 避免重复加载:用 window.EMO_PLUGIN.loaded 标记,emo.js 仅加载一次,PJAX 切换不重复请求;
  • 精准触发:只有找到评论输入框(#comment)才执行初始化,首页等无评论区页面不触发,节省资源;
  • 事件清理:PJAX 切换时调用 destroy() 解绑旧事件,避免内存泄漏拖慢页面;
  • 动态加载:emo.js 用动态创建方式加载,不阻塞页面渲染,且加载完成后才初始化。

替换方案

footer.php 中找到这段:

    });
}
</script>
<!-- 初始化 -->
<script>
    // 初始化main容器

修改成:

    });
}
</script>
<!-- 表情插件脚本(适配PJAX,优化加载速度) -->
<script>
// 全局标记:避免脚本重复加载
window.EMO_PLUGIN = window.EMO_PLUGIN || {
    loaded: false,
    init: function() {
        // 仅在有评论输入框的页面执行(精准触发)
        const commentInput = document.getElementById('comment'); // 替换为你的评论输入框ID
        if (!commentInput) return;

        // 定义表情核心初始化逻辑(事件委托,支持动态DOM)
        function initEmoticon() {
            // 表情弹框显示/隐藏
            $(document).off('click.emoLogo').on('click.emoLogo', '.OwO-logo', function(e){
                e.stopPropagation();
                $('.bq-list').toggleClass('active');
                // 表情分类切换
                $(document).off('click.emoBar').on('click.emoBar', '.OwO-bar-item', function(){
                    $(this).addClass('active').siblings().removeClass('active');
                    const idx = $(this).index();
                    $('.OwO-emoji ul').eq(idx).addClass('active-txt').siblings().removeClass('active-txt');
                });
            });

            // 点击页面其他地方关闭弹框
            $(document).off('click.emoDoc').on('click.emoDoc', function(e){
                if (!$(e.target).closest('.OwO-logo, .bq-list').length) {
                    $('.bq-list').removeClass('active');
                }
            });

            // 点击表情插入输入框(原生JS,无依赖)
            $(document).off('click.emoItem').on('click.emoItem', '.OwO-item', function(e){
                e.stopPropagation();
                const txt = $(this).attr('data-title');
                if (commentInput) {
                    if (commentInput.selectionStart || commentInput.selectionStart === 0) {
                        const startPos = commentInput.selectionStart;
                        const endPos = commentInput.selectionEnd;
                        commentInput.value = commentInput.value.substring(0, startPos) + txt + commentInput.value.substring(endPos);
                        commentInput.focus();
                        commentInput.selectionStart = commentInput.selectionEnd = startPos + txt.length;
                    } else {
                        commentInput.value += txt;
                    }
                }
                $('.bq-list').removeClass('active');
            });
        }

        // 首次加载emo.js(仅加载一次)
        if (!window.EMO_PLUGIN.loaded) {
            window.EMO_PLUGIN.loaded = true;
            // 动态加载emo.js,确保在jQuery之后执行
            const emoScript = document.createElement('script');
            emoScript.src = '<?php echo $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST']; ?>/usr/plugins/Emoticon/assets/emo.js';
            emoScript.defer = true;
            emoScript.onload = initEmoticon; // 加载完成后初始化
            document.body.appendChild(emoScript);
        } else {
            // 已加载过,直接重新初始化(适配PJAX切换)
            initEmoticon();
        }
    },
    // PJAX切换时清理资源,避免内存泄漏
    destroy: function() {
        $(document).off('click.emoLogo click.emoBar click.emoDoc click.emoItem');
    }
};

// 非PJAX页面:页面加载完成后初始化
if (!<?php echo $this->options->pjaxStatus == 'yes' ? 'true' : 'false'; ?>) {
    $(function() {
        window.EMO_PLUGIN.init();
    });
}
</script>
<!-- 初始化 -->
<script>
    // 初始化main容器

额外修改:PJAX 回调中添加表情初始化 / 清理(找到 PJAX 配置部分)
PJAX 代码片段(接着找到这段):

    }).on('pjax:complete', function(event, data, status, xhr, options) {
        if (event.relatedTarget) {
            if (event.relatedTarget.tagName === 'FORM' && event.relatedTarget.id === 'comment-form') {
                // 如果PJAX来源是评论表单,则显示提示信息
                let message = (data.responseText.match(/<div class="container">\s*([\s\S]*?)\s*<\/div>/i) || [, ''])[1].trim();
                if (message) {
                    alert(message);
                    $.pjax({
                        url: xhr.url.replace(/\/comment$/, '/#comments'),
                        container: '#main',
                        fragment: '#main'
                    });
                }
            } else if (event.relatedTarget.tagName === 'FORM' && event.relatedTarget.classList.contains('protected')) {
                // 如果PJAX来源是加密文章密码表单,则显示提示信息
                let message = (data.responseText.match(/<div class="container">\s*([\s\S]*?)\s*<\/div>/i) || [, ''])[1].trim();
                if (message) {
                    alert(message);
                }
            }
        }

        // PJAX完成,初始化main容器(包括代码复制功能)
        initMain();
    }).on('pjax:end', function() {

修改成(添加表情初始化 / 清理):

    }).on('pjax:complete', function(event, data, status, xhr, options) {
        if (event.relatedTarget) {
            if (event.relatedTarget.tagName === 'FORM' && event.relatedTarget.id === 'comment-form') {
                // 如果PJAX来源是评论表单,则显示提示信息
                let message = (data.responseText.match(/<div class="container">\s*([\s\S]*?)\s*<\/div>/i) || [, ''])[1].trim();
                if (message) {
                    alert(message);
                    $.pjax({
                        url: xhr.url.replace(/\/comment$/, '/#comments'),
                        container: '#main',
                        fragment: '#main'
                    });
                }
            } else if (event.relatedTarget.tagName === 'FORM' && event.relatedTarget.classList.contains('protected')) {
                // 如果PJAX来源是加密文章密码表单,则显示提示信息
                let message = (data.responseText.match(/<div class="container">\s*([\s\S]*?)\s*<\/div>/i) || [, ''])[1].trim();
                if (message) {
                    alert(message);
                }
            }
        }

        // PJAX完成:先清理表情事件,再初始化
        if (window.EMO_PLUGIN) {
            window.EMO_PLUGIN.destroy(); // 清理旧事件,避免内存泄漏
            window.EMO_PLUGIN.init();    // 重新初始化表情(适配新页面)
        }
        // 初始化main容器(包括代码复制功能)
        initMain();
    }).on('pjax:end', function() {

注意事项

  • 确保评论输入框的 ID#comment ,如果不是,替换代码中 document.getElementById('comment') 里的 comment 为实际 ID
  • 移除之前在 footer.php 中直接插入的表情脚本(避免重复);
  • 清缓存后测试:PJAX 切换页面时,「网络」面板无重复的 emo.js 请求,加载速度和无表情插件时一致。

未修改前原版本 footer.php

此内容需要评论后才能查看哦 (*^▽^*)

修改后 footer.php

此内容需要评论后才能查看哦 (*^▽^*)