I am trying to track clicks on iframes on my site. So far this code is working and is triggered every time a click happens on an iframe.
var monitor = setInterval(function() {
var elem = document.activeElement;
if(elem && elem.tagName == 'IFRAME'){
alert('click on iframe');
clearInterval(monitor);
}
}, 500);
Now what I would like to do is to trigger different actions based on which iframe is clicked. Since I don't have control over classes or id's that the iframes might have I need to rely on the parent div's which are under my control, so I'm trying something like this which is currently not working.
var monitor = setInterval(function() {
var elem = document.activeElement;
if(elem && elem.tagName == 'IFRAME'){
if ($elem.parents('.ad_left').length) {
alert('this is an ad left iframe');
}
else if ($elem.parents('#youtube').length) {
alert('this is a youtube iframe');
}
else () {
alert('click on different iframe');
}
clearInterval(monitor);
}, 500);
Is this not possible or amd I missing something?
I managed to get it working, this is the code I used to check fi any ancestor of the iframe contains a particular class and performing different actions based on that:
var monitor = setInterval(function() {
var elem = document.activeElement;
if(elem && elem.tagName == 'IFRAME'){
if (elem.closest('.class_one') !== null) {
alert('this iframe has a parent with class class_one');
}
else if (elem.closest('.class_two') !== null) {
alert('this iframe has a parent with class class_two');
}
else if (elem.closest('.class_three') !== null) {
alert('this iframe has a parent with class class_three');
}
clearInterval(monitor);
}
}, 500);
Related
I'm trying to create a reusable function that works by hiding a specific div (outside the iframe) whenever a click is made anywhere inside an iframe.
To be more specific, this div I want to hide is a search menu that can be opened on top (z-index) of an iframe. I'd like to close this menu whenever I click outside it, which happens to be inside the full screen iframe.
I couldn't make it work using the solutions from this and other similar pages (Whenever I change the URL, it doesn't work anymore): Detect click event inside iframe
I managed to do something like this that works but the code is repetitive. I'd like a more general function that works whenever I click inside any iframe.
const iframeListener1 = addEventListener('blur', function() {
if (document.activeElement === document.getElementById('chrono-loader')) {
$('#outer-layer-card').stop().fadeOut('fast');
}
removeEventListener('blur', iframeListener);
});
const iframeListener2 = addEventListener('blur', function() {
if (document.activeElement === document.getElementById('plus-loader')) {
$('#outer-layer-card').stop().fadeOut('fast');
}
removeEventListener('blur', iframeListener);
});
const iframeListener3 = addEventListener('blur', function() {
if (document.activeElement === document.getElementById('google-docs-1-loader')) {
$('#outer-layer-card').stop().fadeOut('fast');
}
removeEventListener('blur', iframeListener);
});
const iframeListener4 = addEventListener('blur', function() {
if (document.activeElement === document.getElementById('google-sheets-2-loader')) {
$('#outer-layer-card').stop().fadeOut('fast');
}
removeEventListener('blur', iframeListener);
});
const iframeListener5 = addEventListener('blur', function() {
if (document.activeElement === document.getElementById('google-docs-3-loader')) {
$('#outer-layer-card').stop().fadeOut('fast');
}
removeEventListener('blur', iframeListener);
});
const iframeListener6 = addEventListener('blur', function() {
if (document.activeElement === document.getElementById('google-docs-4-loader')) {
$('#outer-layer-card').stop().fadeOut('fast');
}
removeEventListener('blur', iframeListener);
});
How can I trigger a function (to hide one specific div) whenever I click on any iframe?
Thanks in advance for any suggestions or help
Can save event listeners into a object and have a function to add them dynamically. That would mean to have some sort of html element which would have data-target attribute or similar. Additionaly can move the id_target to function parameter.
var iframe_listeners = [];
function add_iframe_event(){
const id_target = $('data-target element').data('target');
iframe_listeners[id_target] = addEventListener('blur', function() {
if (document.activeElement === document.getElementById(id_target)) {
$('#outer-layer-card').stop().fadeOut('fast');
}
removeEventListener('blur', iframe_listeners[id_target]);
});
}
Edit:
loop method
var iframe_listeners = [];
const ids = [ '1', '2' ];
for(const id of ids){
// skip if element doesnt exist
if($(`#${id}`).length == 0) continue;
add_iframe_event(id);
}
function add_iframe_event(id_target){
iframe_listeners[id_target] = addEventListener('blur', function() {
if (document.activeElement === document.getElementById(id_target)) {
$('#outer-layer-card').stop().fadeOut('fast');
}
removeEventListener('blur', iframe_listeners[id_target]);
});
}
I'm using flickity, which is a bit irrelevant, and on first load and for each 'change' of a slide I'll search the slide for any videos that have audio enabled (set as a data attribute via PHP via CMS) and then it'll autoplay the video and if the user clicks an unmute button then it'll unmute and vice versa.
This worked fine going forward but going back once the mute button is clicked, the eventListener for the click will fire every time it's existed. I'm guessing the eventListener is being added to each time but I can't work out how to remove the eventListener.
Any help on how to prevent the muteButton.addEventListener('click') from being fired more than once?
//
playVideo = function(index) {
var videos, video, muteButton = null, hasAudio = false;
// Pause all other slide video content if it was playing
flkty.cells.forEach(function(cell, i) {
videos = cell.element.querySelectorAll('video.--autoplay');
videos.forEach(function(video) {
if (video !== null && typeof video !== 'undefined') {
if (!video.paused) {
video.pause();
}
}
});
});
// For current slide
if (index == flkty.selectedIndex) {
videos = flkty.selectedElement.querySelectorAll('video.--autoplay');
muteButton = flkty.selectedElement.querySelector('a.button__mute');
// If videos exist on the current slide
if (videos.length) {
videos.forEach(function(video, index) {
if (video !== null && typeof video !== 'undefined') {
video.play();
if (muteButton !== null && typeof muteButton !== 'undefined' && index == 0) { // Only fire this once per video (as mute = mute all)
console.log(muteButton);
muteButton.addEventListener('click', function(e) {
e.preventDefault();
console.log('clicked'); // TOFIX; fires multiple times
muteVideo(videos, video, muteButton, true);
});
}
}
return;
});
}
}
};
flkty.on('select', function(event, index) {
if (index === 0) {
playVideo(index);
return false;
}
});
flkty.on('change', function(index) {
playVideo(index);
});
//
muteVideo = function(videos, video, muteButton, hasAudio) {
console.log('hasAudio');
if (videos.length > 1) {
videos.forEach(function(video, index) {
if (video.muted == true) {
video.muted = false;
if (index == 0) {
$(muteButton).text('mute');
}
} else {
video.muted = true;
if (index == 0) {
$(muteButton).text('unmute');
}
}
});
} else {
if (video.muted == true) {
$(muteButton).text('mute');
video.muted = false;
} else {
$(muteButton).text('unmute');
video.muted = true;
}
}
};
Just use removeEventListener().
To remove event handlers, the function specified with the addEventListener() method must be an external function.
Anonymous functions, like yours, will not work.
As for only attaching it once: Just set a flag to be checked before adding the eventhandler in the first place.
Don't use removeEventListener() in this case. The real problem is that you are adding event listeners each time your playVideo() function is called. So the problem you should solve is to make sure you only add the event listeners once, probably when the page initializes or something.
Here is what I would do:
Extract the "add event listener code" into a separate function, addButtonListeners() by removing that piece of code from the playVideo(). Then call the addButtonListeners() once when your page is loaded.
I'm trying to get a fancy box popup opening when the user click on "CONTACT" in the navigation menu. It works on JSFiddle, see http://jsfiddle.net/88X6D/1/ but for some reason it doesn't work in live environment, see http://goo.gl/lkfxeO (nothing happens when clicking on "contact" in the menu)
I initially thought there was a conflict between the "smooth scrolling" script and the "contact form" script but since it works on JSfiddle, the issue must be somewhere else. (also fancybox JS files and jquery are correctly called).
Thanks for your help
HTML
<li> Contact
</li>
SCRIPTS (located in this file: js/scripts.js)
//==============
//! Smooth scrolling
//==============
$(function () {
$('a[href*=#]:not([href=#])').click(function () {
if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname) {
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
if (target.length) {
$('html,body').animate({
scrollTop: target.offset().top - 100
}, 'normal');
return false;
}
}
});
})
window.onscroll = scrollFunction;
function scrollFunction() {
var doc = document.documentElement, body = document.body;
var top = (doc && doc.scrollTop || body && body.scrollTop || 0);
if (top > 200) {
$('.back-to-top').fadeIn();
}
else {
$('.back-to-top').fadeOut();
}
}
//==============
//! Contact form
//==============
function validateEmail(email) {
var reg = /^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return reg.test(email);
}
$(document).ready(function() {
$(".modalbox").fancybox();
$("#contact").submit(function() { return false; });
$("#send").on("click", function(){
var emailval = $("#email").val();
var msgval = $("#msg").val();
var msglen = msgval.length;
var mailvalid = validateEmail(emailval);
var nameval = $("#name").val();
if(mailvalid == false) {
$("#email").addClass("error");
}
else if(mailvalid == true){
$("#email").removeClass("error");
}
if(msglen < 4) {
$("#msg").addClass("error");
}
else if(msglen >= 4){
$("#msg").removeClass("error");
}
if(nameval < 2) {
//name must be at least 2 characters
$("#name").addClass("error");
}
else if(nameval >= 2){
$("#name").removeClass("error");
}
if(mailvalid == true && msglen >= 4) {
// if both validate we attempt to send the e-mail
// first we hide the submit btn so the user doesnt click twice
$("#send").replaceWith("<em>sending...</em>");
$.ajax({
type: 'POST',
url: '../sendmessage.php',
data: $("#contact").serialize(),
success: function(data) {
if(data == "true") {
$("#contact").fadeOut("fast", function(){
$(this).before("<p><strong>Success! Your message has been sent, thank you.</strong></p>");
setTimeout("$.fancybox.close()", 1000);
});
}
}
});
}
});
});
The problem is in your click handlers. Your 'contact' link ends up with two handlers:
One for scrolling (set up in your $('a[href*=#]:not([href=#])').click() call)
One for Fancybox (implicitly added by the call to $('.modalbox').fancybox()).
The scrolling click handler ends with return false. This stops all later click handlers running. Thus your scrolling click handler runs, but Fancybox's click handler doesn't - the scrolling click handler told the browser not to.
The scrolling click handler should have an ev.preventDefault() call instead. ev.preventDefault() stops the browser carrying out the "default" action (in this case, trying to follow the link), but doesn't prevent later click handlers running.
Here's an updated scroll handler that should get your Fancybox working:
$('a[href*=#]:not([href=#])').click(function (ev) { // Added 'ev' parameter
if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname) {
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
if (target.length) {
ev.preventDefault(); // We're animating this, so don't let the browser try to navigate to this URL
$('html,body').animate({
scrollTop: target.offset().top - 100
}, 'normal');
}
}
});
$(document).click(function(evt) {
var target = evt.currentTarget;
var inside = $(".menuWraper");
if (target != inside) {
alert("bleep");
}
});
I am trying to figure out how to make it so that if a user clicks outside of a certain div (menuWraper), it triggers an event.. I realized I can just make every click fire an event, then check if the clicked currentTarget is same as the object selected from $(".menuWraper"). However, this doesn't work, currentTarget is HTML object(?) and $(".menuWraper") is Object object? I am very confused.
Just have your menuWraper element call event.stopPropagation() so that its click event doesn't bubble up to the document.
Try it out: http://jsfiddle.net/Py7Mu/
$(document).click(function() {
alert('clicked outside');
});
$(".menuWraper").click(function(event) {
alert('clicked inside');
event.stopPropagation();
});
http://api.jquery.com/event.stopPropagation/
Alternatively, you could return false; instead of using event.stopPropagation();
if you have child elements like dropdown menus
$('html').click(function(e) {
//if clicked element is not your element and parents aren't your div
if (e.target.id != 'your-div-id' && $(e.target).parents('#your-div-id').length == 0) {
//do stuff
}
});
The most common application here is closing on clicking the document but not when it came from within that element, for this you want to stop the bubbling, like this:
$(".menuWrapper").click(function(e) {
e.stopPropagation(); //stops click event from reaching document
});
$(document).click(function() {
$(".menuWrapper").hide(); //click came from somewhere else
});
All were doing here is preventing the click from bubbling up (via event.stopPrpagation()) when it came from within a .menuWrapper element. If this didn't happen, the click came from somewhere else, and will by default make it's way up to document, if it gets there, we hide those .menuWrapper elements.
try these..
$(document).click(function(evt) {
var target = evt.target.className;
var inside = $(".menuWraper");
//alert($(target).html());
if ($.trim(target) != '') {
if ($("." + target) != inside) {
alert("bleep");
}
}
});
$(document).click((e) => {
if ($.contains($(".the-one-you-can-click-and-should-still-open").get(0), e.target)) {
} else {
this.onClose();
}
});
I know that the question has been answered, but I hope my solution helps other people.
stopPropagation caused problems in my case, because I needed the click event for something else. Moreover, not every element should cause the div to be closed when clicked.
My solution:
$(document).click(function(e) {
if (($(e.target).closest("#mydiv").attr("id") != "mydiv") &&
$(e.target).closest("#div-exception").attr("id") != "div-exception") {
alert("Clicked outside!");
}
});
http://jsfiddle.net/NLDu3/
I do not think document fires the click event. Try using the body element to capture the click event. Might need to check on that...
This code will open the menu in question, and will setup a click listener event. When triggered it will loop through the target id's parents until it finds the menu id. If it doesn't, it will hide the menu because the user has clicked outside the menu. I've tested it and it works.
function tog_alerts(){
if($('#Element').css('display') == 'none'){
$('#Element').show();
setTimeout(function () {
document.body.addEventListener('click', Close_Alerts, false);
}, 500);
}
}
function Close_Alerts(e){
var current = e.target;
var check = 0;
while (current.parentNode){
current = current.parentNode
if(current.id == 'Element'){
check = 1;
}
}
if(check == 0){
document.body.removeEventListener('click', Close_Alerts, false);
$('#Element').hide();
}
}
function handler(event) {
var target = $(event.target);
if (!target.is("div.menuWraper")) {
alert("outside");
}
}
$("#myPage").click(handler);
try this one
$(document).click(function(event) {
if(event.target.id === 'xxx' )
return false;
else {
// do some this here
}
});
var visibleNotification = false;
function open_notification() {
if (visibleNotification == false) {
$('.notification-panel').css('visibility', 'visible');
visibleNotification = true;
} else {
$('.notification-panel').css('visibility', 'hidden');
visibleNotification = false;
}
}
$(document).click(function (evt) {
var target = evt.target.className;
if(target!="fa fa-bell-o bell-notification")
{
var inside = $(".fa fa-bell-o bell-notification");
if ($.trim(target) != '') {
if ($("." + target) != inside) {
if (visibleNotification == true) {
$('.notification-panel').css('visibility', 'hidden');
visibleNotification = false;
}
}
}
}
});
I have created a form with malsup's Form Plugin wherein it submits on change of the inputs. I have set up my jQuery script to index drop down menus and visible inputs, and uses that index to determine whether keydown of tab should move focus to the next element or the first element, and likewise with shift+tab keydown. However, instead of moving focus to the first element from the last element on tab keydown like I would like it to, it moves focus to the second element. How can I change it to cycle focus to the actual first and last elements? Here is a live link to my form: http://www.presspound.org/calculator/ajax/sample.php. Thanks to anyone that tries to help. Here is my script:
$(document).ready(function() {
var options = {
target: '#c_main',
success: setFocus
};
$('#calculator').live('submit', function() {
$(this).ajaxSubmit(options);
return false;
});
$(this).focusin(function(event) {
var shiftDown = false;
$('input, select').each(function (i) {
$(this).data('initial', $(this).val());
});
$('input, select').keyup(function(event) {
if (event.keyCode==16) {
shiftDown = false;
$('#shiftCatch').val(shiftDown);
}
});
$('input, select').keydown(function(event) {
if (event.keyCode==16) {
shiftDown = true;
$('#shiftCatch').val(shiftDown);
}
if (event.keyCode==13) {
$('#captured').val(event.target.id);
} else if (event.keyCode==9 && shiftDown==false) {
return $(event.target).each(function() {
var fields = $(this).parents('form:eq(0),calculator').find('select, input:visible');
var index = fields.index(this);
var nextEl = fields.eq(index+1).attr('id');
var firstEl = fields.eq(0).attr('id');
var focusEl = '#'+firstEl;
if (index>-1 && (index+1)<fields.length) {
$('#captured').val(nextEl);
} else if(index+1>=fields.length) {
if ($(this).val() != $(this).data('initial')) {
$('#captured').val(firstEl);
} else {
event.preventDefault();
$(focusEl).focus();
}
}
return false;
});
} else if (event.keyCode==9 && shiftDown==true) {
return $(event.target).each(function() {
var fields = $(this).parents('form:eq(0),calculator').find('select, input:visible');
var index = fields.index(this);
var prevEl = fields.eq(index-1).attr('id');
var lastEl = fields.eq(fields.length-1).attr('id');
var focusEl = '#'+lastEl;
if (index<fields.length && (index-1)>-1) {
$('#captured').val(prevEl);
} else if (index==0) {
if ($(this).val() != $(this).data('initial')) {
$('#captured').val(lastEl);
} else {
event.preventDefault();
$(focusEl).select();
}
}
return false;
});
}
});
});
});
function setFocus() {
with (document.calculator)
var recap = document.getElementById(recaptured.value);
if (recap!=null) {
setTimeout(function() {
if (recap.getAttribute('type')=='text') {
recap.select();
} else {
recap.focus();
}
}, 100 );
}
}
Edit #1: I made a few minor changes to the code, which has brought me a little closer to my intended functionality of the script. However, I only made one change to the code pertaining to the focus: I tried to to disable the tab keydown when pressed on the last element (and also the shift+tab keydown on the first element) in an attempt to force the focus on the element I want without skipping over it like it has been doing. This is the code I added:
$(this).one('keydown', function (event) {
return !(event.keyCode==9 && shiftDown==true);
});
This kind of works. After the page loads, If the user presses tab on the last element without making a change to its value, the focus will be set to the second element. However, the second time the user presses tab on the last element without making a change to its value, and every subsequent time thereafter, the focus will be set to the first element, just as I would like it to.
Edit #2: I replaced the code in Edit #1, with code utilizing event.preventDefault(), which works better. While if a user does a shift+tab keydown when in the first element, the focus moves to the last element as it should. However, if the user continues to hold down the shift key and presses tab again, focus will be set back to the first element. And if the user continues to hold the shift key down still yet and hits tab, the focus will move back to the last element. The focus will shift back and forth between the first and last element until the user lifts the shift key. This problem does not occur when only pressing tab. Here is the new code snippet:
event.preventDefault();
$(focusEl).focus();
You have a lot of code I didn't get full overview over, so I don't know if I missed some functionality you wanted integrated, but for the tabbing/shift-tabbing through form elements, this should do the work:
var elements = $("#container :input:visible");
var n = elements.length;
elements
.keydown(function(event){
if (event.keyCode == 9) { //if tab
var currentIndex = elements.index(this);
var newIndex = event.shiftKey ? (currentIndex - 1) % n : (currentIndex + 1) % n;
var el = elements.eq(newIndex);
if (el.attr("type") == "text")
elements.eq(newIndex).select();
else
elements.eq(newIndex).focus();
event.preventDefault();
}
});
elements will be the jQuery object containing all the input fields, in my example it's all the input fields inside the div #container
Here's a demo: http://jsfiddle.net/rA3L9/
Here is the solution, which I couldn't have reached it without Simen's help. Thanks again, Simen.
$(document).ready(function() {
var options = {
target: '#c_main',
success: setFocus
};
$('#calculator').live('submit', function() {
$(this).ajaxSubmit(options);
return false;
});
$(this).focusin(function(event) {
$('#calculator :input:visible').each(function (i) {
$(this).data('initial', $(this).val());
});
return $(event.target).each(function() {
$('#c_main :input:visible').live(($.browser.opera ? 'keypress' : 'keydown'), function(event){
var elements = $("#calculator :input:visible");
var n = elements.length;
var currentIndex = elements.index(this);
if (event.keyCode == 13) { //if enter
var focusElement = elements.eq(currentIndex).attr('id');
$('#captured').val(focusElement);
} else if (event.keyCode == 9) { //if tab
var newIndex = event.shiftKey ? (currentIndex - 1) % n : (currentIndex + 1) % n;
var el = elements.eq(newIndex);
var focusElement = el.attr('id');
if ($(this).val() != $(this).data('initial')) {
$('#captured').val(focusElement);
} else if ((currentIndex==0 && event.shiftKey) || (currentIndex==n-1 && !event.shiftKey)) {
event.preventDefault();
if (el.attr('type')=='text') {
$.browser.msie ? "" : $(window).scrollTop(5000);
el.select().delay(800);
} else {
$.browser.msie ? "" : $(window).scrollTop(-5000);
el.focus().delay(800);
}
} else if (el.is('select')) {
event.preventDefault();
if (el.attr('type')=='text') {
el.select();
} else {
el.focus();
}
}
}
});
});
});
});
function setFocus() {
with (document.calculator)
var recap = document.getElementById(recaptured.value);
if (recap!=null) {
setTimeout(function() {
if (recap.getAttribute('type')=='text') {
recap.select();
} else {
recap.focus();
}
}, 1 );
}
}
I put my files available to download in my live link: http://www.presspound.org/calculator/ajax/sample.php