I have a script that adds a video tag to a page. That bit works fine, but having added the video I want to add an event listener to the video tag, but it comes back as undefined. I’m assuming it’s because the additional HTML hasn’t yet been added. How do I wait till the video element exists?
const pv = document.querySelectorAll(".playVid"),
ib = document.getElementById("vidbox");
for (let j = 0; j < pv.length; j++) {
pv[j].addEventListener("click", function() {
let vid = this.dataset.vid;
fetch("getVideo.php?id=" + vid)
.then((response) => {
if (response.ok) return response;
throw Error(response.statusText);
})
.then((response) => response.text())
.then((text) => ib.innerHTML = text)
.catch((error) => console.log("There was an error:", error));
document.getElementById("infobox").className = "visible";
document.getElementById("boxHolder").style.display = "flex";
const v = document.getElementsByTagName("video")[0];
console.log(v); // undefined
})
};
It’s been a few years since I worked with it. At the time, with a good chance it was my inexperience, I found it to result in extremely heavy resource consumption. To the point that I abandoned the attempt.
That the API still exists suggests my problems were mine own fault, or perhaps things have improved since then.
Hey @Gandalf, using a mutation observer would be an option, but if you have direct control over the video being added to the document, couldn’t you just add the event listener inside the appropriate callback? I.e.
fetch("getVideo.php?id=" + vid)
//...
.then((text) => {
ib.innerHTML = text
const video = ib.querySelector('video')
video.addEventListener(/* ... */)
})
Thanks guys. I did play with MutationObserver but got thoroughly confused.
Adding the event listener inside the callback was easy enough but it only seems to call the function once at the beginning, so I guess I’m missing something else.
const pv = document.querySelectorAll(".playVid"),
ib = document.getElementById("vidbox");
for (let j = 0; j < pv.length; j++) {
pv[j].addEventListener("click", function() {
let vid = this.dataset.vid;
fetch("getVideo.php?id=" + vid)
.then((response) => {
if (response.ok) return response;
throw Error(response.statusText);
})
.then((response) => response.text())
.then((text) => {
ib.innerHTML = text;
const v = ib.querySelector("video");
v.addEventListener("timeupdate", updateVc(v, vid));
})
.catch((error) => console.log("There was an error:", error));
document.getElementById("infobox").className = "visible";
document.getElementById("boxHolder").style.display = "flex";
})
};
function updateVc (v, vid) {
console.log(v.currentTime);
if (v.currentTime > (v.duration * .5)) {
fetch("updVideoCount.php?id=" + vid);
// ensure it's updated only once
v.removeEventListener("timeupdate", updateVc);
}
};
You need to pass a callback function to addEventListener(), but what you’re doing is calling the function directly and passing the result (which is undefined). So you’d either have to define the function in place, or return another function from updateVc():
fetch("getVideo.php?id=" + vid)
//...
.then((text) => {
ib.innerHTML = text
const video = ib.querySelector('video')
video.addEventListener(function updateVc () {
console.log(video.currentTime, vid)
// ...
})
})
// Or:
fetch("getVideo.php?id=" + vid)
//...
.then((text) => {
ib.innerHTML = text
const video = ib.querySelector('video')
video.addEventListener(updateVc(video, vid))
})
function updateVc (video, vid) {
return function () {
console.log(video.currentTime, vid)
// ...
}
}
That gets me a step forward. I think I need to use the second option because I need to remove the event listener and I don’t think I can do that with an anonymous function? However, the removeEventListener isn’t working now.
(I haven’t got as far as implementing post #6 yet!)
But the callback isn’t anonymous in the 1st option, so you can still remove the event listener as usual:
video.addEventListener('timeupdate', function updateVc () {
// ...
video.removeEventLister('timeupdate', updateVc)
})
As for the 2nd option, the name of the function is probably a bit unfortunate as it’s actually a factory function; what you’d remove is the inner one that’s getting returned:
video.addEventListener('timeupdate', createUpdateHandler(vid))
function createUpdateHandler (vid) {
return function updateVc (event) {
// ...
event.target.removeEventLister('timeupdate', updateVc)
}
}