I have two templates that each contain a Vimeo iframe
player. I'm using FlowRouter to render the templates through {{> Template.dynamic template=main}}
on the main layout.
In both templates I add listeners for video events in onCreated
Template.view.onCreated( function() {
var self = this;
if (window.addEventListener) {
window.addEventListener('message', function(event) {
viewMessageReceived(event, self)}, false);
} else {
window.attachEvent('onmessage', function(event){
viewMessageReceived(event, self)}, false);
}
});
and destroy them in onDestroyed
Template.view.onDestroyed( function() {
if (window.removeEventListener) {
console.log('view remove');
window.removeEventListener('message', function(event) {
viewMessageReceived(event, self)}, false);
} else {
window.detachEvent('onmessage', function(event){
viewMessageReceived(event, self)}, false);
}
});
And here is the function being called by the anonymous event handler:
function viewMessageReceived(event, self) {
// Handle messages from the vimeo player only
if (!(/^https?:\/\/player.vimeo.com/).test(event.origin)) {
return false;
}
if (self.playerOrigin === '*') {
self.playerOrigin = event.origin;
}
var data = JSON.parse(event.data);
switch (data.event) {
case "ready":
initializePlayer(self);
break;
case "playProgress":
self.playerTime.set(data.data.seconds);
if (self.duration === '*') self.duration = data.data.duration;
break;
case "play":
self.playerStatus.set("playing");
break;
case "pause":
self.playerStatus.set("paused");
break;
}
}
When I switch to a different template, onDestroyed
runs and my console.log('view remove')
fires, as expected.
But then when I navigate to the page that loads the other template with a video player, a Vimeo "playProgress" message
arrives that is received by the event handler in the previous video template, which was supposed to have been removed a while ago. This throws an error because the previous template has been destroyed.
Uncaught TypeError: Cannot read property 'contentWindow' of undefined
which comes from the last line in this function:
function post(template, action, value) {
console.log('view action: %s value: %s', action, value);
var data = {method: action};
if (value) data.value = value;
var message = JSON.stringify(data);
template.player[0].contentWindow.postMessage(message, template.playerOrigin);
}
Each of the video-containing templates have their own .js file, so they each have a their own post
function declaration. My understanding is that defining a function that way scopes the function just to the page.
It's only one message that arrives for the wrong player. After that, they arrive for the currently loaded player.
Why does the Vimeo message event arrive or get handled after I've already destroyed the template and when I've moved to another player?
A quote from the W3Schools website regarding the removeEventListener()
method:
// Attach an event handler to <div>
document.getElementById("myDIV").addEventListener("mousemove", myFunction);
// Remove the event handler from <div>
document.getElementById("myDIV").removeEventListener("mousemove", myFunction);
Note: To remove event handlers, the function specified with the addEventListener() method must be an external function, like in the example above (myFunction).
Anonymous functions, like "element.removeEventListener("event", function(){ myScript });" will not work.
For this to work, you'll need to move your function definition somewhere outside of the onRendered
and onDestroyed
events, and just pass the function name in the add/remove event listeners.