为什么音视频事件不会冒泡?

32

我曾想知道为什么我的一些JavaScript不起作用,直到我发现音频事件没有冒泡到DOM树中,例如timeupdate事件。

不让音频和视频标签的事件冒泡是否有合理的理由?

1个回答

48
事件冒泡存在的原因是解决事件的目标元素到底是哪个的不明确问题。所以,如果你在一个 div 上点击,你是想点击这个 div 还是它的父元素呢?如果子元素没有绑定点击事件处理函数,那么它会检查父元素,以此类推。我相信你知道这是怎样工作的。
音频事件之所以不会冒泡是因为它们对于其他元素来说没有意义。当你触发一个音频元素上的 "timeupdate" 事件时,无论它是针对音频元素本身还是其父 div 元素,都不存在歧义,所以没有必要将其冒泡。
你可以在这里阅读有关事件冒泡的更完整的历史记录。
事件委托仍然可以通过利用事件的捕获阶段实现。只需将 true 作为 addEventListener 的第三个参数即可,像这样:
document.addEventListener('play', function(e){
    //e.target: audio/video element
}, true);

请注意,此事件不会冒泡,而是沿着DOM树向下传递,无法使用 stopPropagation 停止。

如果您想要将其与jQuery的.on/.off方法一起使用(例如具有命名空间和其他jQuery事件扩展),可以使用以下函数,该函数取自webshim库

$.createEventCapturing = (function () {
    var special = $.event.special;
    return function (names) {
        if (!document.addEventListener) {
            return;
        }
        if (typeof names == 'string') {
            names = [names];
        }
        $.each(names, function (i, name) {
            var handler = function (e) {
                e = $.event.fix(e);

                return $.event.dispatch.call(this, e);
            };
            special[name] = special[name] || {};
            if (special[name].setup || special[name].teardown) {
                return;
            }
            $.extend(special[name], {
                setup: function () {
                    this.addEventListener(name, handler, true);
                },
                teardown: function () {
                    this.removeEventListener(name, handler, true);
                }
            });
        });
    };
})();

用法:

$.createEventCapturing(['play', 'pause']);

$(document).on('play', function(e){
    $('audio, video').not(e.target).each(function(){
        this.pause();
    });
});

25
虽然将媒体事件向其他类型的元素冒泡没有太多意义,但在body全局注册事件监听器是有意义的,这样可以捕获文档内任何媒体元素触发的事件。 - Sergiu Dumitriu
这真是遗憾,但可以理解。不幸的是,这使得像jQuery的委托系统这样的“技巧”无法使用。也就是说,你可以将一个play监听器附加到例如body上,当任何<audio>触发该事件时,函数将被执行。(这实际上是@Sergiu的想法。) - pimvdb
这不是我的问题 :) 我通常不会太快地给出赏金,但不用担心。 - pimvdb
谢谢您提供这个简洁的解决方案,正是我长期寻找的! 对于像我一样使用 rxjs' fromEvent 的任何人,您可以添加第三个参数来捕获子元素中的事件,如下所示: fromEvent(document, 'timeupdate', {capture: true}) - marjon4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接