-This is the original question for posterity-
I'm having a bit of trouble with a new piece of code I wrote. I'm trying to find the iframe the window that sent a message lives in, assuming it's from an iframe at all. The relevant code:
var getContainingFrame = function(source){
var frames = document.getElementsByTagName('iframe');
for (var i = 0; i < frames.length; ++i) {
if (frames[i].contentWindow === source) {
return frames[i];
}
}
return false;
}
var postMessageReceivedCallback = function(event){
getContainingFrame(event.source);
}
window.addEventListener("message", postMessageReceivedCallback, false);
Works fine in Chrome/Safari, however Firefox matches the first frame every time. (iframe.contentWindow === window regardless of which window). I actually originally found how to do this from another post here, though they don't mention the problem with Firefox.
Each iframe has a different src.
Is there a different method for matching these? Have I done something obviously wrong?
No jQuery, please.
Thanks
Better Question:
My event was being passed through a function.apply(window, params) through the window object, expecting window.event available in the function it was applied to - this works in Chrome/Safari, thought not the case in FF. How do I get the event to be passed in FF?
var someFunction = function(params){
this.event.source //expect this to be the window the event originated in
//... do something, like the iframe check
}
var postMessageReceivedCallback = function(event){
//FF not keeping the event reference on the window that other browsers are,
//so we add it ourselves from the event object passed by the message event.
window.event = event;
someFunction.apply(window, event.message);
}
window.addEventListener("message", postMessageReceivedCallback, false);
Related
I'm trying to dynamically preload list of files which may be anything between images and JavaScript files. Everything is going supersmooth with Chrome and Firefox, but failing when I'm trying to preload JavaScript files with Edge. Edge still can handle images for example but no js files. And yes I've tried with addEventListener, it's not working either.
Edge doesn't give me any errors.
var object = {};
object = document.createElement('object');
object.width = object.height = 0;
object.data = path/to/javascriptfile.js
body.appendChild(object);
object.onload = function(){
console.log('hello world')
//not firing with edge
}
Anything relevant I'm missing?
UPDATE: Didn't get any success after the day. Will probably leave it for now and just skip preloading script files with edge until i find a solution.
Perhaps worth a check - from msdn:
The client loads applications, embedded objects, and images as soon as
it encounters the applet, embed, and img objects during parsing.
Consequently, the onload event for these objects occurs before the
client parses any subsequent objects. To ensure that an event handler
receives the onload event for these objects, place the script object
that defines the event handler before the object and use the onload
attribute in the object to set the handler.
https://msdn.microsoft.com/en-us/library/windows/apps/hh465984.aspx
Edit, a clarification:
You should attach the event listener before the element is added to the page.
Even doing that I'm not sure if it'll work or not though. But to make sure you've exhausted all options try the example below:
function doLoad() {
console.log('The load event is executing');
}
var object = {};
object = document.createElement('object');
object.width = object.height = 0;
object.data = 'path/to/javascriptfile.js';
object.onreadystatechange = function () {
if (object.readyState === 'loaded' || object.readyState === 'complete') doLoad();
console.log('onreadystatechange');
}
if (object.addEventListener) {
object.addEventListener( "load", doLoad, false );
console.log('addEventListener');
}
else
{
if (object.attachEvent) {
object.attachEvent( "onload", doLoad );
console.log('attachEvent');
} else if (object.onLoad) {
object.onload = doLoad;
console.log('onload');
}
}
var body = document.getElementsByTagName("body")[0];
body.appendChild(object);
If this doesn't work, you could perhaps preload using "image" instead of "object" in IE: https://stackoverflow.com/a/11103087/1996783
Working temporary solution is to put onload event directly to script element instead of object. It's sad since it works like a charm in Chrome & FF.
It turns out, object.data with css source did not load either. I don't know if it's a bug since it still can load image from to object.data.
But show must go on.
Cheers, eljuko
Essentially I'm creating a custom video control bar for an html5 video element. Everything works fine except for the seek bar for which I'm using a range input element.
Now for each element in the control bar, I'm assigning it to a variable in a js function which is called upon window load and adding event listeners. Like so:
function handleWindowLoad() {
var video = document.getElementById ("video");
var playPause = document.getElementById ("playPause");
var muteUnmute = document.getElementById ("muteUnmute");
var fullScreen = document.getElementById ("toggleFullscreen");
var scrubSlider = document.getElementById ("seekBar");
playPause.addEventListener ("click", togglePlay);
muteUnmute.addEventListener("click", toggleMute);
fullScreen.addEventListener("click", toggleFullscreen);
scrubSlider.addEventListener("change", scrubVideo);
}
All the event handlers work without a hitch.
e.g:
function togglePlay() {
if (video.paused) {
video.play();
playPause.innerHTML = "Pause";
}
else {
video.pause();
playPause.innerHTML = "Play";
}
}
works just fine.
However the scrubSlider event handler, scrubVideo():
function scrubVideo() {
var scrubTime = video.duration * (scrubSlider.value/100);
video.currentTime = scrubTime;
}
throws a "scrubSlider undefined" error.
The event listener works fine as scrubVideo() is called when expected but outside of the handleWindowLoad() function I am unable to access scrubSlider. The only ways around this error I've tried are using document.getElementById instead of the var or by declaring scrubSlider globally.
I find this interesting as the only differences between scrubSlider and the rest of the elements is that the element in question is an <input> and not a <button> and the event being listened for is a "change" instead of a "click".
Is this error a result of some sort of inherent functionality of the HTML element or something way more trivial which I've simply overlooked?
Here's a JSFiddle
If I see well, you define scrubSlider in the handleWindowLoad function (which makes it only available in that scope) and you try to access it in scrubVideo. To make it work do something like this:
var scrubSlider;
function handleWindowLoad() {
scrubSlider = document.getElementById ("seekBar");
...
}
EDIT:
Funny thing that I didn't know: apparently you reach the html elements by their id in javascript, if you subscribe to the DOMContentLoaded event. https://jsfiddle.net/22oqpcLu/1/
So, this is the reason that you don't get undefined while accessing the video variable. I guess you could access scrubSlider as seekBar.
EDIT:
I've made the changes Matthew and Yossi suggested and it still doesn't seem to work. Those changes I've edited in the post below too.
It now works!
I have a question for a particular problem I can't solve. If you know this question has been answered please send me the link as an answer. I'm trying not to use a framework in this case, but can use jQuery if necessary.
I have found answers on how to attach listeners via functions but I need something so as I wouldn't have to refactor all the code I already have. I'm a freelancer and am working on somebody else's code.
What happens is that I want to detect a touch event for a touch device. This code should work for a PC too so I need to detect clicks. There's this DIV which is created programatically to which I need to add the click or touch, depending on the device. Originally the function was called from an onmousedown event like this:
arrDivAnswers[c].onmousedown = onQuestionDown;
And this is the function it calls:
function onQuestionDown(e)
{
if(!itemSelected)
{
if(this.getAttribute('data-isCorrect') == 'true')
setStyleQCorrect(this, true);
else
setStyleQIncorrect(this);
this.querySelector('.answerText').style.color = '#ffffff';
this.querySelector('.isCorrect').style.visibility = 'visible';
}
itemSelected = true;
}
This was working fine. Now I've made this one which would try and select the correct event for a click or touch (I need a function because I have to use this more than once - and the isTouchDevice is working fine. I use that on some other apps so that code is pretty short and has been tested):
function detectEventClickOrTouch(element, functionToCall){
//detectEventClickOrTouch(arrDivAnswers[c], 'onQuestionDown');
if(isTouchDevice()){
element.addEventListener("touchend", functionToCall, false);
} else{
element.addEventListener("click", functionToCall, false);
}
}
The DIV element gets created like this on some loop:
arrDivAnswers[c] = document.createElement('div');
console.log( "Answer object #" + c + " = " + arrDivAnswers[c] );
arrDivAnswers[c].className = 'autosize';
arrDivAnswers[c].style.textAlign = 'left';
arrDivAnswers[c].setAttribute('data-isCorrect',false);
arrDivAnswers[c].setAttribute('data-isSelected',false);
divAnswerContainer.appendChild(arrDivAnswers[c]);
And then the events get attached to it like this (the older method has been commented out):
for(c;c < arrQuestions[index].arrAnswers.length;c++)
{
var curAnswer = arrQuestions[index].arrAnswers[c];
arrDivAnswers[c].onmouseover = function (e){setStyleQHover(e.currentTarget)};
arrDivAnswers[c].onmouseout = function (e){setStyleQUp(e.currentTarget)};
// Detect touch here *************************
detectEventClickOrTouch(arrDivAnswers[c], onQuestionDown);
//arrDivAnswers[c].onmousedown = onQuestionDown;
// Detect touch here *************************
arrDivAnswers[c].style.visibility = 'visible';
arrDivAnswers[c].querySelector('.answerText').innerHTML = curAnswer.strAnswer;
arrDivAnswers[c].setAttribute('data-isCorrect',curAnswer.isCorrect);
if(curAnswer.isCorrect)
{
//arrDivAnswers[c].classList.add("correctAnswer");
arrDivAnswers[c].className = "correctAnswer";
}
else
{
//arrDivAnswers[c].classList.remove("correctAnswer");
arrDivAnswers[c].className = "autosize";
}
arrDivAnswers[c].setAttribute('data-isSelected',false);
setStyleQUp(arrDivAnswers[c]);
itemSelected = false;
}
[...]
The debugger is throwing this error:
Uncaught TypeError: Object [object DOMWindow] has no method 'getAttribute'
I'm sure I'm messing up the "this" because I'm not calling the function properly.
I agree the "this" variable is getting messed up. The problem is that you are attaching an anonymous function as the callback that then calls eval on another method. This seems unnecessary.
Could you just do this:
function detectEventClickOrTouch(element, functionToCall){
//detectEventClickOrTouch(arrDivAnswers[c], 'onQuestionDown');
if(isTouchDevice()){
element.addEventListener("touchend", functionToCall, false);
} else{
element.addEventListener("click", functionToCall, false);
}
}
And then when you attach the event just do:
detectEventClickOrTouch(arrDivAnswers[c], onQuestionDown);
Since you now call the onQuestionDown function indirectly by the eval the this context seen by the onQuestionDown is the global namespace and not the the element which fired the event.
You don't need the eval anyway... you can pass the function it self
detectEventClickOrTouch(arrDivAnswers[c], onQuestionDown);
and:
element.addEventListener("touchend", functionToCall, false);
I am facing a weird behavior and I need some help..
I encounter a situation when I try to recognize whether the content of the page was modified. I do it using
gBrowser.tabContainer.addEventListener("DOMSubtreeModified", function (e) { this.foo(e); }, false);
I also tried listening to document.DOMSubtreeModified and window.DOMSubtreeModified.
However, I sometimes get a situation in which the default\selected document is something that is irrelevant to me - perhaps some IFrame or a commercial built in or whatever, and bottom line my content is modified but when staring at the browser DOMSubtreeModified doesn't fire since it listens to a document\whatever that was indeed not modified...
Can you please help my understnad where's my problem? I need to create some event that recognizes any content modification (something like DOMSubtreeModified) that fires for every document, so that I could identify my relevant content and process it?
Thanks a lot,
Nili
You could explicitly listen for all DOM modification by adding a listener on the document object for each <iframe> element within the element you're interested in:
function listenForDomModified(node, listener) {
node.addEventListener("DOMSubtreeModified", listener, false);
var iframes = node.getElementsByTagName("iframe");
for (var i = 0, len = iframes.length, doc; i < len; ++i) {
// Catch and ignore errors caused by iframes from other domains
try {
doc = iframes[i].contentDocument || iframes[i].contentWindow.document;
doc.addEventListener("DOMSubtreeModified", listener, false);
} catch (ex) {}
}
}
listenForDomModified(gBrowser.tabContainer);
Note that the DOMSubtreeModified event doesn't fire at all in Opera, so your code won't work in that browser.
for FF 2, Safari, Opera 9.6+
doc.addEventListener('DOMNodeInserted', callback, false);
doc.addEventListener('DOMNodeRemoved', callback, false);
Using the method .attachEvent() in IE, how can I reference the caller object (the element that triggered the event) with this? In normal browsers, using .addEventListener, the var this points to the element, while in IE it points to the window object.
I need it to work with the following code:
var element = //the element, doesn't matter how it is obtained
element.addAnEvent = function(name, funct){
if(element.addEventListener) // Works in NORMAL browsers...
else if(element.attachEvent){
element.attachEvent("on"+name, funct);
//where the value of "this" in funct should point to "element"
}
}
I just made that code up, it's not exactly the same as my code, but if it works with it then it works with me!
From this quirksmode article with regard to attachEvent:
Events always bubble, no capturing possibility.
The event handling function is referenced, not copied, so the this keyword always refers to the window and is completely useless.
The result of these two weaknesses is that when an event bubbles up it is impossible to know which HTML element currently handles the event. I explain this problem more fully on the Event order page.
Since the Microsoft event adding model is only supported by Explorer 5 and higher on Windows, it cannot be used for cross–browser scripts. But even for Explorer–on–Windows only applications it’s best not to use it, since the bubbling problem can be quite nasty in complex applications.
I haven't tested it, but a possible workaround may be to wrap the handler in an anonymous function that calls your handler via funct.call().
else if(element.attachEvent){
element.attachEvent("on"+name, function(){ funct.call( element ) });
}
My apologies for the untested solution. I don't like doing that, but can't easily get to IE right now.
If you're in IE, you can get the "caller" object, as you call it, by accessing window.event.srcElement within the event handler function. This object is normally referred to as the event target or source.
var funct = function(event) {
if ( !event ) {
event = window.event;
}
var callerElement = event.target || event.srcElement;
// Do stuff
};
This code is untested, but should set you off in the right direction.
Bind it. Well, you can't use Function.prototype.bind that gets added in javascript 1.8.5, but you can use closure and Function.prototype.apply.
var element = //the element, doesn't matter how it is obtained
element.addAnEvent = function(name, funct){
if(element.addEventListener) // Works in NORMAL browsers...
//...
else if(element.attachEvent){
element.attachEvent("on"+name, function() {
//call funct with 'this' == 'element'
return funct.apply(element, arguments);
});
}
}
I think it would be better to extend the Element object , through the prototype chain, instead of adding your method to each element you want to add events to (works with all browsers)..
Element.prototype.addAnEvent = function(name, funct){
if(this.addEventListener) // Works in NORMAL browsers...
{
this.addEventListener(name,funct, false);
}
else if(this.attachEvent){
var _this = this;
this.attachEvent("on"+name, function(){funct.apply(_this);});
//where the value of "this" in funct should point to "element"
}
};
This way it will work for all your current and future elements (and you only have to run it once).