As an extension to my previous question and this answer (https://stackoverflow.com/a/31327605/1582712), I am trying to modulate my JavaScript code so I utilize the same double click functionality across other classes.
My code is in the following jfiddle
If you try double clicking the H3 or the table, they don't seem to come back. I believe the reason is because the $el variable is not being registered and/or remembered properly.
I have a tried a few things, such as returning the $el variable and reusing it. I tried using an array for $el[search] so each is unique. None worked. I'm a little stumped, I think its a subtle JavaScript memory issue here!
FYI:
If you run this jfiddle You'll see the double clicking works over and over again. I am hoping to get this same effect, but generalized to any html class/id in the DOM.
The problem is that you are not only hiding the elements but also removing them from the DOM (line 25 in your JSFiddle). After removing them you are also calling hide (line 17) and then toggle (line 18) but the elements do not exist to hide or toggle anymore. You can resolve the problem by removing $rowsToHide.hide(); (line 17) and $rowsToHide.remove(); (line 25). See this updated JSFiddle. This will hide the elements but not remove them from the DOM.
This is not right:
$('table#safe-distances-table').dblclick(function () {
handleDoubleClick('tr.no-impact');
});
function handleDoubleClick(search) {
var $el = $(this);
// rest of the code
}
Try to do this:
$('table#safe-distances-table').dblclick(function (evt) {
handleDoubleClick(evt.currentTarget);
});
function handleDoubleClick(el) {
var $el = $(el);
// rest of the code
}
You have to pass the clicked element, not a selector to search and modify... its not the same thing. Be careful becase this evt.currentTarget means the element the user clicked. Maybe you must capture the parent element (.parent()) in order to get all the data, but ALWAYS try to send the event... and then with currentTarget obtain the clicked element.
ENJOY!
Related
Question:
I am trying to build a dynamic form using jQuery. There are a few standard html form inputs in a row in addition to a CSS stylized "hidden checkbox" toggle button. (Example)
The first row is statically coded, and there is a button to add more rows to the form for batch submissions. JQuery applied to the .on(click) event works perfectly with the static content, but newly appended form rows ignore the binding. Even jsfiddle's dynamic display doesn't appear to function, though it is possible my fiddle example has a bug that my working copy doesn't have. (Fiddle)
/* activeToggle is the selector for the container and descendants,
// .toggleHappy is the hidden input element within activeToggle */
$(activeToggle).on("click", ".toggleHappy", function(e) {
e.preventDefault();
$(this).val(($(this).val() == 0) ? 1 : 0);
console.log($(this).val());
});
I have researched and found the common mistake of using .click() instead of delegating using .on(click, selector, fn()) and am thus using the latter now.
What is frustrating is I have another .on(click) that IS working with the dynamic content. (the remove function) So, I was hoping another pair of eyes might help me find out what my mistake is. I know this is very similar to other questions on the basic subject, but I have read quite a number of those discussions first, and have applied many of them to get where I currently am.
Updates:
I tried what Madhavan suggested and it does in fact work, but as expected, only for the first-child. So dynamically added rows are not pointed to. Thanks for the fast look. I feel like it might be a selector/scope issue, but whats weird is that once the page is loaded, I can type this directly into console and it works?
//this works run from console, but doesn't fire from a real click?
$(".theContainer .data .switch .toggleHappy").first().click();
ANSWER I have been working on another project in the interim and it is starting to look like .on(event, selector, fn) is not working at all for dynamically added items. But, I had a breakthrough! The other project was slightly simplified, and I found the following:
//.theContainer is static
//.data .switch .toggleHappy is the dynamic chain created
//does not delegate and bind dynamically
$(".theContainer").on("click", ".data .switch .toggleHappy", function() {});
//DOES work!
$(".theContainer").on("click", ".toggleHappy", function() {});
It would appear that a selector like .path .to .element only works well on existing static content, while .element allows .on() to be bound and delegated properly for dynamically generated nodes. See the updated fiddle.
The part that confused me was that hopping into the console and referencing dynamic elements with the full selector DID work on dynamic elements, but the events weren't delegated to them. Thanks again for the eyes that looked over this question, hope it helps someone else, because I still haven't found this on the web yet.
It appears that delegating jQuery events with .on() will only work on static content when the selector used within .on() is a list of consecutive elements. In my code, I had a single container which was static, I delegated an event to it referencing multiple elements between the container and the destination elements. It would work locally for any number of statically identified elements, but any dynamically added ones would ignore the bindings. It also would throw no errors, and it WOULD respond to lines of script executed within the console directly, using the same selector chaining as the function. I found the following:
//.theContainer is static
//.data .switch .toggleHappy is the dynamic chain created
//does not delegate and bind dynamically
$(".theContainer").on("click", ".data .switch .toggleHappy", function() {});
//DOES work!
$(".theContainer").on("click", ".toggleHappy", function() {});
It would appear that a selector like ".path .to .element" only works well on existing static content, while ".element" allows .on() to be bound and delegated properly for dynamically generated nodes. See the updated fiddle.
The part that confused me was that hopping into the console and referencing dynamic elements with the full selector DID work on dynamic elements, but the events weren't delegated to them. Thanks again for the eyes that looked over this question, hope it helps someone else, because I still haven't found this on the web yet.
$(activeToggle).on("click", function(e) {
e.preventDefault();
$(":first-child",this).val(($(":first-child",this).val() == 0) ? 1 : 0);
console.log($(":first-child",this).val());
});
I made a change like this and works fine. But I'm not sure why it is not triggering for '.toggleHappy' instead.
I wanted to get all elements that has a specific attribute and has a child of a specific type that is hidden. To do this, I wrote this selector:
$("*[custom-attr]:has('span:hidden')")
In addition to this, I wanted to add a live function on hover. For example like this:
$("*[custom-attr]:has('span:hidden')").live("hover", function() { /* do something */ });
I thought this was working properly, since I got the visual result I wanted. However, when using Firebug, I noticed that it was probably not perfect. When moving the mouse within the body, even if not hovering the elements that were meeting the selection, Firebug indicates that the HTML elements hovered are affected (they appear as yellow in Firebug HTML tab). See attached image:
Why is this and how can I avoid it?
I noticed that a similar selector did not cause the same behavior, like this one to get elements without the span child:
$("*[custom-attr]:not(:has('span'))").live("hover", function() { /* do something */ });
Thanks.
Try using natural browser selectors and that way avoid jQuery adding temporary attributes. For your case, instead of has() you can use:
$("[custom-attr]").on("mouseenter", function() {
if ( $(this).find('span:hidden').length ) {
/* your code */
};
});
Since you're getting the visual results you are expecting, it sounds like it's working. I think Firebug simply highlights elements that have recently had something change on itself or, if collapsed in the HTML view like on your screenshot, somewhere among its children. This highlighting will happen both when e.g. an attribute is change and when the change is reverted on e.g. mouseleave, and the highlighting will stay for some time after the change has occurred.
That said, it's also a good idea, as Kartikeya mentioned in a comment, to switch from .live(), which is deprecated, to .on().
I have implemented a user-generated keyword list for a project I'm working on, using jQueryUI autocomplete to suggest existing keywords.
On selecting the autocomplete suggestion, the returned string is added to the html of a div, as a child div.
I would like to add a removal function whereby the user can remove the child div if erroneously entered.
I've tried multiple suggested answers from Stackoverflow and elsewhere, but can't seem to get it working.
I've created a fiddle containing the pertinent elements.
The most logical solution to me was:
$('.keyword-entry').click(function(e){
var id = $(this).closest('div').prop('id');
$('#'+id).remove();
}
Though it would appear this doesn't work.
Whilst a solution to the problem would be very much appreciated to save my dwindling supply of coffee from running out this evening, I would also appreciate a rundown as to why I'm going wrong.
Thanks in advance.
Event delegation.
It's basically that you're attempting to attach an event to an DOM element that doesn't exist in the DOM at the time of load. Rewrite the .click() handler too:
$(document).on('click', '.trashYes', function () {
$(this).remove();
});
Fiddle: http://jsfiddle.net/6bBU4/
What it's doing is that, it's attaching the .click() event to the document (The top most DOM element) will travel down to find any new .trashYes, thus successfully executing the .remove(). This doesn't have to be bound to the document but to any DOM element within the document as well at load.
No need to get the id and then try and find it again, just do this...
$('<div id="'+id+'" class="keyword-entry" style="z-index:0">'+ui.item.value+' <--I want to remove this</div>')
.appendTo($('#keyword-list'))
.click(function(e){
$(this).remove();
});
when adding the keyword entry
I have two custom dropdown lists that have the same markup. I need to have only one show at a time. Right now, I'm able to open both at the same time. Both should also close when I click off the list.
The same markup for both lists is required, so I can't use unique ID's or additional classes to make this happen.
Here is a link to my fiddle example: http://jsfiddle.net/dg7Lc/29/
Any help would be greatly appreciated, thanks!
-D
Consider adding a data attribute such as 'active' via jquery when you click on one of them, then hide all those that have that attribute.
$('.custom-select').eq(0).hide() will hide the first one.
Use .show() instead of .hide() to show (obviously) and change the index to (1) to get the second one.
First thought would be if you could wrap a span or div around either or both and use that to get around the "same markup" limitation. Other than that, though, I'd suggest using order in page - use .next() and .prev() to get between them, and something like
$("div.custom-select").get(0)
or
$("div.custom-select").get(1)
to select them from outside.
edit: if you can run them off of something like an onmouseover, onchange, or whatnot, it's even easier - the one that's changing will be passed into the function as the "this" parameter. Just hide both, and show this, or show both and hide this.
edit2: similarly, once you have one of them hidden properly - well, that one will be hidden, and respond to the ":hidden" selector. Use that to distinguish between them (and save the distinction as a jquery variable) before you go showing or hiding anything else
Hide the first:
$('.custom-select').first().hide();
Hide the second:
$('.custom-select').last().hide();
And then put these lines of code where needed.
http://jsfiddle.net/dg7Lc/31/
Basically, closing the others:
$('.custom-select').not(this).find('ul').slideUp('fast');
And for closing when clicking outside the box, I used this piece of code but it's a bit dirty:
$("body").click(function(e) {
var should = true;
for($e = $(e.target); should && $e.length; $e = $e.parent()) {
should = !$e.is(".custom-select");
}
if(should) {
$('.custom-select').find('ul').slideUp('fast');
}
});
You can bind a click to the document, that looks to see if they clicked on the custom-select or the document outside it and hides any open lists as it should:
$(document).click(function(ev){
if(!$(ev.target).is('.custom-select span')){ $('.custom-select').find('ul').slideUp('fast'); }
});
Updated JSFiddle
I'm using the jquery-plugin qTip. What's the command to destroy all tooltips in my page ?
I tried:
$('.option img[title], span.taxonomy-image-link-alter img[title]').qtip("destroy");
But it didn't work...
Thanks
I've solved with $(".qtip").remove();
qTip2 is newer version of this script, but I would just like to point out 1 thing.
$(".qtip").remove();
This piece of code didn't destroy all the tooltips - it simply removed their containers. All the handlers and events attached to objects which invoked the tooltips are still avaiable in browser's memory.
In qTip to delete the tooltip and it's handler scompletely you would have to use:
$(mytooltip).qtip("destroy");
or
$(mytooltip).qtip('api').destroy();
In qTip2 however using this:
$(mytooltip).remove();
Would automaticaly call out the api and destroy tooltip and it's handlers completely.
$('.qtip').each(function(){
$(this).data('qtip').destroy();
})
qtip("destroy") is buggy (version 2.1.1) and doesn't clear everything.
I found this as a proper workaround:
// don't call destroy if not needed
if (element.data("qtip")) {
// the 'true' makes the difference
element.qtip("destroy",true);
// extra cleanup
element.removeData("hasqtip");
element.removeAttr("data-hasqtip");
}
Looks buggy. I've had some luck with this, but it does not restore the original titles. I suspect destroy doesn't do that either...
$('span.taxonomy-image-link-alter img')
.filter(function(){return $(this).data('qtip');})
.qtip('destroy');
It seems you cannot call destroy on elements without qTip - it doesn't fail silently, but throws an exception and stops the loop.
I experienced that the api-call
$(selector).qtip('destroy')
doesn't remove all qtip-data dependably, especially when using several qtips simultaneously.
In my case I had to remove a visible qtip and successfully used this workaround:
$(selector).removeData('qtip');
$('.qtip :visible').remove();
What about:
$('[data-hasqtip]').qtip('destroy', true);
Seems to be working with qTip2 version 3.0.2.
if ( jQuery( '.qtip' ).length > 0 )
{
jQuery( "#IdElement").qtip("destroy");
}
None of these answers helped me.
In my case, I had a qtip on an element with a close button. The close button removed the element, so there was no reference point to remove the qtip after the element was removed.
I thought $('.qtip:visible').remove() would work, but it somehow removed all of the qtips on the page, and not the single one that I wanted removed.
I noticed that the visible qtip is given a class qtip-active, so what worked for me was:
$('.qtip-active').remove();
It may be a little late, but I had issues with memory and page load when an ajax call replace the content in the page, deleting the target qtip2 objects before destroy them, so some elements remains even if the target had gone.
Based on the fact that sometimes you want to clean all qtips2 elements and data, no matter if the original object exist or not, some tooltip elements remains on the body, so when the original target has gone there is no easy way to call the destroy() method.
Unless you do it searching for the created objects instead of the targets.
jQuery('div[id^="qtip-"]').each(function(){ //search for remaining objects
_qtip2 = jQuery(this).data("qtip"); //access the data where destroy() exist.
//if it's a proper qtip2 object then call the destroy method.
if(_qtip2 != undefined){
// the "true" is for immediate destroy
_qtip2.destroy(true);
}
//if everything went right the data and the remaining objects in the body must be gone.
});
I used JQuery for a no conflict issue, but you can use "$" (symbol) instead of JQuery