how can i mute an iframe on keypress - javascript

I have 2 pages, one is a video playing using html video tag
the other is an iframe of the first page.
I want to mute audio in iframe on keypress
The pages are not on the same domain.
this is the code I tried
function cynMute(){
const myElement = document.getElementById("myVideo");
if(myElement.muted === false) {
myElement.muted = true
}
else{
myElement.muted = false
}
}
document.addEventListener("keypress", function(event) {
if (event.keyCode === 13) {
cynMute()
}
});
which works on the first page but it doesn't work when I try to press enter on the second page. I tried putting the code with the respective ids on the second page but still nothing.

From what i understand, you are not able to select the video element inside your iframe. The same function will not work because this time #myVideo is not in the document.
You will have to get your iframe first and the query inside it using contentWindow.
function cynMuteForIframe(){
let iframe = document.getElementById("myFrame");
if(iframe !=null){
const myElement = iframe.contentWindow.document.getElementById("myVideo");
//Rest of your code
if(myElement.muted === false) {
myElement.muted = true
}
else{
myElement.muted = false
}
}

Related

Opening links in a new tab with javascript stops working

I have some code on my tumblr blog that makes it so the links in the "notes" div open in a new tab — it works fine but once i click "Show More Notes" (which loads more links), it stops working on those links. Here is my code.
<script type="text/javascript">
$(function(){
$("#notes a").attr("target","_blank");
});
</script>
Here is what tumblr says on this issue: "Notes are paginated with AJAX. If your theme needs to manipulate the Notes markup or DOM nodes, you can add a Javascript callback that fires when a new page of Notes is loaded or inserted"
tumblrNotesLoaded(notes_html) "If this Javascript function is defined, it will be triggered when a new page of Notes is loaded and ready to be inserted. If this function returns false, it will block the Notes from being inserted."
tumblrNotesInserted() "If this Javascript function is defined, it will be triggered after a new page of Notes has been inserted into the DOM."
the newly loaded links wont have target set to _blank ... what you should do, when you've loaded the new links is execute
$("#notes a").attr("target","_blank");
again - as you've shown no code regarding the loading of these links, that's the best I can do
edit: I just looked at your page - I think this should do the trick
this.style.display = 'none';
document.getElementById('notes_loading_121152690941').style.display = 'block';
if (window.ActiveXObject) var tumblrReq = new ActiveXObject('Microsoft.XMLHTTP');
else if (window.XMLHttpRequest) var tumblrReq = new XMLHttpRequest();
else return false;
tumblrReq.onreadystatechange = function() {
if (tumblrReq.readyState == 4) {
var notes_html = tumblrReq.responseText.split('<!-- START ' + 'NOTES -->')[1].split('<!-- END ' + 'NOTES -->')[0];
if (window.tumblrNotesLoaded)
if (tumblrNotesLoaded(notes_html) == false) return;
var more_notes_link = document.getElementById('more_notes_121152690941');
var notes = more_notes_link.parentNode;
notes.removeChild(more_notes_link);
notes.innerHTML += notes_html;
if (window.tumblrNotesInserted) tumblrNotesInserted(notes_html);
// ************************************************
$("#notes a").attr("target","_blank");
// ************************************************
}
};
tumblrReq.open('GET', '/notes/121152690941/lhtXynZtK?from_c=1433902641', true);
tumblrReq.send();
return false;
the added line is surrounded by // ************************************************
You could set target attribute on mousedown delegated event, e.g:
$(function () {
$('#notes').on('mousedown', 'a:not([target])', function () {
this.target = "_blank";
});
});
Use this:
$('ol.notes').on('click', 'a', function (e) {
if (e.which > 1 || e.shiftKey || e.altKey || e.metaKey || e.isDefaultPrevented()) {
return;
}
window.open(this.href);
e.preventDefault();
});
This attaches an event handler on the notes container, making sure that it only gets clicks on a elements and that the clicks are not middle-clicks, alt-clicks, …
Once all of those superfluous clicks are filtered out, it simply opens a new tab via Javascript; there's no need to set target="_blank"

Toggling on the first click

I've tried two different methods of toggling the play/pause button on my player, neither of which work on the first click, for some reason.
This one, supposedly checks the status of the audio to see if it's paused or ended:
function togglePlayPause() {
var audioPlayer = document.getElementsByTagName('audio')[0];
var playpause = document.getElementById("playpause");
if (audioPlayer.paused || audioPlayer.ended) {
playpause.title = "pause";
playpause.innerHTML = "pause";
}
else {
playpause.title = "play";
playpause.innerHTML = "play";
}
}
Or I've tried this one, which just toggles via the onClick toggle(this):
function toggle(obj) {
if (obj.className== 'playButton') {
obj.className = 'pauseButton';
obj.title = "PAUSE";
obj.innerHTML = "PAUSE";
} else {
obj.className = 'playButton';
obj.title = "PLAY";
obj.innerHTML = "PLAY";
}
}
Neither toggle the first time the button is clicked, although the first method does change from the default inner "PLAY" to "play", so I guess that's something:
<div title="play" class="playButton" id="playpause">PLAY</div>
In both methods, subsequent clicks work fine. Any idea why this is happening? Could it have something to do with the way the audioPlayer variable is called? The array starts from 0. (I'm clutching at straws.)
Many thanks as usual!
I would go without creating functions, I would check if the link is clicked then proceed to the events that would be fired.
so something like $("#start").click(function(){}); in can be tried.
First, have the jQuery library included in your HTML header.
Then create a new javascript file, included it as well (usually this is put after the jQuery included)
In your new javascript file write the following
$(document).ready(function() {
$("#start, #stop, #play, #pause").click(function() { //you can have more or less selectors (selectors are the ones with #)
//Your code goes here
});
});
Here is a fiddle for that solution. http://jsfiddle.net/JRRm2/1/ (tidier text: http://jsfiddle.net/JRRm2/2/)

Detecting when an iframe gets or loses focus

What's the correct way of detecting when an iframe gets or loses focus (i.e. will or will not receive keyboard events)? The following is not working in Fx4:
var iframe = /* my iframe */;
iframe.addEventListener("focus", function() { /* never gets called */ }, false);
You can poll "document.activeElement" to determine if it matches the iframe. Polling isn't ideal, but it works:
function checkFocus() {
if(document.activeElement == document.getElementsByTagName("iframe")[0]) {
console.log('iframe has focus');
} else {
console.log('iframe not focused');
}
}
window.setInterval(checkFocus, 1000);
i know it's old, but i also had the same problem.
i ended up using this little pice of code:
$(document).on('focusout', function(){
setTimeout(function(){
// using the 'setTimout' to let the event pass the run loop
if (document.activeElement instanceof HTMLIFrameElement) {
// Do your logic here..
}
},0);
});
Turns out it's not really possible. I had to change the logic of my page to avoid the need of tracking if the iframe has focus.
How to check when an iframe has been clicked in or out of as well as hover-state.
Note: I would highly recommend you don't choose a polling method and go with an event driven method such as this.
Disclaimer
It is not possible to use the focus or blur events directly on an iframe but you can use them on the window to provide an event driven method of checking the document.activeElement. Thus you can accomplish what you're after.
Although we're now in 2018, my code is being implemented in GTM and tries to be cross browser compatible back to IE 11. This means there's more efficient code if you're utilizing newer ES/ECMAScript features.
Setup
I'm going to take this a few steps further to show that we can also get the iframe's src attribute as well as determine if it's being hovered.
Code
You would ideally need to put this in a document ready event, or at least encapsulate it so that the variables aren't global [maybe use an IIFE]. I did not wrap it in a document ready because it's handled by GTM. It may also depend where you place this or how you're loading it such as in the footer.
https://jsfiddle.net/9285tbsm/9/
I have noticed in the JSFiddle preview that it's already an iframe, sometimes you have to focus it first before events start to capture. Other issues can be that your browser window isn't yet focused either.
// Helpers
var iframeClickedLast;
function eventFromIframe(event) {
var el = event.target;
return el && el.tagName && el.tagName.toLowerCase() == 'iframe';
}
function getIframeSrc(event) {
var el = event.target;
return eventFromIframe(event) ? el.getAttribute('src') : '';
}
// Events
function windowBlurred(e) {
var el = document.activeElement;
if (el.tagName.toLowerCase() == 'iframe') {
console.log('Blurred: iframe CLICKED ON', 'SRC:', el.getAttribute('src'), e);
iframeClickedLast = true;
}
else {
console.log('Blurred', e);
}
}
function windowFocussed(e) {
if (iframeClickedLast) {
var el = document.activeElement;
iframeClickedLast = false;
console.log('Focussed: iframe CLICKED OFF', 'SRC:', el.getAttribute('src'), e);
}
else {
console.log('Focussed', e);
}
}
function iframeMouseOver(e) {
console.log('Mouse Over', 'SRC:', getIframeSrc(e), e);
}
function iframeMouseOut(e) {
console.log('Mouse Out', 'SRC:', getIframeSrc(e), e);
}
// Attach Events
window.addEventListener('focus', windowFocussed, true);
window.addEventListener('blur', windowBlurred, true);
var iframes = document.getElementsByTagName("iframe");
for (var i = 0; i < iframes.length; i++) {
iframes[i].addEventListener('mouseover', iframeMouseOver, true);
iframes[i].addEventListener('mouseout', iframeMouseOut, true);
}
I have solved this by using contentWindow instead of contentDocument.
The good thing about contentWindow is
it works also in case user clicks another window (another application) or another browser tab. If using activeElement, if user clicks away from the entire window to go to another application, then that logic still think the iframe is in focus, while it is not
and we don't need to poll and do a setInterval at all. This uses the normal addEventListener
let iframe = document.getElementsByTagName("iframe")[0];
// or whatever way you do to grab that iFrame, say you have an `id`, then it's even more precise
if(iframe){
iframeWindow = iframe.contentWindow;
iframeWindow.addEventListener('focus', handleIframeFocused);
iframeWindow.addEventListener('blur', handleIframeBlurred);
}
function handleIframeFocused(){
console.log('iframe focused');
// Additional logic that you need to implement here when focused
}
function handleIframeBlurred(){
console.log('iframe blurred');
// Additional logic that you need to implement here when blurred
}
This solution is working for me on both mobile and desktop:
;(function pollForIframe() {
var myIframe = document.querySelector('#my_iframe');
if (!myIframe) return setTimeout(pollForIframe, 50);
window.addEventListener('blur', function () {
if (document.activeElement == myIframe) {
console.log('myIframe clicked!');
}
});
})();
The solution is to inject a javascript event on the parent page like this :
var script = document.createElement('script');
script.type = 'text/javascript';
script.innerHTML =
"document.addEventListener('click', function()" +
"{ if(document.getElementById('iframe')) {" +
// What you want
"}});";
head.appendChild(script);
Here is the code to Detecting when an iframe gets or loses focus
// This code can be used to verify Iframe gets focus/loses.
function CheckFocus(){
if (document.activeElement.id == $(':focus').context.activeElement.id) {
// here do something
}
else{
//do something
}
}
A compact function that accepts callbacks you want to run when iframe gets or loses focus.
/* eslint-disable no-unused-vars */
export default function watchIframeFocus(onFocus, onBlur) {
let iframeClickedLast;
function windowBlurred(e) {
const el = document.activeElement;
if (el.tagName.toLowerCase() == 'iframe') {
iframeClickedLast = true;
onFocus();
}
}
function windowFocussed(e) {
if (iframeClickedLast) {
iframeClickedLast = false;
onBlur();
}
}
window.addEventListener('focus', windowFocussed, true);
window.addEventListener('blur', windowBlurred, true);
}
This might work
document.addEventListener('click', function(event) {
var frame= document.getElementById("yourFrameID");
var isClickInsideFrame = frame.contains(event.target);
if (!isClickInsideFrame ) {
//exec code
}
});

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.

jQuery .ready in a dynamically inserted iframe

We are using jQuery thickbox to dynamically display an iframe when someone clicks on a picture. In this iframe, we are using galleria a javascript library to display multiple pictures.
The problem seems to be that $(document).ready in the iframe seems to be fired too soon and the iframe content isn't even loaded yet, so galleria code is not applied properly on the DOM elements. $(document).ready seems to use the iframe parent ready state to decide if the iframe is ready.
If we extract the function called by document ready in a separate function and call it after a timeout of 100 ms. It works, but we can't take the chance in production with a slow computer.
$(document).ready(function() { setTimeout(ApplyGalleria, 100); });
My question: which jQuery event should we bind to to be able to execute our code when the dynamic iframe is ready and not just it's a parent?
I answered a similar question (see Javascript callback when IFRAME is finished loading?).
You can obtain control over the iframe load event with the following code:
function callIframe(url, callback) {
$(document.body).append('<IFRAME id="myId" ...>');
$('iframe#myId').attr('src', url);
$('iframe#myId').load(function() {
callback(this);
});
}
In dealing with iframes I found good enough to use load event instead of document ready event.
Using jQuery 1.3.2 the following worked for me:
$('iframe').ready(function() {
$('body', $('iframe').contents()).html('Hello World!');
});
REVISION:!
Actually the above code sometimes looks like it works in Firefox, never looks like it works in Opera.
Instead I implemented a polling solution for my purposes. Simplified down it looks like this:
$(function() {
function manipIframe() {
el = $('body', $('iframe').contents());
if (el.length != 1) {
setTimeout(manipIframe, 100);
return;
}
el.html('Hello World!');
}
manipIframe();
});
This doesn't require code in the called iframe pages. All code resides and executes from the parent frame/window.
In IFrames I usually solve this problem by putting a small script to the very end of the block:
<body>
The content of your IFrame
<script type="text/javascript">
//<![CDATA[
fireOnReadyEvent();
parent.IFrameLoaded();
//]]>
</script>
</body>
This work most of the time for me. Sometimes the simplest and most naive solution is the most appropriate.
Following DrJokepu's and David Murdoch idea I implemented a more complete version.
It requires jQuery on both the parent and iframe and the iframe to be in your control.
iframe code:
var iframe = window.frameElement;
if (iframe){
iframe.contentDocument = document;//normalization: some browsers don't set the contentDocument, only the contentWindow
var parent = window.parent;
$(parent.document).ready(function(){//wait for parent to make sure it has jQuery ready
var parent$ = parent.jQuery;
parent$(iframe).trigger("iframeloading");
$(function(){
parent$(iframe).trigger("iframeready");
});
$(window).load(function(){//kind of unnecessary, but here for completion
parent$(iframe).trigger("iframeloaded");
});
$(window).unload(function(e){//not possible to prevent default
parent$(iframe).trigger("iframeunloaded");
});
$(window).on("beforeunload",function(){
parent$(iframe).trigger("iframebeforeunload");
});
});
}
parent test code:
$(function(){
$("iframe").on("iframeloading iframeready iframeloaded iframebeforeunload iframeunloaded", function(e){
console.log(e.type);
});
});
Found the solution to the problem.
When you click on a thickbox link that open a iframe, it insert an iframe with an id of TB_iframeContent.
Instead of relying on the $(document).ready event in the iframe code, I just have to bind to the load event of the iframe in the parent document:
$('#TB_iframeContent', top.document).load(ApplyGalleria);
This code is in the iframe but binds to an event of a control in the parent document. It works in FireFox and IE.
This function from this answer is the best way to handle this as $.ready explicitly fails for iframes. Here's the decision not to support this.
The load event also doesn't fire if the iframe has already loaded. Very frustrating that this remains a problem in 2020!
function onIframeReady($i, successFn, errorFn) {
try {
const iCon = $i.first()[0].contentWindow,
bl = "about:blank",
compl = "complete";
const callCallback = () => {
try {
const $con = $i.contents();
if($con.length === 0) { // https://git.io/vV8yU
throw new Error("iframe inaccessible");
}
successFn($con);
} catch(e) { // accessing contents failed
errorFn();
}
};
const observeOnload = () => {
$i.on("load.jqueryMark", () => {
try {
const src = $i.attr("src").trim(),
href = iCon.location.href;
if(href !== bl || src === bl || src === "") {
$i.off("load.jqueryMark");
callCallback();
}
} catch(e) {
errorFn();
}
});
};
if(iCon.document.readyState === compl) {
const src = $i.attr("src").trim(),
href = iCon.location.href;
if(href === bl && src !== bl && src !== "") {
observeOnload();
} else {
callCallback();
}
} else {
observeOnload();
}
} catch(e) {
errorFn();
}
}
Basically what others have already posted but IMHO a bit cleaner:
$('<iframe/>', {
src: 'https://example.com/',
load: function() {
alert("loaded")
}
}).appendTo('body');
Try this,
<iframe id="testframe" src="about:blank" onload="if (testframe.location.href != 'about:blank') testframe_loaded()"></iframe>
All you need to do then is create the JavaScript function testframe_loaded().
I'm loading the PDF with jQuery ajax into browser cache. Then I create embedded element with data already in browser cache. I guess it will work with iframe too.
var url = "http://example.com/my.pdf";
// show spinner
$.mobile.showPageLoadingMsg('b', note, false);
$.ajax({
url: url,
cache: true,
mimeType: 'application/pdf',
success: function () {
// display cached data
$(scroller).append('<embed type="application/pdf" src="' + url + '" />');
// hide spinner
$.mobile.hidePageLoadingMsg();
}
});
You have to set your http headers correctly as well.
HttpContext.Response.Expires = 1;
HttpContext.Response.Cache.SetNoServerCaching();
HttpContext.Response.Cache.SetAllowResponseInBrowserHistory(false);
HttpContext.Response.CacheControl = "Private";
This was the exact issue I ran into with our client. I created a little jquery plugin that seems to work for iframe readiness. It uses polling to check the iframe document readyState combined with the inner document url combined with the iframe source to make sure the iframe is in fact "ready".
The issue with "onload" is that you need access to the actual iframe being added to the DOM, if you don't then you need to try to catch the iframe loading which if it is cached then you may not. What I needed was a script that could be called anytime, and determine whether or not the iframe was "ready" or not.
Here's the question:
Holy grail for determining whether or not local iframe has loaded
and here's the jsfiddle I eventually came up with.
https://jsfiddle.net/q0smjkh5/10/
In the jsfiddle above, I am waiting for onload to append an iframe to the dom, then checking iframe's inner document's ready state - which should be cross domain because it's pointed to wikipedia - but Chrome seems to report "complete". The plug-in's iready method then gets called when the iframe is in fact ready. The callback tries to check the inner document's ready state again - this time reporting a cross domain request (which is correct) - anyway it seems to work for what I need and hope it helps others.
<script>
(function($, document, undefined) {
$.fn["iready"] = function(callback) {
var ifr = this.filter("iframe"),
arg = arguments,
src = this,
clc = null, // collection
lng = 50, // length of time to wait between intervals
ivl = -1, // interval id
chk = function(ifr) {
try {
var cnt = ifr.contents(),
doc = cnt[0],
src = ifr.attr("src"),
url = doc.URL;
switch (doc.readyState) {
case "complete":
if (!src || src === "about:blank") {
// we don't care about empty iframes
ifr.data("ready", "true");
} else if (!url || url === "about:blank") {
// empty document still needs loaded
ifr.data("ready", undefined);
} else {
// not an empty iframe and not an empty src
// should be loaded
ifr.data("ready", true);
}
break;
case "interactive":
ifr.data("ready", "true");
break;
case "loading":
default:
// still loading
break;
}
} catch (ignore) {
// as far as we're concerned the iframe is ready
// since we won't be able to access it cross domain
ifr.data("ready", "true");
}
return ifr.data("ready") === "true";
};
if (ifr.length) {
ifr.each(function() {
if (!$(this).data("ready")) {
// add to collection
clc = (clc) ? clc.add($(this)) : $(this);
}
});
if (clc) {
ivl = setInterval(function() {
var rd = true;
clc.each(function() {
if (!$(this).data("ready")) {
if (!chk($(this))) {
rd = false;
}
}
});
if (rd) {
clearInterval(ivl);
clc = null;
callback.apply(src, arg);
}
}, lng);
} else {
clc = null;
callback.apply(src, arg);
}
} else {
clc = null;
callback.apply(this, arguments);
}
return this;
};
}(jQuery, document));
</script>

Categories

Resources