TextTrackList onchange 事件在 IE 和 Edge 中不起作用

Posted

技术标签:

【中文标题】TextTrackList onchange 事件在 IE 和 Edge 中不起作用【英文标题】:TextTrackList onchange event not working in IE and Edge 【发布时间】:2018-11-19 13:18:39 【问题描述】:

TextTrackList.onchange 事件在 IEEdge 中不起作用。在 ChromeFireFox 中它工作正常。

我可以使用任何替代方法吗?我搜索了可用的事件,但没有找到。

或者我怎样才能创建一个解决方法?所以它适用于所有浏览器?

https://www.javascripture.com/TextTrackList

var video = document.getElementById('video');

video.textTracks.addEventListener('change', function () 
  console.log("TextTracks change event fired!");
);
video 
  max-width: 400px;
  max-height: 400px;
<video controls id="video">
  <source src="https://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_30mb.mp4" type="video/mp4" />
  <track label="Caption #1" kind="subtitles" srclang="nl" src="path/to/caption1.vtt">
  <track label="Caption #2" kind="subtitles" srclang="en" src="path/to/caption2.vtt">
  <track label="Caption #3" kind="subtitles" srclang="de" src="path/to/caption3.vtt">
</video>

【问题讨论】:

有趣的问题。我发现这个在上述浏览器上工作,但采用不同的方法。也许这里也有适合你的东西。原文地址:developer.mozilla.org/en-US/Apps/Fundamentals/…现场演示:iandevlin.github.io/mdn/video-player-with-captionsGitHubSourceCode:github.com/iandevlin/iandevlin.github.io/tree/master/mdn/… 字幕工作正常,问题不在于字幕本身。但是,当您更改标题时,JavaScript 事件处理程序 onchange 在 IE 和 Edge 中不起作用。 有趣的部分是当你做 console.log(video.textTracks);事件发生前。 IE 只有事件“onaddtrack”; Chrome 具有添加、删除和更改功能。这大概就是问题所在。也许这不受支持,需要添加到该元素中。 【参考方案1】:

你也许可以创建一种 polyfill。

首先检测我们是否支持该事件,我们可以检查('onchange' in window.TextTrackList)。因此我们可以有条件地集成我们不完美的 polyfill,而保持正确的实现不变。

然后,我们可以在 TextTrackList 的 TextTrack 上迭代每个 x 次,以找出哪个是活动的,它应该是其 mode 设置为 "showing" 的那个。

现在,我们只需要存储上一个活动曲目并检查当前曲目是否相同。否则,触发事件。

所以一个简单的实现可以是

// in an outer scope
// textTracks is our TextTrackList object
var active = getActive();

// start polling
poll();

function poll() 
  // schedule next check in a frame
  requestAnimationFrame(poll);
  var current = getActive();

  if (current !== active) 
    active = current; // update the active one
    // dispatchEvent is not supported on TextTrackList in IE...
    onchange(
      target: textTracks
    );
  


function getActive() 
  for (var i = 0; i < textTracks.length; i++) 
    if (textTracks[i].mode === 'showing') 
      return textTracks[i];
    
  

但为了实现更好的 polyfill,我们需要覆盖 TextTrackList 原型的原始 addEventListenerremoveEventListeneronchange 属性。

这里是一个粗略的实现,不会处理[add/remove]EventListener的第三个参数。

(function() 
  /* Tries to implement an 'change' Event on TextTrackList Objects when not implemented */

  if (window.TextTrackList && !('onchange' in window.TextTrackList.prototype)) 
    var textTracksLists = [], // we will store all the TextTrackLists instances
      polling = false; // and run only one polling loop

    var proto = TextTrackList.prototype,

      original_addEvent = proto.addEventListener,
      original_removeEvent = proto.removeEventListener;

    var onchange = 
      get: getonchange,
      set: setonchange
    ;

    Object.defineProperty(proto, 'onchange', onchange);
    Object.defineProperty(proto, 'addEventListener', fnGetter(addListener));
    Object.defineProperty(proto, 'removeEventListener', fnGetter(removeListener));

    function fnGetter(fn) 
      return 
        get: function() 
          return fn;
        
      ;
    


    /* 	When we add a new EventListener, we attach a new object on our instance
    	This object set as '._fakeevent' will hold informations about
    		the current EventListeners
    		the current onchange handler
    		the parent <video> element if any
    		the current activeTrack
    */
    function initFakeEvent(instance) 
      // first try to grab the video element from where we were generated
      // this is useful to not run useless tests when the video is detached
      var vid_elems = document.querySelectorAll('video'),
        vid_elem = null;
      for (var i = 0; i < vid_elems.length; i++) 
        if (vid_elems[i].textTracks === instance) 
          vid_elem = vid_elems[i];
          break;
        
      

      textTracksLists.push(instance);
      instance._fakeevent = 
        parentElement: vid_elem,
        listeners: 
          change: []
        
      
      if (!polling)  // if we are the first instance being initialised
        polling = true;
        requestAnimationFrame(poll); // start the checks
      

      return instance._fakeevent;
    

    function getonchange() 
      var fakeevent = this._fakeevent;
      if (!fakeevent || typeof fakeevent !== 'object') 
        return null;
      
      return fakeevent.onchange || null;
    

    function setonchange(fn) 
      var fakeevent = this._fakeevent;
      if (!fakeevent) 
        fakeevent = initFakeEvent(this);
      
      if (fn === null) fakeevent.onchange = null;
      if (typeof fn !== 'function') return fn;
      return fakeevent.onchange = fn;
    

    function addListener(type, fn, options) 
      if (type !== 'change')  // we only handle change for now
        return original_addEvent.bind(this)(type, fn, options);
      
      if (!fn || typeof fn !== 'object' && typeof fn !== 'function') 
        throw new TypeError('Argument 2 of EventTarget.addEventListener is not an object.');
      
      var fakeevent = this._fakeevent;
      if (!fakeevent) 
        fakeevent = initFakeEvent(this);
      
      if (typeof fn === 'object') 
        if (typeof fn.handleEvent === 'function') 
          fn = fn.handleEvent;
         else return;
      
      // we don't handle options yet...
      if (fakeevent.listeners[type].indexOf(fn) < 0) 
        fakeevent.listeners[type].push(fn);
      
    

    function removeListener(type, fn, options) 
      if (type !== 'change')  // we only handle change for now
        return original_removeEvent.call(this, arguments);
      
      var fakeevent = this._fakeevent;
      if (!fakeevent || !fn || typeof fn !== 'object' && typeof fn !== 'function') 
        return
      
      if (typeof fn === 'object') 
        if (typeof fn.handleEvent === 'function') 
          fn = fn.handleEvent;
         else return;
      
      // we don't handle options yet...
      var index = fakeevent.listeners[type].indexOf(fn);
      if (index > -1) 
        fakeevent.listeners[type].splice(index, 1);
      
    


    function poll() 
      requestAnimationFrame(poll);
      textTracksLists.forEach(check);
    

    function check(instance) 
      var fakeevent = instance._fakeevent;
      // if the parent vid not in screen, we probably have not changed
      if (fakeevent.parentElement && !fakeevent.parentElement.parentElement) 
        return;
      
      // get the current active track
      var current = getActiveTrack(instance);
      // has changed
      if (current !== fakeevent.active) 
        if (instance.onchange) 
          try 
            instance.onchange(
              type: 'change',
              target: instance
            );
           catch (e) 
        
        fakeevent.listeners.change.forEach(call, this);
      
      fakeevent.active = current;
    

    function getActiveTrack(textTracks) 
      for (var i = 0; i < textTracks.length; i++) 
        if (textTracks[i].mode === 'showing') 
          return textTracks[i];
        
      
      return null;
    

    function call(fn) 
      fn(
        type: 'change',
        target: this
      );
    


  
)();

var video = document.getElementById('video');

video.textTracks.onchange = function ontrackchange(e) 
  console.log('changed');
;
video 
  max-width: 400px;
  max-height: 400px;
<video controls id="video">
  <source src="https://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_30mb.mp4" type="video/mp4" />
   <track label="Caption #1" kind="subtitles" srclang="nl" src="path/to/caption1.vtt">
   <track label="Caption #2" kind="subtitles" srclang="en" src="path/to/caption2.vtt">
   <track label="Caption #3" kind="subtitles" srclang="de" src="path/to/caption3.vtt">
</video>

【讨论】:

我在IE11上测试过,不行。更改标题时,console.log('changed', e); 不会被触发。 @Red han... 可能还有一些他们没有做对的事情... 将暂时删除,一旦我有时间启动我的虚拟机,可能会回来修复 现在似乎可以工作了,如果我有时间进一步测试,请告诉您。谢谢。 @Red 好的。我会看看我是否有时间像 polyfill 一样重写它(使用 addEventListener 和 removeEventListener) 谢谢,非常感谢!【参考方案2】:

你是对的,这是 IE 和 Edge 的问题。 您可以做的是收听轨道负载上的事件。请注意,轨道必须在同一个域中,否则,您将收到静默错误 (CURS),并且事件列表器不会列出该事件。

我已经创建了一个代码盘,所以你可以试试https://codepen.io/shahar-polak/project/live/AnVpEw/

注意:该代码将在 IE 和 Edge 中触发一个事件,在 Chrome 和 Firefox 中触发两个事件。确保在生产中使用之前检查客户端浏览器。

const video = document.getElementById('video');
const tracks = Array.from(document.querySelectorAll('track'));
video.textTracks.addEventListener('change', function() 
  console.log('TextTracks change event fired!');
);
// For IE and Edge
tracks.forEach((track) => 
  track.addEventListener("load", function() 
    console.log('change track');
  , false);
)
video 
  max-width: 400px;
  max-height: 400px;
<video controls id="video" >
    <source src="https://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_30mb.mp4" type="video/mp4"/>
    <track label="Caption #1" kind="subtitles" srclang="nl" src="./en.vtt">
    <track label="Caption #2" kind="subtitles" srclang="en" src="./en.vtt">
    <track label="Caption #3" kind="subtitles" srclang="de" src="https://iandevlin.com/html5/dynamic-track/captions/sintel-en.vtt">
</video>

【讨论】:

我也试图帮助他,并尝试过类似的事情。但是就像我刚刚测试过的你的情况一样,它只会在你第一次点击字幕时触发“更改”。所以总共会有 3 个变更事件,你不能有第四个或第五个。 @SehaxX 对不起,你是什么意思?他要求听音轨的变化事件,而这正是代码所做的。 我的意思是,如果您转到标题 1,然后转到标题 2,然后再转到标题 3(这很好用),但是当您返回标题 1 或 2 时,就没有任何事件了。或者也许我测试错了。 你是对的,我昨晚没有勾选这个选项。今晚我会看看我能做些什么并找到另一个解决方案。 是的,它有点作用。但就像@SehaxX 所说,它并没有真正起作用。

以上是关于TextTrackList onchange 事件在 IE 和 Edge 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

change事件和onchange事件,click事件和onclick事件这些都有啥区别

为啥onchange事件触发不了?求高手指教

获取 onChange 事件类型

js 触发select onchange事件代码

关于js的onchange事件

WdatePicker 日历控件的onchange事件屏蔽