Execute a javascript function on the second load of an iframe - javascript

Is there a way to execute a javascript function on the second load of an iframe? Right now I am nesting two addEventListeners:
document.getElementById('my_iframe').addEventListener("load", function() {
document.getElementById('my_iframe').addEventListener("load", doSomething(), true);
}, true);
The first "load" is triggered when I use document.my_iframe.write('...'). I want to trigger an action after I submit a form in the iframe, which is the second "load".
Is there a better way to accomplish my goal?

The event will fire each time the iframe is loaded no need for nesting, just for a counter.
var count;
element.addEventListener("load", function() {
count++;
if(count==2){
//code goes here
}
}, true);

Related

jQuery callback function accumulating

I wrote some code for a custom confirm box that calls a function when confirm button (yes-button) is pressed and passes another function as a parameter and I bind it to 2 different button clicks with different functions as a parameter. For example:
$('#button1').click(function() {
callFunction(function() { alert("test"); });
});
$('#button2').click(function() {
callFunction(function() { alert("test2"); });
});
function callFunction(callback) {
//code to display custom confirm box
console.log(callback);
$('.confirm-box .yes-button').click(function() {
callback();
});
}
Everything happens as expected, confirm box appears and on confirm button click I get a callback function executed and it alerts "test" (or "test2" depending on which button called the confirm box). The problem arises when I click button1 that sends a function that alerts "test", then instead of confirming I cancel that (nothing happens as expected), and then click button2 that passes alert("test2") as a callback function. Now once I press the yes-button instead of alerting just "test2", I get both "test2" and "test" alerts even though that console.log I wrote logs just the function that alerts "test2" at the time of that button2 click. It seems like these callback functions get stacked somewhere, but I don't understand where and why.
The .click() function can add more than one handler to an element, and I think that's what's happening here. Try this:
// ...
$('.confirm-box .yes-button').unbind('click').click(function() {
callback();
});
This removes any previous click handler before applying the new one.
When you execute the code:
$('.confirm-box .yes-button').click(function() {
callback();
});
you are binding an event handler to the .yes-button element. If you run that code twice, it will have two events bound to it. And so on.
One solution is to use .one instead, so that the event handler will be removed after the first time it is fired:
$('.confirm-box .yes-button').one("click", function() {
callback();
});
This of course has issues if there are two confirm boxes open simultaneously or if there are two .yes-button elements, but for a simple use case it works fine.
What is happening is that each time a button is clicked the callFunction method is executing. It runs through that code block and applies an event listener to the $('.confirm-box .yes-button') button. So clicking your button N times will apply the click listener N times as well. One solution is to store the function in a variable.
Not sure what the end goal is, but this is one solution.
Another solution would be to remove buttons event listeners each time.
var functionToCallOnYes = function() {};
$('#button1').click(function() {
functionToCallOnYes = function() {
alert("test");
};
});
$('#button2').click(function() {
functionToCallOnYes = function() {
alert("test2");
};
});
$('.confirm-box .yes-button').click(function() {
console.log(functionToCallOnYes);
functionToCallOnYes();
});
You can do it by setting an identity by classes,
var button = $('.confirm-box .yes-button');
$('#button1').click(function() {
button.removeClass("b").addClass("a");
});
$('#button2').click(function() {
button.removeClass("a").addClass("b");
});
button.click(function() {
if($(this).hasClass("a")){
callBackForButton1();
} else {
callBackForButton2();
}
});
It is a bad practice to stack up the event handler for a single element.
Yes, extra callbacks are getting stacked up. In $('button1').click(f), the function f will be called with no parameters every time button1 is clicked. In this case, f is callFunction-- a function that itself attaches a new handler to any .confirm-box .yes-button element each time it's invoked. So on the Nth click, you should have N-1 alerts.
To make things like this easier, you can refer to functions by name in JavaScript. So if you had function test() { console.log("test"); };, you could write $(".confirm-box").click(test) just once and every click on a .confirm-box from then on would print test to the console.
Usually if you have callbacks whose sole purpose is to call a callback, you can just remove that callback.

Ajax Load Event Incrementing By One

I have a simple textarea where users can input text which is then passed through via AJAX to a URL once they hit the return key. My issue is that on the first press of the return key the text data is sent once, on the second it's sent twice, and so on incrementing my one each time.
After some reading up I realise that if I was using a form submit I'd have to unbind it to prevent this happening. I've tried adding a value flag to prevent the multiple events, but have only got so far as to get it to trigger once only.
My code is as follows. Any guidance on how to prevent the incrementing events would be appreciated - as you can probably tell my confidence/knowledge in Javascript isn't the best. Thank you!
$(function() {
$("#myTextarea").keypress(function(e) {
// If the user hits the return key
if(e.which == 13) {
e.preventDefault();
$.ajax({
success: function(){
var modal = $('#myModal'), modalBody = $('#myModal .modal-body');
modal
// Load the webpage result within the modal Body
.on('show.bs.modal', function () {
modalBody.load('http://www.things.co.uk/things' + document.getElementById('myTextArea').value)
})
.modal();
// Hide the modal after five seconds
myModalTimeout = setTimeout(function() {
$('#myModal').modal('hide');
}, 5000);
}
});
}
});
});
Edit: I solved this by using one() for my modal event via http://www.andismith.com/blog/2011/11/on-and-off/. Thank you everyone.
You must attach the event handler only once. I suppose you're getting the JS in your AJAX response, and executing it again and again on each AJAX load. Removing and re-attaching the handlers is a hacky solution.
To avoid to attach the event handlers more than once, simply put your script in a part of the page which is not reloaded by AJAX, so the event is attached only once.
You can even attach an event handler to an element that is reloaded by ajax using delegated events: Understanding Event Delegation
With this technique, you attach the event handler to a container parent element which is not reloaded by ajax, and handle the events of the reloaded children specified by a filter.
$( "#container" ).on("<event>", "<children filter>", function( event ) {
// code to handle the event
});
Note that in this sample #container is the element which isn't reloaded by ajax. And <children filter> is a jquery selector that chooses the children whose event mus be handled. (<event> is obviously the event name, like click or keyPress).
Explanation: when the event is trigger in the child element, it pops up to the container. The container catches it, and checks that the children passes the filter. If so, the vent is handled.
If there are no more event handlers on myTextarea div code below should suffice.
If there are multiple event handlers attached to keypress event you will have to use named function and remove it using $.unbind() more on how to do this.
$(function() {
$("#myTextarea").off();
$("#myTextarea").keypress(function(e) {
// If the user hits the return key
if(e.which == 13) {
e.preventDefault();
$.ajax({
success: function(){
var modal = $('#myModal'), modalBody = $('#myModal .modal-body');
modal
// Load the webpage result within the modal Body
.on('show.bs.modal', function () {
modalBody.load('http://www.things.co.uk/things' + document.getElementById('myTextArea').value)
})
.modal();
// Hide the modal after five seconds
myModalTimeout = setTimeout(function() {
$('#myModal').modal('hide');
}, 5000);
}
});
}
});
});

How to make an <a> tag element to trigger multiple functions with javascript/jquery

There is a link in my webpage, the link itself triggers a function that I could not modify, but I want to make the link, when clicked, also calls another JavaScript function at the same time or preferably after the first function is done. So one click to call two functions...could it be implemented? Thanks
<a title="Next Page" href="javascript:__doPostBack('Booklet1','V4504')">Next</a>
is the sample tag I want to modify, how could make it also call "myFunc" at the same time or preferably after _doPostBack is done.
P.S. the function parameter for _doPostBack such as V4504 is dynamically generated by the ASP user control. So I cannot simply treat it as a static function and bind it with another. I think I could only append some function to it? Unless I parse the whole page first and extract the function name with its current parameters...Since every time I click the link, the parameter such as V4504 changes its value....
Thanks!
You should be able to attach multiple event handlers to a single anchor tag, either with .onclick or .addEventListener('click', function)
https://developer.mozilla.org/en/DOM/element.addEventListener
You can attach a handler to an element click event using plain Javascript in such a way:
function hello()
{
alert("Hello!")
}
var element = document.getElementById("YourAElementID");
if (element.addEventListener)
{
element.addEventListener("click", hello, false);
}
else
{
element.attachEvent("onclick", hello);
}
It supprots all common browsers.
Yes, you can do this MANY ways (I use both $(this) and $('identifier') as you don't say how the functions are bound) :
$(this).click(function(){
my_function_1();
my_function2()
});
Or
$('my element').click(function(){
my_function_1();
});
$('my element').click(function(){
my_function_2();
});
Or, if the functions reside on another object:
$(this).click(function(){
my_function_1();
$('#other_element_id').trigger('click'); //there are a bunch of syntaxes for this
});
Sans JQuery, you can use:
var myObj = document.getElementById('element name');
myObj.addEventListener('click', function(){
alert('first!');
});
myObj.addEventListener('click', function(){
alert('second!');
});
Clicking will result in two sequential alert prompts

How can I get Iframe event before load?

In my site, I use an iframeA in an iframeB, and, when the iframeA changes it's content I have to set the src. I can set it only with the onload event, but this called when the site is loaded. I am looking for some event or trigger, that helps me detect the location/src change before it starts loading. I don't want to wait the whole page load, before the src set. I have no direct access to iframeA (just the script below)
Some code:
var myframe = document.getElementById('frameB').contentWindow.document.getElementById('frameA');
myframe.onload=function (funcname) {...};
Check this gist or my answer to this question. The code there does exactly that:
function iframeURLChange(iframe, callback) {
var unloadHandler = function () {
// Timeout needed because the URL changes immediately after
// the `unload` event is dispatched.
setTimeout(function () {
callback(iframe.contentWindow.location.href);
}, 0);
};
function attachUnload() {
// Remove the unloadHandler in case it was already attached.
// Otherwise, the change will be dispatched twice.
iframe.contentWindow.removeEventListener("unload", unloadHandler);
iframe.contentWindow.addEventListener("unload", unloadHandler);
}
iframe.addEventListener("load", attachUnload);
attachUnload();
}
It utilizes the unload event. Whenever a page is unloaded, a new one is expected to start loading. If you listen for that event, though, you will get the current URL, not the new one. By adding a timeout with 0 milliseconds delay, and then checking the URL, you get the new iframe URL.
However, that unload listener is removed each time a new page is loaded, so it must be re-added again on each load.
The function takes care of all that, though. To use it, you only have to do:
iframeURLChange(document.getElementById("myframe"), function (url) {
console.log("URL changed:", url);
});
What will be changing the source of the iframe? If you have access to that code then you can do whatever is in your onload function then.
If a link has it's target attribute set to the iframe and that is how the source is changing then you can hi-jack the link clicks:
$('a[target="frameB"]').bind('click', function () {
//run your onload code here, it will run as the iframe is downloading the new content
});
Also, just a side-note, you can bind an event handler for the load event in jQuery like this:
$('#frameB').bind('load', function () {
//run onload code here
});
UPDATE
SITE -> frameB -> frameA
$("#frameB").contents().find("#frameA").bind('load', function () {
//load code here
});
This selects the #frameB element (that is in the current top level DOM), gets it's contents, finds the #frameA element, and then binds an event handler for the load event.
Note that this code must be run after #frameB is loaded with the #frameA element already present in it's DOM. Something like this might be a good idea:
$('#frameB').bind('load', function () {
$(this).contents().find('#frameA').bind('load', function () {
//run load code here
});
});
UPDATE
To hi-jack links in the #frameB element:
$('#frameB').contents().find('a[target="frameA"]').bind('click', function () {
/*run your code here*/
});
This will find any link in the #frameB element that has its target attribute set to frameA and add a click event handler.
And again, this will only work if the #frameB iframe element has loaded (or atleast gotten to the document.ready event) so you can select it's elements.
You could also try taking the approach of detecting when your iframe is going to leave its current location. This may be useful in some situations. To do this, put the following code in you iFarme source.
$(window).on('beforeunload', function () {
alert('before load ...');
});
I think adding inline onload attribute with appropriate event handler to iframe tag will solve your problem.
function onIframeLoad(){
//Write your code here
}
Markup change
<iframe src='..' onload='onIframeLoad()' />

How to prevent repeat in jQuery function?

I have a simple jQuery function as
$('.button').click(function(){
$("#target").slideToggle().load('http://page');
});
By slideToggle behavior, every click cause a slide, but the problem is that it will load url again too.
How can I limit the load() function to be performed only once, but slideToggle() on every click. In other words, how to prevent load() (only load, not the entire function) in the subsequent clicks?
$('.button')
.on('click.loadPage', function() {
$("#target").load('http://page');
$(this).off("click.loadPage");
})
.on('click.slideToggle', function(){
$("#target").slideToggle();
});
and another way without global vars:
$('.button')
.on('click', function() {
if ( !$(this).data("loaded") ) {
$("#target").load('http://page');
$(this).data("loaded", true);
}
$("#target").slideToggle();
});
Have a variable (global) which says whether it has been loaded or not. E.g:
var loaded = false;
$('.button').click(function(){
if(!loaded){
$('#target').load('http://page');
loaded = true;
}
$("#target").slideToggle();
});
This will cause the slideToggle to occur on every click, but the page to load only the once. :)

Categories