anonymous window.setTimeout function reloading page - javascript

Using Tampermonkey to change the behavior of a website. Have some problems with a website with the following code:
<script language="JavaScript">
if (typeof jQuery != 'undefined') {
jQuery(window).load(function() {
window.setTimeout(function() {
window.location.replace(window.location.href);
}, 180E3);
});
}
</script>
How does one remove/prevent it reloading the page?

Without messing that much with jQuery, if your script runs before that piece of code you can do the following:
jQuery.fn.load = function() {
console.log("Tried to reload the page!")
}
// Then the following, outputs 'Tried to reload the page!' and does nothing else
jQuery(window).load(function() {
// code
})
If you still need the load function afterwards you could do the following:
var oldLoad = jQuery.fn.load
var undesiredCallback = "function() {
window.setTimeout(function() {
window.location.replace(window.location.href);
}, 180E3);
}"
jQuery.fn.load = function(callback) {
if(callback.toString() !== undesiredCallback) {
oldLoad(callback);
}
}
But it's browser dependent and very unreliable
Another way would be adding a onbeforeunload event, but that would pop a prompt on your tab:
window.onbeforeunload = function() {
return "Stay here please";
}

You can also override setTimeout functionallity.
var oldSetTimeout = window.setTimeout;
window.setTimeout = function(func,interval){
if(typeof func === "function" && func.toString().indexOf('window.location.replace(window.location.href)') > -1){
/*do nothing*/
}else{
oldSetTimeout(func,interval)
}
}

Related

Executing a jquery function or bypass jquery countdown

There is a countdown in a page. And i want to bypass or speed it up.
Is there a possibility to run add ticket function from chrome's console or something?Or can I change the xtime variable to something like 2-3 seconds.Or directly call the add ticket function?
jQuery(document).ready(function($){
var xTime=120;
$("#ticketTimer").html(xTime);
var myVar = setInterval(
function(){
if(typeof checkAdBlock === 'undefined')
{
document.getElementById("compt").innerHTML = "<b>you use an AdBlocker!</b> To win a ticket, <b>Desactivate AdBlock and reload the page.</b>";
return false;
}
else if(xTime>0)
{
xTime--;
$("#ticketTimer").html(xTime);
}
else
{
addTicket();
clearInterval(myVar);
}
},
1000);
});

On close tab ask for unsaved changes and then reload the page - How?

I've come across a nasty website that managed to refresh the page every time I tried to close the tab. Basically you couldn't close that page. I got around it by disabling javascript on that page.
How do they do it?
I've tried location.replace inside onbeforeunload and it doesn't work:
window.onbeforeunload = function(e) {
window.location.replace(window.location.href);
};
Here is the source of the page in question, where I don't get how they do it:
<script type="text/javascript">
function c(b)
{
document.write(b);
}
function wrapped3(d)
{
return d.replace(/(.)(.)/g, '%$1$2');
}
function wrapped(b)
{
var tmp = window['decodeURI' + 'Component'];
return tmp(wrapped3(b))
}
function show_page(a)
{
c(wrapped(a)
.split('{{addr}}').join('//ip')
.split('{{country}}').join('Great Britain')
.split('{{region}}').join('London, City of')
.split('{{city}}').join('London')
.split('{{ltude}}').join('//latitude')
.split('{{referrer}}').join('//url here')
);
}
show_page('//long string here');
</script>
window.onbeforeunload is the correct solution, here is an example:
var popit = true;
window.onbeforeunload = function() {
if(popit == true) {
popit = false;
return "Are you sure you want to leave?";
}
}
http://jsfiddle.net/SQAmG/3/

javascript html5 history, variable initialization and popState

main question
Is there a javascript way to identify if we are accessing a page for the first time or it is a cause of a back?
My problem
I'm implementing html5 navigation in my ajax driven webpage.
On the main script, I initialize a variable with some values.
<script>
var awnsers=[];
process(awnsers);
<script>
Process(awnsers) will update the view according to the given awnsers, using ajax.
In the funciton that calls ajax, and replaces the view, I store the history
history.pushState(state, "", "");
I defined the popstate also, where I restore the view according to the back. Moreover, I modify the global variable awnsers for the old value.
function popState(event) {
if (event.state) {
state = event.state;
awnsers=state.awnsers;
updateView(state.view);
}
}
Navigation (back and forth) goes corectly except when I go to an external page, and press back (arrving to my page again).
As we are accessing the page, first, the main script is called,the valiable awnsers is updated, and the ajax starts. Meanwile, the pop state event is called, and updates the view. After that the main ajax ends, and updates the view according to empty values.
So I need the code:
<script>
var awnsers=[];
process(awnsers);
<script>
only be called when the user enters the page but NOT when it is a back. Any way to do this?
THanks!
Possible solution
After the first awnser I have thought of a possible solution. Tested and works, whoever, I don't know if there is any cleaner solution. I add the changes that I've done.
First I add:
$(function() {
justLoaded=true;
});
then I modify the popState function, so that is in charge to initialize the variables
function popState(event) {
if (event.state) {
state = event.state;
awnsers=state.awnsers;
updateView(state.view);
} else if(justLoaded){
awnsers=[];
process(awnsers);
}
justLoaded=false;
}
Thats all.
what about using a global variable?
var hasLoaded = false;
// this function can be called by dom ready or window load
function onPageLoad() {
hasLoaded = true;
}
// this function is called when you user presses browser back button and they are still on your page
function onBack() {
if (hasLoaded) {
// came by back button and page was loaded
}
else {
// page wasn't loaded. this is first visit of the page
}
}
Use cookie to store the current state.
yeah! This is what I have:
var popped = (($.browser.msie && parseInt($.browser.version, 10) < 9) ? 'state' in window.history : window.history.hasOwnProperty('state')), initialURL = location.href;
$(window).on('popstate', function (event) {
var initialPop = !popped && location.href === initialURL, state;
popped = true;
if (initialPop) { return; }
state = event.originalEvent.state;
if (state && state.reset) {
if (history.state === state) {
$.ajax({url: state.loc,
success: function (response) {
$(".fragment").fadeOut(100, function () {
$(".fragment").html($(".fragment", response).html()).fadeIn(100);
);
document.title = response.match(/<title>(.*)<\/title>/)[1];
}
});
} else { history.go(0); }
else {window.location = window.location.href; }
});
And:
$.ajax({url:link,
success: function (response) {
var replace = args.replace.split(",");
$.each(replace, function (i) {
replace[i] += ($(replace[i]).find("#video-content").length > 0) ? " #video-content" : "";
var selector = ".fragment "+replace[i];
$(selector).fadeOut(100, function () {
$(selector).html($(selector,response).html()).fadeIn(100, function () {
if (base.children("span[data-video]")[0]) {
if ($.browser.msie && parseInt($.browser.version, 10) === 7) {
$("#theVideo").html("");
_.videoPlayer();
} else {
_.player.cueVideoById(base.children("span[data-video]").attr("data-video"));
}
}
});
});
});
document.title = response.match(/<title>(.*)<\/title>/)[1];
window.history.ready = true;
if (history && history.pushState) { history.pushState({reset:true, loc:link}, null, link); }
}
});

Detect Click into Iframe using JavaScript

I understand that it is not possible to tell what the user is doing inside an iframe if it is cross domain. What I would like to do is track if the user clicked at all in the iframe. I imagine a scenario where there is an invisible div on top of the iframe and the the div will just then pass the click event to the iframe.
Is something like this possible? If it is, then how would I go about it? The iframes are ads, so I have no control over the tags that are used.
This is certainly possible. This works in Chrome, Firefox, and IE 11 (and probably others).
const message = document.getElementById("message");
// main document must be focused in order for window blur to fire when the iframe is interacted with.
// There's still an issue that if user interacts outside of the page and then click iframe first without clicking page, the following logic won't run. But since the OP is only concerned about first click this shouldn't be a problem.
window.focus()
window.addEventListener("blur", () => {
setTimeout(() => {
if (document.activeElement.tagName === "IFRAME") {
message.textContent = "clicked " + Date.now();
console.log("clicked");
}
});
}, { once: true });
<div id="message"></div>
<iframe width="50%" height="300" src="//example.com"></iframe>
Caveat: This only detects the first click. As I understand, that is all you want.
This is small solution that works in all browsers even IE8:
var monitor = setInterval(function(){
var elem = document.activeElement;
if(elem && elem.tagName == 'IFRAME'){
clearInterval(monitor);
alert('clicked!');
}
}, 100);
You can test it here: http://jsfiddle.net/oqjgzsm0/
Based on Mohammed Radwan's answer I came up with the following jQuery solution. Basically what it does is keep track of what iFrame people are hovering. Then if the window blurs that most likely means the user clicked the iframe banner.
the iframe should be put in a div with an id, to make sure you know which iframe the user clicked on:
<div class='banner' bannerid='yyy'>
<iframe src='http://somedomain.com/whatever.html'></iframe>
<div>
so:
$(document).ready( function() {
var overiFrame = -1;
$('iframe').hover( function() {
overiFrame = $(this).closest('.banner').attr('bannerid');
}, function() {
overiFrame = -1
});
...
this keeps overiFrame at -1 when no iFrames are hovered, or the 'bannerid' set in the wrapping div when an iframe is hovered. All you have to do is check if 'overiFrame' is set when the window blurs, like so:
...
$(window).blur( function() {
if( overiFrame != -1 )
$.post('log.php', {id:overiFrame}); /* example, do your stats here */
});
});
Very elegant solution with a minor downside: if a user presses ALT-F4 when hovering the mouse over an iFrame it will log it as a click. This only happened in FireFox though, IE, Chrome and Safari didn't register it.
Thanks again Mohammed, very useful solution!
Is something like this possible?
No. All you can do is detect the mouse going into the iframe, and potentially (though not reliably) when it comes back out (ie. trying to work out the difference between the pointer passing over the ad on its way somewhere else versus lingering on the ad).
I imagine a scenario where there is an invisible div on top of the iframe and the the div will just then pass the click event to the iframe.
Nope, there is no way to fake a click event.
By catching the mousedown you'd prevent the original click from getting to the iframe. If you could determine when the mouse button was about to be pressed you could try to get the invisible div out of the way so that the click would go through... but there is also no event that fires just before a mousedown.
You could try to guess, for example by looking to see if the pointer has come to rest, guessing a click might be about to come. But it's totally unreliable, and if you fail you've just lost yourself a click-through.
The following code will show you if the user click/hover or move out of the iframe:-
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Detect IFrame Clicks</title>
<script type="text/javascript">
$(document).ready(function() {
var isOverIFrame = false;
function processMouseOut() {
log("IFrame mouse >> OUT << detected.");
isOverIFrame = false;
top.focus();
}
function processMouseOver() {
log("IFrame mouse >> OVER << detected.");
isOverIFrame = true;
}
function processIFrameClick() {
if(isOverIFrame) {
// replace with your function
log("IFrame >> CLICK << detected. ");
}
}
function log(message) {
var console = document.getElementById("console");
var text = console.value;
text = text + message + "\n";
console.value = text;
}
function attachOnloadEvent(func, obj) {
if(typeof window.addEventListener != 'undefined') {
window.addEventListener('load', func, false);
} else if (typeof document.addEventListener != 'undefined') {
document.addEventListener('load', func, false);
} else if (typeof window.attachEvent != 'undefined') {
window.attachEvent('onload', func);
} else {
if (typeof window.onload == 'function') {
var oldonload = onload;
window.onload = function() {
oldonload();
func();
};
} else {
window.onload = func;
}
}
}
function init() {
var element = document.getElementsByTagName("iframe");
for (var i=0; i<element.length; i++) {
element[i].onmouseover = processMouseOver;
element[i].onmouseout = processMouseOut;
}
if (typeof window.attachEvent != 'undefined') {
top.attachEvent('onblur', processIFrameClick);
}
else if (typeof window.addEventListener != 'undefined') {
top.addEventListener('blur', processIFrameClick, false);
}
}
attachOnloadEvent(init);
});
</script>
</head>
<body>
<iframe src="www.google.com" width="100%" height="1300px"></iframe>
<br></br>
<br></br>
<form name="form" id="form" action=""><textarea name="console"
id="console" style="width: 100%; height: 300px;" cols="" rows=""></textarea>
<button name="clear" id="clear" type="reset">Clear</button>
</form>
</body>
</html>
You need to replace the src in the iframe with your own link. Hope this'll help.
Regards,
Mo.
Just found this solution...
I tried it, I loved it..
Works for cross domain iframes for desktop and mobile!
Don't know if it is foolproof yet
window.focus();
window.addEventListener('blur',function(){
if(document.activeElement.id == 'CrossDomainiframeId'){
//do something :-)
}
});
Happy coding
You can achieve this by using the blur event on window element.
Here is a jQuery plugin for tracking click on iframes (it will fire a custom callback function when an iframe is clicked) :
https://github.com/finalclap/iframeTracker-jquery
Use it like this :
jQuery(document).ready(function($){
$('.iframe_wrap iframe').iframeTracker({
blurCallback: function(){
// Do something when iframe is clicked (like firing an XHR request)
}
});
});
see http://jsfiddle.net/Lcy797h2/ for my long winded solution that doesn't work reliably in IE
$(window).on('blur',function(e) {
if($(this).data('mouseIn') != 'yes')return;
$('iframe').filter(function(){
return $(this).data('mouseIn') == 'yes';
}).trigger('iframeclick');
});
$(window).mouseenter(function(){
$(this).data('mouseIn', 'yes');
}).mouseleave(function(){
$(this).data('mouseIn', 'no');
});
$('iframe').mouseenter(function(){
$(this).data('mouseIn', 'yes');
$(window).data('mouseIn', 'yes');
}).mouseleave(function(){
$(this).data('mouseIn', null);
});
$('iframe').on('iframeclick', function(){
console.log('Clicked inside iframe');
$('#result').text('Clicked inside iframe');
});
$(window).on('click', function(){
console.log('Clicked inside window');
$('#result').text('Clicked inside window');
}).blur(function(){
console.log('window blur');
});
$('<input type="text" style="position:absolute;opacity:0;height:0px;width:0px;"/>').appendTo(document.body).blur(function(){
$(window).trigger('blur');
}).focus();
http://jsfiddle.net/QcAee/406/
Just make a invisible layer over the iframe that go back when click and go up when mouseleave event will be fired !!
Need jQuery
this solution don't propagate first click inside iframe!
$("#invisible_layer").on("click",function(){
alert("click");
$("#invisible_layer").css("z-index",-11);
});
$("iframe").on("mouseleave",function(){
$("#invisible_layer").css("z-index",11);
});
iframe {
width: 500px;
height: 300px;
}
#invisible_layer{
position: absolute;
background-color:trasparent;
width: 500px;
height:300px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="message"></div>
<div id="invisible_layer">
</div>
<iframe id="iframe" src="//example.com"></iframe>
This works for me on all browsers (included Firefox)
https://gist.github.com/jaydson/1780598
https://jsfiddle.net/sidanmor/v6m9exsw/
var myConfObj = {
iframeMouseOver : false
}
window.addEventListener('blur',function(){
if(myConfObj.iframeMouseOver){
console.log('Wow! Iframe Click!');
}
});
document.getElementById('idanmorblog').addEventListener('mouseover',function(){
myConfObj.iframeMouseOver = true;
});
document.getElementById('idanmorblog').addEventListener('mouseout',function(){
myConfObj.iframeMouseOver = false;
});
<iframe id="idanmorblog" src="https://sidanmor.com/" style="width:400px;height:600px" ></iframe>
<iframe id="idanmorblog" src="https://sidanmor.com/" style="width:400px;height:600px" ></iframe>
Mohammed Radwan,
Your solution is elegant. To detect iframe clicks in Firefox and IE, you can use a simple method with document.activeElement and a timer, however... I have searched all over the interwebs for a method to detect clicks on an iframe in Chrome and Safari. At the brink of giving up, I find your answer. Thank you, sir!
Some tips:
I have found your solution to be more reliable when calling the init() function directly, rather than through attachOnloadEvent(). Of course to do that, you must call init() only after the iframe html. So it would look something like:
<script>
var isOverIFrame = false;
function processMouseOut() {
isOverIFrame = false;
top.focus();
}
function processMouseOver() { isOverIFrame = true; }
function processIFrameClick() {
if(isOverIFrame) {
//was clicked
}
}
function init() {
var element = document.getElementsByTagName("iframe");
for (var i=0; i<element.length; i++) {
element[i].onmouseover = processMouseOver;
element[i].onmouseout = processMouseOut;
}
if (typeof window.attachEvent != 'undefined') {
top.attachEvent('onblur', processIFrameClick);
}
else if (typeof window.addEventListener != 'undefined') {
top.addEventListener('blur', processIFrameClick, false);
}
}
</script>
<iframe src="http://google.com"></iframe>
<script>init();</script>
You can do this to bubble events to parent document:
$('iframe').load(function() {
var eventlist = 'click dblclick \
blur focus focusin focusout \
keydown keypress keyup \
mousedown mouseenter mouseleave mousemove mouseover mouseout mouseup mousemove \
touchstart touchend touchcancel touchleave touchmove';
var iframe = $('iframe').contents().find('html');
// Bubble events to parent
iframe.on(eventlist, function(event) {
$('html').trigger(event);
});
});
Just extend the eventlist for more events.
I ran into a situation where I had to track clicks on a social media button pulled in through an iframe. A new window would be opened when the button was clicked. Here was my solution:
var iframeClick = function () {
var isOverIframe = false,
windowLostBlur = function () {
if (isOverIframe === true) {
// DO STUFF
isOverIframe = false;
}
};
jQuery(window).focus();
jQuery('#iframe').mouseenter(function(){
isOverIframe = true;
console.log(isOverIframe);
});
jQuery('#iframe').mouseleave(function(){
isOverIframe = false;
console.log(isOverIframe);
});
jQuery(window).blur(function () {
windowLostBlur();
});
};
iframeClick();
Combining above answer with ability to click again and again without clicking outside iframe.
var eventListener = window.addEventListener('blur', function() {
if (document.activeElement === document.getElementById('contentIFrame')) {
toFunction(); //function you want to call on click
setTimeout(function(){ window.focus(); }, 0);
}
window.removeEventListener('blur', eventListener );
});
This definitely works if the iframe is from the same domain as your parent site. I have not tested it for cross-domain sites.
$(window.frames['YouriFrameId']).click(function(event){ /* do something here */ });
$(window.frames['YouriFrameId']).mousedown(function(event){ /* do something here */ });
$(window.frames['YouriFrameId']).mouseup(function(event){ /* do something here */ });
Without jQuery you could try something like this, but again I have not tried this.
window.frames['YouriFrameId'].onmousedown = function() { do something here }
You can even filter your results:
$(window.frames['YouriFrameId']).mousedown(function(event){
var eventId = $(event.target).attr('id');
if (eventId == 'the-id-you-want') {
// do something
}
});
We can catch all the clicks. The idea is to reset focus on an element outside the iFrame after each click:
<input type="text" style="position:fixed;top:-1000px;left:-1000px">
<div id="message"></div>
<iframe id="iframe" src="//example.com"></iframe>
<script>
focus();
addEventListener('blur', function() {
if(document.activeElement = document.getElementById('iframe')) {
message.innerHTML += 'Clicked';
setTimeout(function () {
document.querySelector("input").focus();
message.innerHTML += ' - Reset focus,';
}, 1000);
}
});
</script>
JSFiddle
Assumptions -
Your script runs outside the iframe BUT NOT in the outermost window.top window. (For outermost window, other blur solutions are good enough)
A new page is opened replacing the current page / a new page in a new tab and control is switched to new tab.
This works for both sourceful and sourceless iframes
var ifr = document.getElementById("my-iframe");
var isMouseIn;
ifr.addEventListener('mouseenter', () => {
isMouseIn = true;
});
ifr.addEventListener('mouseleave', () => {
isMouseIn = false;
});
window.document.addEventListener("visibilitychange", () => {
if (isMouseIn && document.hidden) {
console.log("Click Recorded By Visibility Change");
}
});
window.addEventListener("beforeunload", (event) => {
if (isMouseIn) {
console.log("Click Recorded By Before Unload");
}
});
If a new tab is opened / same page unloads and the mouse pointer is within the Iframe, a click is considered
Based in the answer of Paul Draper, I created a solution that work continuously when you have Iframes that open other tab in the browser. When you return the page continue to be active to detect the click over the framework, this is a very common situation:
focus();
$(window).blur(() => {
let frame = document.activeElement;
if (document.activeElement.tagName == "IFRAME") {
// Do you action.. here frame has the iframe clicked
let frameid = frame.getAttribute('id')
let frameurl = (frame.getAttribute('src'));
}
});
document.addEventListener("visibilitychange", function () {
if (document.hidden) {
} else {
focus();
}
});
The Code is simple, the blur event detect the lost of focus when the iframe is clicked, and test if the active element is the iframe (if you have several iframe you can know who was selected) this situation is frequently when you have publicity frames.
The second event trigger a focus method when you return to the page. it is used the visibility change event.
Here is solution using suggested approaches with hover+blur and active element tricks, not any libraries, just pure js. Works fine for FF/Chrome. Mostly approache is same as #Mohammed Radwan proposed, except that I use different method proposed by #zone117x to track iframe click for FF, because window.focus is not working without addition user settings:
Makes a request to bring the window to the front. It may fail due to
user settings and the window isn't guaranteed to be frontmost before
this method returns.
Here is compound method:
function () {
const state = {};
(function (setup) {
if (typeof window.addEventListener !== 'undefined') {
window.addEventListener('load', setup, false);
} else if (typeof document.addEventListener !== 'undefined') {
document.addEventListener('load', setup, false);
} else if (typeof window.attachEvent !== 'undefined') {
window.attachEvent('onload', setup);
} else {
if (typeof window.onload === 'function') {
const oldonload = onload;
window.onload = function () {
oldonload();
setup();
};
} else {
window.onload = setup;
}
}
})(function () {
state.isOverIFrame = false;
state.firstBlur = false;
state.hasFocusAcquired = false;
findIFramesAndBindListeners();
document.body.addEventListener('click', onClick);
if (typeof window.attachEvent !== 'undefined') {
top.attachEvent('onblur', function () {
state.firstBlur = true;
state.hasFocusAcquired = false;
onIFrameClick()
});
top.attachEvent('onfocus', function () {
state.hasFocusAcquired = true;
console.log('attachEvent.focus');
});
} else if (typeof window.addEventListener !== 'undefined') {
top.addEventListener('blur', function () {
state.firstBlur = true;
state.hasFocusAcquired = false;
onIFrameClick();
}, false);
top.addEventListener('focus', function () {
state.hasFocusAcquired = true;
console.log('addEventListener.focus');
});
}
setInterval(findIFramesAndBindListeners, 500);
});
function isFF() {
return navigator.userAgent.search(/firefox/i) !== -1;
}
function isActiveElementChanged() {
const prevActiveTag = document.activeElement.tagName.toUpperCase();
document.activeElement.blur();
const currActiveTag = document.activeElement.tagName.toUpperCase();
return !prevActiveTag.includes('BODY') && currActiveTag.includes('BODY');
}
function onMouseOut() {
if (!state.firstBlur && isFF() && isActiveElementChanged()) {
console.log('firefox first click');
onClick();
} else {
document.activeElement.blur();
top.focus();
}
state.isOverIFrame = false;
console.log(`onMouseOut`);
}
function onMouseOver() {
state.isOverIFrame = true;
console.log(`onMouseOver`);
}
function onIFrameClick() {
console.log(`onIFrameClick`);
if (state.isOverIFrame) {
onClick();
}
}
function onClick() {
console.log(`onClick`);
}
function findIFramesAndBindListeners() {
return Array.from(document.getElementsByTagName('iframe'))
.forEach(function (element) {
element.onmouseover = onMouseOver;
element.onmouseout = onMouseOut;
});
}
}
A colleague and I, we have a problem similar to that of Brian Trumpsett and found this thread very helpful.
Our kiosk has animations inside iframes and we need to track the page activity to set a timer.
As suggested here, rather than tracking the clicks, we now detect the focus change at each click and change it back
The following code is Okay on macOS with Safari and Chrome but does not work with FireFox (why?):
var eventListener = window.addEventListener('blur', function() {
if (document.activeElement.classList && document.activeElement.classList[0] == 'contentiFrame') {
refresh(); //function you want to call on click
setTimeout(function(){ window.focus(); }, 1);
}
window.removeEventListener('blur', eventListener );
});
The problem is that, on Windows, it works neither with Chrome nor with FireFox and thus, our kiosk is not functional.
Do you know why it is not working ?
Do you have a solution to make it work on Windows ?
As found there : Detect Click into Iframe using JavaScript
=> We can use iframeTracker-jquery :
$('.carousel-inner .item').each(function(e) {
var item = this;
var iFrame = $(item).find('iframe');
if (iFrame.length > 0) {
iFrame.iframeTracker({
blurCallback: function(){
// Do something when iFrame is clicked (like firing an XHR request)
onItemClick.bind(item)(); // calling regular click with right context
console.log('IFrameClick => OK');
}
});
console.log('IFrameTrackingRegistred => OK');
}
})
My approach was similar to that proposed by Paul Draper above. However, it didn't work in Firefox because activeElement did not update in time for the code to execute. So we wait a little bit.
This will also fire if you tab into the iframe. For my use case, it's fine, but you could filter for that keypress.
addEventListenerOnIframe() {
window.addEventListener('blur', this.onBlur);
}
onBlur = () => {
setTimeout(() => {
let activeElement = document.activeElement;
let iframeElement = document.querySelector('iframe');
if (activeElement === iframeElement) {
//execute your code here
//we only want to listen for the first time we click into the iframe
window.removeEventListener('blur', this.onBlur);
}
}, 500);
};
I believe you can do something like:
$('iframe').contents().click(function(){function to record click here });
using jQuery to accomplish this.

How to tell if a <script> tag failed to load

I'm dynamically adding <script> tags to a page's <head>, and I'd like to be able to tell whether the loading failed in some way -- a 404, a script error in the loaded script, whatever.
In Firefox, this works:
var script_tag = document.createElement('script');
script_tag.setAttribute('type', 'text/javascript');
script_tag.setAttribute('src', 'http://fail.org/nonexistant.js');
script_tag.onerror = function() { alert("Loading failed!"); }
document.getElementsByTagName('head')[0].appendChild(script_tag);
However, this doesn't work in IE or Safari.
Does anyone know of a way to make this work in browsers other than Firefox?
(I don't think a solution that requires placing special code within the .js files is a good one. It's inelegant and inflexible.)
UPDATE 2021:
All browsers today support onerror="" on script tags, examples:
Building script tag in js on MDN
Html example by #Rudey in comments: <script src="nonexistent.js" onerror="alert('error!')"></script>
Original comment from 2010:
If you only care about html5 browsers you can use error event.
From the spec:
If the src attribute's value is the
empty string or if it could not be
resolved, then the user agent must
queue a task to fire a simple event
named error at the element, and
abort these steps.
(...)
If the load resulted in an error (for
example a DNS error, or an HTTP 404
error) Executing the script block must
just consist of firing a simple event
named error at the element.
This means you don't have to do any error prone polling and can combine it with async and defer attribute to make sure the script is not blocking page rendering:
The defer attribute may be specified
even if the async attribute is
specified, to cause legacy Web
browsers that only support defer (and
not async) to fall back to the defer
behavior instead of the synchronous
blocking behavior that is the default.
More on http://www.w3.org/TR/html5/scripting-1.html#script
There is no error event for the script tag. You can tell when it is successful, and assume that it has not loaded after a timeout:
<script type="text/javascript" onload="loaded=1" src="....js"></script>
my working clean solution (2017)
function loaderScript(scriptUrl){
return new Promise(function (res, rej) {
let script = document.createElement('script');
script.src = scriptUrl;
script.type = 'text/javascript';
script.onerror = rej;
script.async = true;
script.onload = res;
script.addEventListener('error',rej);
script.addEventListener('load',res);
document.head.appendChild(script);
})
}
As Martin pointed, used like that:
const event = loaderScript("myscript.js")
.then(() => { console.log("loaded"); })
.catch(() => { console.log("error"); });
OR
try{
await loaderScript("myscript.js")
console.log("loaded");
}catch{
console.log("error");
}
The script from Erwinus works great, but isn't very clearly coded. I took the liberty to clean it up and decipher what it was doing. I've made these changes:
Meaningful variable names
Use of prototype.
require() uses an argument variable
No alert() messages are returned by default
Fixed some syntax errors and scope issues I was getting
Thanks again to Erwinus, the functionality itself is spot on.
function ScriptLoader() {
}
ScriptLoader.prototype = {
timer: function (times, // number of times to try
delay, // delay per try
delayMore, // extra delay per try (additional to delay)
test, // called each try, timer stops if this returns true
failure, // called on failure
result // used internally, shouldn't be passed
) {
var me = this;
if (times == -1 || times > 0) {
setTimeout(function () {
result = (test()) ? 1 : 0;
me.timer((result) ? 0 : (times > 0) ? --times : times, delay + ((delayMore) ? delayMore : 0), delayMore, test, failure, result);
}, (result || delay < 0) ? 0.1 : delay);
} else if (typeof failure == 'function') {
setTimeout(failure, 1);
}
},
addEvent: function (el, eventName, eventFunc) {
if (typeof el != 'object') {
return false;
}
if (el.addEventListener) {
el.addEventListener(eventName, eventFunc, false);
return true;
}
if (el.attachEvent) {
el.attachEvent("on" + eventName, eventFunc);
return true;
}
return false;
},
// add script to dom
require: function (url, args) {
var me = this;
args = args || {};
var scriptTag = document.createElement('script');
var headTag = document.getElementsByTagName('head')[0];
if (!headTag) {
return false;
}
setTimeout(function () {
var f = (typeof args.success == 'function') ? args.success : function () {
};
args.failure = (typeof args.failure == 'function') ? args.failure : function () {
};
var fail = function () {
if (!scriptTag.__es) {
scriptTag.__es = true;
scriptTag.id = 'failed';
args.failure(scriptTag);
}
};
scriptTag.onload = function () {
scriptTag.id = 'loaded';
f(scriptTag);
};
scriptTag.type = 'text/javascript';
scriptTag.async = (typeof args.async == 'boolean') ? args.async : false;
scriptTag.charset = 'utf-8';
me.__es = false;
me.addEvent(scriptTag, 'error', fail); // when supported
// when error event is not supported fall back to timer
me.timer(15, 1000, 0, function () {
return (scriptTag.id == 'loaded');
}, function () {
if (scriptTag.id != 'loaded') {
fail();
}
});
scriptTag.src = url;
setTimeout(function () {
try {
headTag.appendChild(scriptTag);
} catch (e) {
fail();
}
}, 1);
}, (typeof args.delay == 'number') ? args.delay : 1);
return true;
}
};
$(document).ready(function () {
var loader = new ScriptLoader();
loader.require('resources/templates.js', {
async: true, success: function () {
alert('loaded');
}, failure: function () {
alert('NOT loaded');
}
});
});
I know this is an old thread but I got a nice solution to you (I think). It's copied from an class of mine, that handles all AJAX stuff.
When the script cannot be loaded, it set an error handler but when the error handler is not supported, it falls back to a timer that checks for errors for 15 seconds.
function jsLoader()
{
var o = this;
// simple unstopable repeat timer, when t=-1 means endless, when function f() returns true it can be stopped
o.timer = function(t, i, d, f, fend, b)
{
if( t == -1 || t > 0 )
{
setTimeout(function() {
b=(f()) ? 1 : 0;
o.timer((b) ? 0 : (t>0) ? --t : t, i+((d) ? d : 0), d, f, fend,b );
}, (b || i < 0) ? 0.1 : i);
}
else if(typeof fend == 'function')
{
setTimeout(fend, 1);
}
};
o.addEvent = function(el, eventName, eventFunc)
{
if(typeof el != 'object')
{
return false;
}
if(el.addEventListener)
{
el.addEventListener (eventName, eventFunc, false);
return true;
}
if(el.attachEvent)
{
el.attachEvent("on" + eventName, eventFunc);
return true;
}
return false;
};
// add script to dom
o.require = function(s, delay, baSync, fCallback, fErr)
{
var oo = document.createElement('script'),
oHead = document.getElementsByTagName('head')[0];
if(!oHead)
{
return false;
}
setTimeout( function() {
var f = (typeof fCallback == 'function') ? fCallback : function(){};
fErr = (typeof fErr == 'function') ? fErr : function(){
alert('require: Cannot load resource -'+s);
},
fe = function(){
if(!oo.__es)
{
oo.__es = true;
oo.id = 'failed';
fErr(oo);
}
};
oo.onload = function() {
oo.id = 'loaded';
f(oo);
};
oo.type = 'text/javascript';
oo.async = (typeof baSync == 'boolean') ? baSync : false;
oo.charset = 'utf-8';
o.__es = false;
o.addEvent( oo, 'error', fe ); // when supported
// when error event is not supported fall back to timer
o.timer(15, 1000, 0, function() {
return (oo.id == 'loaded');
}, function(){
if(oo.id != 'loaded'){
fe();
}
});
oo.src = s;
setTimeout(function() {
try{
oHead.appendChild(oo);
}catch(e){
fe();
}
},1);
}, (typeof delay == 'number') ? delay : 1);
return true;
};
}
$(document).ready( function()
{
var ol = new jsLoader();
ol.require('myscript.js', 800, true, function(){
alert('loaded');
}, function() {
alert('NOT loaded');
});
});
To check if the javascript in nonexistant.js returned no error you have to add a variable inside http://fail.org/nonexistant.js like var isExecuted = true; and then check if it exists when the script tag is loaded.
However if you only want to check that the nonexistant.js returned without a 404 (meaning it exists), you can try with a isLoaded variable ...
var isExecuted = false;
var isLoaded = false;
script_tag.onload = script_tag.onreadystatechange = function() {
if(!this.readyState ||
this.readyState == "loaded" || this.readyState == "complete") {
// script successfully loaded
isLoaded = true;
if(isExecuted) // no error
}
}
This will cover both cases.
I hope this doesn't get downvoted, because in special circumstances it is the most reliable way to solve the problem. Any time the server allows you to get a Javascript resource using CORS (http://en.wikipedia.org/wiki/Cross-origin_resource_sharing), you have a rich array of options to do so.
Using XMLHttpRequest to fetch resources will work across all modern browsers, including IE. Since you are looking to load Javascript, you have Javascript available to you in the first place. You can track the progress using the readyState (http://en.wikipedia.org/wiki/XMLHttpRequest#The_onreadystatechange_event_listener). Finally, once you receive the content of the file, you can execute it with eval ( ). Yes, I said eval -- because security-wise it is no different from loading the script normally. In fact, a similar technique is suggested by John Resig to have nicer tags (http://ejohn.org/blog/degrading-script-tags/).
This method also lets you separate the loading from the eval, and execute functions before and after the eval happens. It becomes very useful when loading scripts in parallel but evaluating them one after the other -- something browsers can do easily when you place the tags in HTML, but don't let you by adding scripts at run-time with Javascript.
CORS is also preferable to JSONP for loading scripts (http://en.wikipedia.org/wiki/XMLHttpRequest#Cross-domain_requests). However, if you are developing your own third-party widgets to be embedded in other sites, you should actually load the Javascript files from your own domain in your own iframe (again, using AJAX)
In short:
Try to see if you can load the resource using AJAX GET
Use eval after it has successfully loaded
To improve it:
Check out the cache-control headers being sent
Look into otherwise caching the content in localStorage, if you need to
Check out Resig's "degrading javascript" for cleaner code
Check out require.js
This trick worked for me, although I admit that this is probably not the best way to solve this problem. Instead of trying this, you should see why the javascripts aren't loading. Try keeping a local copy of the script in your server, etc. or check with the third party vendor from where you are trying to download the script.
Anyways, so here's the workaround:
1) Initialize a variable to false
2) Set it to true when the javascript loads (using the onload attribute)
3) check if the variable is true or false once the HTML body has loaded
<html>
<head>
<script>
var scriptLoaded = false;
function checkScriptLoaded() {
if (scriptLoaded) {
// do something here
} else {
// do something else here!
}
}
</script>
<script src="http://some-external-script.js" onload="scriptLoaded=true;" />
</head>
<body onload="checkScriptLoaded()">
<p>My Test Page!</p>
</body>
</html>
Here is another JQuery-based solution without any timers:
<script type="text/javascript">
function loadScript(url, onsuccess, onerror) {
$.get(url)
.done(function() {
// File/url exists
console.log("JS Loader: file exists, executing $.getScript "+url)
$.getScript(url, function() {
if (onsuccess) {
console.log("JS Loader: Ok, loaded. Calling onsuccess() for " + url);
onsuccess();
console.log("JS Loader: done with onsuccess() for " + url);
} else {
console.log("JS Loader: Ok, loaded, no onsuccess() callback " + url)
}
});
}).fail(function() {
// File/url does not exist
if (onerror) {
console.error("JS Loader: probably 404 not found. Not calling $.getScript. Calling onerror() for " + url);
onerror();
console.error("JS Loader: done with onerror() for " + url);
} else {
console.error("JS Loader: probably 404 not found. Not calling $.getScript. No onerror() callback " + url);
}
});
}
</script>
Thanks to:
https://stackoverflow.com/a/14691735/1243926
Sample usage (original sample from JQuery getScript documentation):
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery.getScript demo</title>
<style>
.block {
background-color: blue;
width: 150px;
height: 70px;
margin: 10px;
}
</style>
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
</head>
<body>
<button id="go">ยป Run</button>
<div class="block"></div>
<script>
function loadScript(url, onsuccess, onerror) {
$.get(url)
.done(function() {
// File/url exists
console.log("JS Loader: file exists, executing $.getScript "+url)
$.getScript(url, function() {
if (onsuccess) {
console.log("JS Loader: Ok, loaded. Calling onsuccess() for " + url);
onsuccess();
console.log("JS Loader: done with onsuccess() for " + url);
} else {
console.log("JS Loader: Ok, loaded, no onsuccess() callback " + url)
}
});
}).fail(function() {
// File/url does not exist
if (onerror) {
console.error("JS Loader: probably 404 not found. Not calling $.getScript. Calling onerror() for " + url);
onerror();
console.error("JS Loader: done with onerror() for " + url);
} else {
console.error("JS Loader: probably 404 not found. Not calling $.getScript. No onerror() callback " + url);
}
});
}
loadScript("https://raw.github.com/jquery/jquery-color/master/jquery.color.js", function() {
console.log("loaded jquery-color");
$( "#go" ).click(function() {
$( ".block" )
.animate({
backgroundColor: "rgb(255, 180, 180)"
}, 1000 )
.delay( 500 )
.animate({
backgroundColor: "olive"
}, 1000 )
.delay( 500 )
.animate({
backgroundColor: "#00f"
}, 1000 );
});
}, function() { console.error("Cannot load jquery-color"); });
</script>
</body>
</html>
This can be done safely using promises
function loadScript(src) {
return new Promise(function(resolve, reject) {
let script = document.createElement('script');
script.src = src;
script.onload = () => resolve(script);
script.onerror = () => reject(new Error("Script load error: " + src));
document.head.append(script);
});
}
and use like this
let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js");
promise.then(
script => alert(`${script.src} is loaded!`),
error => alert(`Error: ${error.message}`)
);
onerror Event
*Update August 2017: onerror is fired by Chrome and Firefox. onload is fired by Internet Explorer. Edge fires neither onerror nor onload. I wouldnt use this method but it could work in some cases. See also
<link> onerror do not work in IE
*
Definition and Usage
The onerror event is triggered if an error occurs while loading an external file (e.g. a document or an image).
Tip: When used on audio/video media, related events that occurs when there is some kind of disturbance to the media loading process, are:
onabort
onemptied
onstalled
onsuspend
In HTML:
element onerror="myScript">
In JavaScript, using the addEventListener() method:
object.addEventListener("error", myScript);
Note: The addEventListener() method is not supported in Internet Explorer 8 and earlier versions.
Example
Execute a JavaScript if an error occurs when loading an image:
img src="image.gif" onerror="myFunction()">
The reason it doesn't work in Safari is because you're using attribute syntax. This will work fine though:
script_tag.addEventListener('error', function(){/*...*/}, true);
...except in IE.
If you want to check the script executed successfully, just set a variable using that script and check for it being set in the outer code.
This doesn't need jquery, doesn't need to load the script async, needs no timer nor to have the loaded script set a value. I've tested it in FF, Chrome, and Safari.
<script>
function loadScript(src) {
return new Promise(function(resolve, reject) {
let s = window.document.createElement("SCRIPT");
s.onload = () => resolve(s);
s.onerror = () => reject(new Error(src));
s.src = src;
// don't bounce to global handler on 404.
s.addEventListener('error', function() {});
window.document.head.append(s);
});
}
let successCallback = (result) => {
console.log(scriptUrl + " loaded.");
}
let failureCallback = (error) => {
console.log("load failed: " + error.message);
}
loadScript(scriptUrl).then(successCallback, failureCallback);
</script>
It was proposed to set a timeout and then assume load failure after a timeout.
setTimeout(fireCustomOnerror, 4000);
The problem with that approach is that the assumption is based on chance. After your timeout expires, the request is still pending. The request for the pending script may load, even after the programmer assumed that load won't happen.
If the request could be canceled, then the program could wait for a period, then cancel the request.
This is how I used a promise to detect loading errors that are emited on the window object:
<script type='module'>
window.addEventListener('error', function(error) {
let url = error.filename
url = url.substring(0, (url.indexOf("#") == -1) ? url.length : url.indexOf("#"));
url = url.substring(0, (url.indexOf("?") == -1) ? url.length : url.indexOf("?"));
url = url.substring(url.lastIndexOf("/") + 1, url.length);
window.scriptLoadReject && window.scriptLoadReject[url] && window.scriptLoadReject[url](error);
}, true);
window.boot=function boot() {
const t=document.createElement('script');
t.id='index.mjs';
t.type='module';
new Promise((resolve, reject) => {
window.scriptLoadReject = window.scriptLoadReject || {};
window.scriptLoadReject[t.id] = reject;
t.addEventListener('error', reject);
t.addEventListener('load', resolve); // Careful load is sometimes called even if errors prevent your script from running! This promise is only meant to catch errors while loading the file.
}).catch((value) => {
document.body.innerHTML='Error loading ' + t.id + '! Please reload this webpage.<br/>If this error persists, please try again later.<div><br/>' + t.id + ':' + value.lineno + ':' + value.colno + '<br/>' + (value && value.message);
});
t.src='./index.mjs'+'?'+new Date().getTime();
document.head.appendChild(t);
};
</script>
<script nomodule>document.body.innerHTML='This website needs ES6 Modules!<br/>Please enable ES6 Modules and then reload this webpage.';</script>
</head>
<body onload="boot()" style="margin: 0;border: 0;padding: 0;text-align: center;">
<noscript>This website needs JavaScript!<br/>Please enable JavaScript and then reload this webpage.</noscript>
Well, the only way I can think of doing everything you want is pretty ugly. First perform an AJAX call to retrieve the Javascript file contents. When this completes you can check the status code to decide if this was successful or not. Then take the responseText from the xhr object and wrap it in a try/catch, dynamically create a script tag, and for IE you can set the text property of the script tag to the JS text, in all other browsers you should be able to append a text node with the contents to script tag. If there's any code that expects a script tag to actually contain the src location of the file, this won't work, but it should be fine for most situations.

Categories

Resources