Javascript Popup won't close after adding it dynamically - javascript

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.

Related

Link created with jquery does not run modal

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.

How can I return a href link to it's default behavior after using e.preventDefault();?

I updated my code and rephrased my question:
I am trying to create the following condition. When a link with an empty href attribute (for example href="") is clicked, a modal is launched and the default behavior of that link is prevented..
But when the href attribute contains a value (href="www.something.com") I would like for the link to work as it normally does using its default behavior.
For some reason my code isn't working. Any help is appreciated.
// Semicolon (;) to ensure closing of earlier scripting
// Encapsulation
// $ is assigned to jQuery
;(function($) {
// DOM Ready
$(function() {
// Binding a click event
// From jQuery v.1.7.0 use .on() instead of .bind()
$('.launch').bind('click', function(e) {
var attrId = $(this).attr('attrId');
if( $('.launch').attr('href') == '') {
// Prevents the default action to be triggered.
e.preventDefault();
// Triggering bPopup when click event is fired
$('div[attrId="' + attrId+'"]').bPopup({
//position: ['auto', 'auto'], //x, y
appendTo: 'body'
});
} else {
$(this).removeAttribute('attrId');
return true;
}
});
});
})(jQuery);
Your jQuery is... very wrong. $('href').attr('') is getting the empty attribute from an element <href>... Did you mean:
if( $(this).attr('href') == '')
A few things: event is undefined, as your event object is simply called e. Secondly, e.stopPropagation(); will not do what you want. stopPropagation simply prevents parent events from firing (eg: clicking a <td> will also fire click on the containing <tr> unless stopped).
Try just replacing your else statement with
return true;
Also your jQuery is incorrect (as stated in the other answer here).
This answer may help as well:
How to trigger an event after using event.preventDefault()
Good luck!

Jquery window.load function and Ajax call

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.

jQuery: Get reference to click event and trigger it later?

I want to wrap an existing click event in some extra code.
Basically I have a multi part form in an accordion and I want to trigger validation on the accordion header click. The accordion code is used elsewhere and I don't want to change it.
Here's what I've tried:
//Take the click events off the accordion elements and wrap them to trigger validation
$('.accordion h1').each(function (index, value) {
var currentAccordion = $(value);
//Get reference to original click
var originalClick = currentAccordion.click;
//unbind original click
currentAccordion.unbind('click');
//bind new event
currentAccordion.click(function () {
//Trigger validation
if ($('#aspnetForm').valid()) {
current = parseInt($(this).next().find('.calculate-step').attr('data-step'));
//Call original click.
originalClick();
}
});
});
jQuery throws an error because it's trying to do this.trigger inside the originalClick function and I don't think this is what jQuery expects it to be.
EDIT: Updated code. This works but it is a bit ugly!
//Take the click events off the accordion elements and wrap them to trigger validation
$('.accordion h1').each(function (index, value) {
var currentAccordion = $(value);
var originalClick = currentAccordion.data("events")['click'][0].handler;
currentAccordion.unbind('click');
currentAccordion.click(function (e) {
if ($('#aspnetForm').valid()) {
current = parseInt($(this).next().find('.calculate-step').attr('data-step'));
$.proxy(originalClick, currentAccordion)(e);
}
});
});
I think this:
var originalClick = currentAccordion.click;
Isn't actually doing what you think it is - you're capturing a reference to the jQuery click function, rather than event handler you added, so when you call originalClick() it's equivalent to: $(value).click()
I finally came up with something reliable:
$(".remove").each(function(){
// get all our click events and store them
var x = $._data($(this)[0], "events");
var y = {}
for(i in x.click)
{
if(x.click[i].handler)
{
y[i] = x.click[i].handler;
}
}
// stop our click event from running
$(this).off("click")
// re-add our click event with a confirmation
$(this).click(function(){
if(confirm("Are you sure?"))
{
// if they click yes, run click events!
for(i in y)
{
y[i]()
}
return true;
}
// if they click cancel, return false
return false;
})
})
This may seem a bit weird (why do we store the click events in the variable "y"?)
Originally I tried to run the handlers in x.click, but they seem to be destroyed when we call .off("click"). Creating a copy of the handlers in a separate variable "y" worked. Sorry I don't have an in depth explanation, but I believe the .off("click") method removes the click event from our document, along with the handlers.
http://www.frankforte.ca/blog/32/unbind-a-click-event-store-it-and-re-add-the-event-later-with-jquery/
I'm not a jQuery user, but in Javascript, you can set the context of the this keyword.
In jQuery, you use the $.proxy() method to do this.
$.proxy(originalClick, value);
originalClick();
Personally, I'd look at creating callback hooks in your Accordion, or making use of existing callbacks (if they exist) that trigger when opening or closing an accordion pane.
Hope that helps :)
currentAccordion.click is a jQuery function, not the actual event.
Starting with a brute-force approach, what you'd need to do is:
Save references to all the currently bound handlers
Unbind them
Add your own handler, and fire the saved ones when needed
Make sure new handlers bound to click are catched too
This looks like a job for an event filter plugin, but I couldn't find one. If the last point is not required in your application, then it's a bit simpler.
Edit: After some research, the bindIf function shown here looks to be what you'd need (or at least give a general direction)

Issue with selectors & .html() in jquery?

The function associated with the selector stops working when I replace it's contents using .html(). Since I cannot post my original code I've created an example to show what I mean...
Jquery
$(document).ready(function () {
$("#pg_display span").click(function () {
var pageno = $(this).attr("id");
alert(pageno);
var data = "<span id='page1'>1</span><span id='page2'> 2</span><span id='page3'> 3</span>";
$("#pg_display").html(data);
});
});
HTML
<div id="pg_display">
<span id="page1">1</span>
<span id="page2">2</span>
<span id="page3">3</span>
</div>
Is there any way to fix this??...Thanks
Not sure I understand you completely, but if you're asking why .click() functions aren't working on spans that are added later, you'll need to use .live(),
$("#someSelector span").live("click", function(){
# do stuff to spans currently existing
# and those that will exist in the future
});
This will add functionality to any element currently on the page, and any element that is later created. It keeps you have having to re-attach handlers when new elements are created.
You have to re-bind the event after you replace the HTML, because the original DOM element will have disappeared. To allow this, you have to create a named function instead of an anonymous function:
function pgClick() {
var pageno = $(this).attr("id");
alert(pageno);
var data="<span id='page1'>1</span><span id='page2'> 2</span><span id='page3'> 3</span>";
$("#pg_display").html(data);
$("#pg_display span").click(pgClick);
}
$(document).ready(function(){
$("#pg_display span").click(pgClick);
});
That's to be expected, since the DOM elements that had your click handler attached have been replaced with new ones.
The easiest remedy is to use 1.3's new "live" events.
In your situation, you can use 'Event delegation' concept and get it to work.
Event delegation uses the fact that an event generated on a element will keep bubbling up to its parent unless there are no more parents. So instead of binding click event to span, you will find the click event on your #pg_display div.
$(document).ready(
function()
{
$("#pg_display").click(
function(ev)
{
//As we are binding click event to the DIV, we need to find out the
//'target' which was clicked.
var target = $(ev.target);
//If it's not span, don't do anything.
if(!target.is('span'))
return;
alert('page #' + ev.target.id);
var data="<span id='page1'>1</span><span id='page2'>2</span><span id='page3'>3</span>";
$("#pg_display").html(data);
}
);
}
);
Working demo: http://jsbin.com/imuye
Code: http://jsbin.com/imuye/edit
The above code has additional advantage that instead of binding 3 event handlers, it only binds one.
Use the $("#pg_display span").live('click', function....) method instead of .click. Live (available in JQuery 1.3.2) will bind to existing and FUTURE matches whereas the click (as well as .bind) function is only being bound to existing objects and not any new ones. You'll also need (maybe?) to separate the data from the function or you will always add new span tags on each click.
http://docs.jquery.com/Events/live#typefn

Categories

Resources