Using AJAX, I am populating a container with the id of logos-page-main-content-wrapper (assigned to a variable called mainContent) on a WordPress page with several levels of descendants; the important (images) descendants have the class medallion-image.
The following jQuery function performs differently than I expect:
jQuery(document).ready(function($){
var mainContent = $('#logos-page-main-content-wrapper');
var medallionImage = $('.medallion-image');
var combinedMarkImage = $('.combined-mark-image');
var monogramImage = $('.monogram-image');
var wordmarkImage = $('wordmark-image');
mainContent.on('click', medallionImage,function(){
jQuery.ajax({
url: ajax_object.ajaxurl,
data: {
action: 'validateUser',
security: ajax_object.ajax_nonce,
current_post_id: ajax_object.current_post_id
},
method: "POST",
success: function(data){
console.log(data);
if(data != true){
window.location = window.location.hostname + '/login';
}
}
});
});
});
The idea is that when a user clicks on the image with the class of medallion-image they will be redirected to a login page if they aren't currently logged into WordPress. The AJAX call works properly, but the .on() function responds to a click on any descendant of mainContent instead of just clicks on the medallion-image images. As I understand it, this shouldn't be happening; only the medallion-image class should respond to clicks.
I've scoured the documentation for .on on the jQuery API site but haven't figured out why it's behaving this way. Any ideas?
This is actually the correct behaviour because jQuery will consider .medallion-image clicked even though you clicked on child elements. This works well because imagine if you have a table of content and you bind .on to the row. Instead building a bunch of child selectors of different elements in the row it triggers based on the fact that you interacted with the row. Here is the behaviour similar to the "issue" you're having:
https://jsfiddle.net/zLfqoj65/
A method to do it so it only executes the function on the parent and only the parent element is to do a check in your .on to ensure that this is the element you want with:
$('.outer').click(function(e) {
if ($(e.target).hasClass('outer')) {
// Execute code.
}
});
This will check to see if this element you clicked on has the exact class you want. Example here:
https://jsfiddle.net/zLfqoj65/2/
You can do prevent capturing the click on mainContent using CSS pointer-events property as:
pointer-events: none
and in the medallion-image turn it back on using:
pointer-events: auto
Related
I have a table with live search on it. There is a link in this table that opens a modal where some changes can be made. The modal works when no search is made. But when searching, emptying the table contents, I print the data coming to ajax to the table. Unfortunately, the link that opens modal, does not open modal
This is the link I printed with jQuery:
<a class="show-modal btn btn-warning btn-sm interview-add" customer-id="'.$data->customer_id.'" href="javascript:void(0)"><i class="fas fa-plus text-white"></i></a>
This is the jQuery code:
$('.interview-add').click(function (e) {
e.preventDefault();
var customerId = $(this)[0].getAttribute('customer-id');
$('#interview-customer').val(customerId);
$('#interview-add').modal('show');
})
This is the code where I printed the incoming data:
$('#search-button').click(function (e) {
e.preventDefault();
var searching = $("#search").val();
if(searching === '') {
alert('');
}else{
$.ajax({
url: "{{route('customerSearch')}}",
type: "POST",
data: {
searching: searching
},
success: function (data) {
$("#customers-table tbody").empty();
$(".pagination").remove();
$("#customers-table tbody").html(data);
}
});
}
});
If I understand your question correctly, the problem is that $('.interview-add').click(...) only applies the click handler to elements with the interview-add class that already exist at the time that you do that. Any expression of the form $('<some CSS selector>') performs a search for matching elements at that time, and then anything you do with the result of the search is only applied to those elements. So that would explain why this stops working for you if you dynamically repopulate the table with new elements.
The way to make this work is to use on instead. This is a method that you call on the container element that will contain the elements you're looking for (which could be either your table, or just $(document) for the page in general), and you give it a selector, an event name, and an event handler. Then, whenever that event happens to any element within that container, it will check whether the element matches the selector and if so, it calls the handler.
So, try this:
$(document).on('click', '.interview-add', function (e) {
e.preventDefault();
var customerId = $(this)[0].getAttribute('customer-id');
$('#interview-customer').val(customerId);
$('#interview-add').modal('show');
});
I used $(document) there because in your original code, you weren't restricting your selector to just .interview-add things within a specific part of the page. If you're not going to use that class for anything outside of the table then that's fine, but if you want to make it more specific you could replace $(document) with for instance $('#id-of-my-table') if the table has a unique ID.
The .click() event doesn't work with dynamically added elements. For this you have to use .on('click', function(){}).
There is a really good answer on "Difference between .on('click') vs .click()", that points out some differences between those two.
I'm using the following jquery code in my page:
jQuery(window).load(function(){
jQuery('#narrow-by-list dd > ol.filter_list').each(function(){
var FormHeight = jQuery(this).outerHeight();
if(FormHeight > 70){
jQuery(this).next('.layer_nav_more').css("display", "inline-block");
jQuery(this).height(70).css("display", "block");
}else{
jQuery(this).height(70).css("display", "block");
}
});
jQuery(".layer_nav_more").click(function(){
jQuery(this).prev('.filter_list').animate({ height:205 }, 500, function() {
jQuery(this).addClass("scrollable");
});
});
});
The page also uses ajax calls to update it's content, so after content is refreshed the jquery code is ignored. I don;t think that posting the full js file which handles ajax will help you. I guess that the following lines should be quite ok for you to understand what's going on:
requestUrl = document.location.href
if (requestUrl.indexOf('#') >= 0) {
var requestUrl = requestUrl.substring(0,requestUrl.indexOf('#'));
}
if (requestUrl.indexOf('?') >= 0) {
requestUrl = requestUrl.replace('?', '?no_cache=true&');
} else {
requestUrl = requestUrl + '?no_cache=true';
}
requestUrl = this.replaceToolbarParams(requestUrl);
this.showLoading();
new Ajax.Request(requestUrl, {
method : 'post',
parameters : parameters,
onSuccess: this.onSuccessSend.bindAsEventListener(this),
onFailure: this.onFailureSend.bindAsEventListener(this)
});
What can I do to fix this?
EDIT:
I changed the code based on David's recommendations
jQuery(window).load(function(){
function adjust_list_height(){
jQuery('#narrow-by-list dd > ol.filter_list').each(function(){
var FormHeight = jQuery(this).outerHeight();
if(FormHeight > 70){
jQuery(this).next('.layer_nav_more').css("display", "inline-block");
jQuery(this).height(70).css("display", "block");
}else{
jQuery(this).height(70).css("display", "block");
}
});
}
adjust_list_height();
jQuery(document).on('click', '.layer_nav_more', function(){
jQuery(this).prev('.filter_list').animate({ height:205 }, 500, function() {
jQuery(this).addClass("scrollable");
});
});
});
so after content is refreshed the jquery code is ignored
No it isn't. It's not going to be automatically re-invoked, clearly, but why should it be? The handler you posted is for the window's load event. Unless you're loading the window again, I wouldn't expect the code to execute again.
It sounds like the problem is that you're adding new elements to the page after you've added click handlers to existing elements. Keep in mind that handlers are attached to elements, not to selectors. So if a particular element doesn't exist when you execute this code, it's not going to get a click handler.
The standard approach to this is to defer handling click events to parent elements. Any common parent element will do, as long as it's not removed/replaced during the life of the page. document is often used for this, but any parent div or anything like that would work just as well. Something like this:
jQuery(document).on('click', '.layer_nav_more', function(){
//...
});
What this does is attach the actual click handler to document instead of to the matching .layer_nav_more elements. When any element invokes a click, that event will propagate upwards through the parent elements and invoke any click handlers on them. When it gets to this handler on the document, jQuery will filter for the originating element using that second selector. So this will effectively handle any clicks from .layer_nav_more elements.
Any other functionality that you need to invoke when the page content changes (functionality besides delegate-able event handlers) would need to be re-invoked when you logically need to do so. For example, executing .each() over a series of elements like you're doing. There's no way to "defer" that, so you'd want to encapsulate it within a function of its own and simply execute that function whenever you need to re-invoke that logic.
I was running some test popups by putting them into the HTML file manually and the JS function that I had to close them worked fine. However, when I add the popups dynamically, the closing function breaks and they are unable to be removed.
Here is the JS function that tells all popups in the .popup class to close when the .close button is clicked. The code also contains a hover function to switch the images out for the close button when the user hovers over it, that is also broken.
$('.popup').on('click', '.close', function() {
$(this).closest('.popup').remove(); //or .hide() if you just want to hide the popup
});
$('img.close').hover(function () {
this.src = '/engine/themes/img/popup.close.hover.png';
}, function () {
this.src = '/engine/themes/img/popup.close.idle.png';
});
And here is my method of adding it to the DOM
var popupID = 'popup1';
// Create popup div
var popupHTML = '<div id="'+popupID+'" class="popup">'+
'<div class="toolbar"><div id="title">Please Wait</div>'+
'<img class="close" src="/engine/themes/img/popup.close.idle.png" alt="Close" title="Close" />'+
'</div><p class="text">Loading...<p></div>';
$('body').append(popupHTML);
$.ajax({
url: pageURL,
cache: false,
success: function(data) {
var matches, pageTitle;
matches = data.match(/<title>(.*?)<\/title>/);
pageTitle = 'MERKD.COM';
if ( typeof matches !== 'undefined' && matches != null ) {
pageTitle = matches[1];
}
$('#'+popupID).html(strReplace('Loading...', data, $('#'+popupID).html()));
$('#'+popupID).html(strReplace('Please Wait', pageTitle, $('#'+popupID).html()));
} // end success call
}); // end ajax function
Note at the bottom I use a manually-written replacing method instead of just using $('#popup1 p').html('some text'); because when I do it that, it shows the text retrieved in data twice, anyone know why that is?
I'm lost as to why this stopped working when I added the popups dynamically, but surely I'm just overlooking something, because I also can't figure out why the text retrieved in data is displayed twice when I do a regular .html() or .text() jQuery call.
Try this:
var hovered;
$(document).on('mouseenter','img.close',function () {
hovered = this;
this.src = '/engine/themes/img/popup.close.hover.png';
});
$(document).on('mouseleave','img.close',function () {
hovered.src = '/engine/themes/img/popup.close.idle.png';
});
Event handlers are bound only to the selected elements and they must exist already on the page at the time the code loads. Otherwise, to correct that, you can use event delegation and add the event handler to "something" that was already there, document is always a safe card.
Read more about .on()
EDIT:
I corrected my code, I actually realized that .on() and hover don't work together, so I adapted to a mouseenter & mouseleave instead. You can not use .hover() here anyway since you need to use delegation.
From jQuery's documentation:
Deprecated in jQuery 1.8, removed in 1.9: The name "hover" used as a shorthand for the string "mouseenter mouseleave". It attaches a single event handler for those two events, and the handler must examine event.type to determine whether the event is mouseenter or mouseleave. Do not confuse the "hover" pseudo-event-name with the .hover() method, which accepts one or two functions.
I have a set of jQuery UI AJAX tabs that load individual .php pages when they are clicked. All of my styling, etc. conveys, because the pages that hold the tabs widget provide the already linked CSS, and scripts. When it comes to the actual pages that load when clicking on the tabs however, I can't seen to get preventDefault() to work with .on() on these newly created DOM elements.
I'm using jQuery BBQ with my tabs so I can't have "#"s being appended to the URL. This is caused when links within the tab panels are clicked.
I've been able to successfully use preventDefault() on DOM elements that are initially loaded, but not ones that are being fetched into the tabs widget via AJAX.
My function for a content toggler is...
$(function(){
$(".showMoreOrLess").on('click', (function() {
if (this.className.indexOf('clicked') != -1 ) {
$(this).removeClass('clicked');
$(this).prev().slideUp(500);
$(this).html("Read More" + "<span class='moreUiIcon'></span>");
}
else {
$(this).addClass('clicked');
$(this).prev().slideDown(500);
$(this).html("See Less" + "<span class='lessUiIcon'></span>");
}
}));
});
I'd like to combine the preventDefault() from this function into it.
// prevents default link behavior on BBQ history stated tab panels with "showMoreOrLess" links
$(".showMoreOrLess").click(function (event)
{
event.preventDefault();
//here you can also do all sort of things
});
// /prevents default behavior on "showMoreOrLess" links
I've tried several ways using .on("click", function(work)), etc. I've used .on() in a separate function and also tried to combine it in the first function above. Does anyone know what I'm doing wrong? The code works on tab content that is static, just not content loaded via AJAX. Any help would be greatly appreciated. Can't seem to figure this out. Thanks in advance.
the part $(".showMoreOrLess").click just applies to already accessable links on your page
try to use event delegation (here the clicks are captured on an every time existing element and you just pass the selector it is watching for... as a nice side effect you save listeners
$(document).on("click", ".showMoreOrLess", function(event) {
event.preventDefault();
//here you can also do all sort of things
});
rather than document use a certain id from your page $("#myContainerId") (EDIT: of course the elements you are clicking on need to be inside of the element with the id)
$("body").on('click', ".showMoreOrLess", function(event) {
event.preventDefault();
var self = $(this);
if (self.hasClass('clicked')) {
self.html("Read More" + "<span class='moreUiIcon'></span>").removeClass('clicked').prev().slideUp(500);
}else {
self.html("See Less" + "<span class='lessUiIcon'></span>").addClass('clicked').prev().slideDown(500);
}
});
I'd like to use this lightbox plugin for some autocomplete links, that don't yet exist on my page.
You normally activate it using:
$(document).ready(function($) {
$('a[rel*=facebox]').facebox()
})
Since the a links aren't all on the page upon page load, I would normally look to the .live or .delegate methods to bind to an event, but in this case, what 'event' would I bind to to say "once this element is on the page, then call this method on it".
Or am I going about this totally the wrong way?
There is no such event.
You need to invoke the plugin when you add the elements to the page.
// create a new <a>, append it, and call the plugin against it.
$('<a>',{rel:"facebox"}).appendTo('body').facebox();
This example creates a new <a> element. If you're getting some elements from an AJAX response, call it against those:
var elems = $( response );
elems.filter( 'a[rel="facebox"]' ).facebox(); // if the <a> is at the top level
elems.find( 'a[rel="facebox"]' ).facebox(); // if the <a> is nested
elems.appendTo('body');
Not yet tested :
$(document).ready(function($) {
$(document).bind('change', docChanged) ;
})
function docChanged()
{
if ($('a[rel*=facebox][class!="faceboxed"]').length > 0)
{
$('a[rel*=facebox][class!="faceboxed"]').addClass("faceboxed").facebox();
}
}
This is entirely possible using the .live function. You just need to use the DOMNodeInserted event.
$(document).ready(function() {
$("a[rel*=facebox]").live("DOMNodeInserted", function() {
$(this).facebox();
});
});
You'll need to just add this call to the ajax that loads in the links.