最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - TextTrackList onchange event not working in IE and Edge - Stack Overflow

programmeradmin3浏览0评论

The TextTrackList.onchange event is not working in IE and Edge. In Chrome and FireFox it works fine.

Is there any alternative I can use? Ive searched through the available events but can't find any.

Or how can I create a workaround? So it works amongst all browsers?

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=".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>

The TextTrackList.onchange event is not working in IE and Edge. In Chrome and FireFox it works fine.

Is there any alternative I can use? Ive searched through the available events but can't find any.

Or how can I create a workaround? So it works amongst all browsers?

https://www.javascripture./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./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>

Share Improve this question edited Jun 22, 2018 at 8:24 Red asked Jun 9, 2018 at 20:28 RedRed 7,35410 gold badges52 silver badges95 bronze badges 3
  • Intresting issue. I have found this one working on the mentioned browsers, but with a different approach. Maybe there is something here for you too. Original URL: developer.mozilla/en-US/Apps/Fundamentals/… Live Demo: iandevlin.github.io/mdn/video-player-with-captions GitHubSourceCode: github./iandevlin/iandevlin.github.io/tree/master/mdn/… – SehaxX Commented Jun 15, 2018 at 11:36
  • The captions are working fine, the problem is not the captions itself. But the JavaScript event handler onchange, when you change a caption, isn't working in IE and Edge. – Red Commented Jun 15, 2018 at 11:42
  • The intresting part is that when you do console.log(video.textTracks); before the event. IE has only the event "onaddtrack"; Chrome has add, remove and change. This is probably the issue. Maybe that is not supported and need to be added to that element. – SehaxX Commented Jun 15, 2018 at 13:09
Add a ment  | 

2 Answers 2

Reset to default 4 +100

You might be able to create a kind of polyfill.

First to detect if we support the event or not, we can check for ('onchange' in window.TextTrackList). So we can integrate our unperfect polyfill conditionally and leave the correct implementations unchanged.

Then, we can iterate every x-time over our TextTrackList's TextTracks in order to find which one is the active one, it should be the one with its mode set to "showing".

Now, we just have to store the previous active track and check if the current one is the same. Otherwise, fire the Event.

So a simple implementation could be

// 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];
    }
  }
}

But to implement a better polyfill, we will want to override the original addEventListener, removeEventListener and onchange properties of the TextTrackList prototype.

Here is a rough implementation, which will not take care of the third param of [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./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>

You are correct, it is an issue with IE and Edge. What you can do is listen to an event on the track load. Just pay attention that the track has got to be on the same domain, otherwise, you will get a silent error (CURS) and the event lister will not list the event.

I have created a code pan so you could try it https://codepen.io/shahar-polak/project/live/AnVpEw/

NOTE: The code will trigger one event in IE and Edge and two events on Chrome and Firefox. Make sure you check for the client browser before using in production.

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./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./html5/dynamic-track/captions/sintel-en.vtt">
</video>

发布评论

评论列表(0)

  1. 暂无评论