Javascript 键绑定 "Interrupted" 或 "Lose Page Focus" 在 Chrome 中加载广告后

Javascript Keybindings "Interrupted" or "Lose Page Focus" After Advertisement Loads in Chrome

我有网页上箭头键的键事件绑定。这些绑定在页面加载时完美运行,但在加载页面上的 Google AdSense 广告时突然停止工作。并非每次 page/ad 加载都会发生这种情况,但大约有一半会发生这种情况。

发生这种情况时,如果我在页面加载期间重复按箭头键,绑定事件将成功触发,直到广告出现,此时事件停止工作,键的默认操作(滚动页面)开始当我一直按下键时发生。这很奇怪,因为我通过以下方式禁用了这些键的默认操作:

window.onkeydown = function(e) {
    // Spacebar, all four arrow keys
    if (e.keyCode == 32 || e.keyCode == 37 || e.keyCode == 38 || e.keyCode == 39 ||
        e.keyCode == 40) {
        e.preventDefault();
        return false;
    }
};

如果我随后单击页面正文中的任意位置,键绑定将再次开始工作。

我猜也许当广告完成加载时(在页面的其余部分之后),它会导致页面 "steals focus" 的某种中断(但显然不是 window 因为滚动仍然发生?)。

实际的键绑定是使用 Mousetrap 完成的,尽管这似乎与问题无关,而且我只在 Google Chrome 中遇到过这个问题。我在 Firefox 中没有这种行为。如果我在 Chrome 上启用 AdBlock,则不会出现此问题,进一步表明它是触发此 "interruption."

的广告

有什么明显的我没有意识到的可以通过这种方式完全中断键绑定,同时仍然允许键滚动页面直到用户再次点击正文吗?有什么方法可以防止广告以这种方式干扰用户与页面的交互吗?

一个建议是捕获 AdSense ADS_LOADED 事件,但这似乎只有在视频中使用 Google IMA SDK 时才可用。

我想出的解决方案是侦听所有模糊事件,当广告从文档正文中窃取焦点时,return 将焦点转移到 window。这仅在 window.focus() 被超时包装时有效,这是我目前不理解的。

isDescendant() 函数取自 this answer

function isDescendant(child, parent) {
    /*
    Test if child is contained within parent regardless of how many levels deep
    */
     var node = child.parentNode;
     while (node != null) {
         if (node == parent) {
             return true;
         }
         node = node.parentNode;
     }
     return false;
}

window.addEventListener('blur', function() {
    var ads = document.getElementsByClassName('ad');
    var n = ads.length;
    for (i=0; i<n; i++) {
        if (isDescendant(document.activeElement, ads[i])) {
            // This only works if wrapped in a timeout (why?)
            window.setTimeout(function () {
                window.focus();
            }, 0);
            break;
        }
    }
});