Tools 是非官方社区Wiki。社区文档正在编写中,欢迎参与。 Wiki编辑答疑群:717421103
版本250722.2
全站通知:

Widget:AnchorFix

来自WIKI实验室WIKI_BWIKI_哔哩哔哩
跳到导航 跳到搜索

AnchorFix - 修复锚点跳动

措施是替换 jquery 的 animate函数,带有 scrollTop 属性的 animate 不执行

已知副作用:

  • #gotop 点击事件,已经用原生js增加了点击回顶部事件。
尚不清楚其他副作用!

源码:

<script>
/**
 * 避免锚点乱跑,措施是当jquery的 animate 函数带有 scrollTop 参数时, animate 不执行
 *
 * 目前 jquery 和 乱搞锚点的 JS ,是在一个文件中加载的:
 * load.php?lang=zh-cn&modules=jquery%2Coojs-ui-core%2Coojs-ui-widgets%7Cmediawiki.extra.bili&skin=vector&version=kyo1q
 * 因此需要在 jquery 生效后,调用 scrollTop 之前,替换 animate,窗口时间很短
 *
 * 没有优雅的办法,由于jq加载需要时间,而且Bugfix会提前加载另一份jq,
 * 
 * 目前有效的方案是jq加载后,多次覆盖。
 *
 * 已知副作用: #gotop 点击事件失效,方案是用原生js实现点击事件。
 * 
 */
(function() {
    var MAX_WAIT_COUNT = 152; // 最长等待 152 x 32ms = 4864ms

    // 等待jQuery加载,很激进,每32ms检查一次,然后限制animate函数
    function waitJQueryLoad(waitCount) { 
        if (window.jQuery || waitCount > MAX_WAIT_COUNT) {
            jqueryAnimate_YouthPlus();
        } else{
            setTimeout(function() {waitJQueryLoad(waitCount + 1)}, 32); // 每隔32毫秒检查一次jq加载没
        }
    }
    // 开始等待JQ
    waitJQueryLoad(0);

    function jqueryAnimate_YouthPlus() { // 通过禁用 animate(with scrollTop), 实现“青春版”Animate
        var local_animate = null;
        console.info('jQuery loaded,准备禁用 animate(with scrollTop)');
        let count = 0;
        const intervalId = setInterval(() => {
            if (count >= 50) {
                clearInterval(intervalId);
                return;
            }
            count++;

            // 无论如何都尝试覆盖 animate
            if(window.jQuery.fn.animate){
                if (local_animate === null) {
                    // 存一个本地的 animate 函数,用于过滤 scrollTop 属性,带有 scrollTop 属性的 animate 不执行
                    local_animate = window.jQuery.fn.animate;
                    console.log('禁止 JQuery 的 animate 函数使用 scrollTop 参数,避免锚点跳动。');
                }
                
                window.jQuery.fn.animate = function(prop, speed, easing, callback) {
                    if (prop.scrollTop !== undefined) {
                        console.warn('已禁止 JQuery 的 animate 函数使用 scrollTop 参数,这是由于相关平台JS会导致锚点跳动。 此次调用的prop参数:', prop);
                        return this;
                    }
                    return local_animate(prop, speed, easing, callback);
                };
            }

            // 目前测试可以不禁用 scrollTop 函数
            // window.jQuery.fn.scrollTop = function(val) {
            //     console.warn('scrollTop function is disabled');
            //     return this; 
            // };
        }, 10);
    }

})();

// 用原生js实现#gotop的点击回顶部事件
document.addEventListener('click', function(event) {
    if (event.target.matches('#gotop')) {
        window.scrollTo({
            top: 0,
            behavior: 'smooth'
        });
    }
});

</script>