TextTrackList.onchange
事件在 IE 和 Edge 中无效。在 Chrome 和 FireFox 中,它运行正常。
我可以使用任何替代方案吗?我搜索了可用的事件,但找不到任何。
或者我该如何创建变通方法?它适用于所有浏览器吗?
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>
答案 0 :(得分:4)
您也许可以创建一种polyfill。
首先要检测我们是否支持该事件,我们可以检查('onchange' in window.TextTrackList)
。因此,我们可以有条件地集成不完善的polyfill,并保持正确的实现不变。
然后,我们可以遍历TextTrackList的TextTracks上的每个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原型的原始addEventListener
,removeEventListener
和onchange
属性。
这是一个粗略的实现,它将不会处理 [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>
答案 1 :(得分: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>