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.
Related
I'm currently designing a webpage presenting a twitter-like user input that generates an <li> (inside a <ul>) element in which are appended one <h6> element (the post's title) and a <p> element underneath (the content).This works, therefore the input and generation of elements is not the problem.
But what I want to do is use jQuery to hide the posts's content, and toggle it when I click on the post's title. The issue is that the event handler seems to work only for every second post. Whenever i post once more, the 1st post on the list can be toggled, the second not, third yes, etc.
From what I've seen in some answers, I've tried the .click() method, the .on() method, I've tried to replace .toggle() with.hide() and .show() under conditionals, then created a class with display:none to toggle on click. This was my last stop, and the result is described in the above paragraph. Here's the event handler:
$('.postinstance').on("click", "h6.postname", function() {
$(this).siblings().toggleClass('postOff');
});
The .siblings() is actually only the post content, but that's the only way I could get near what I wanted. When I replace $(this).siblings() with the actual class of the content element, every post's content toggles when I click on any title.
How can I make each post open individually when I click on it?
Here's a JSFiddle isolating the input & posts part.
I have looked thoroughly in Stack Overflow and other places, even tutorials, to solve this problem but although similar questions were found none of their answers provided a solution.
You should not attach event handlers to dynamically generated elements directly, instead use some common parent element. Here's a piece of your snippet where I changed the selector and everything started working:
$('.postlist').on("click", "h6.postname", function() {
$(this).siblings().toggleClass('postOff');
});
Important note: you must pull this piece of code out from $('.postbtn').click(..) one level up, otherwise for even number of posts toggling will not work!
And move this out of click handler:
$('.postlist').on("click", "h6.postname", function() {
console.log(this);
$(this).siblings().toggleClass('postOff');
});
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 got following problem: I generate a div with "jQuery-Load" links. Theese links inside the div should reload the same div with different parameters. I found a working solution, which generates theese links, which are clickable and... ...trigger the chosen event once. So clicking the same link inside the generated div, after it has been regenerated, doesnt work anymore. Tried a lot of things...
It looks like that now:
click
<div id="aaa0"> I'm the div - level1! </div>
div gets filled - beautyful.
It now contains this: (actually its generated what is why wrote [time] wich is time(); generated in php. as a changing parameter
[...] Link inside Updated Div [...]
when i click the link inside the div it works. when i click it again, it wont...
I want to generate a nice 'click deeper inside the data'-thing, which would be amazing getting this thing work and is the reason why everything must be as best as possible inside the "onclick" event :|
Sorry btw. for the a bit confusing post-style, its a confusing topic, and im not native speaking :)
Thanks for any help or hint in advance,
Harry
Maybe you're missing the concepts between bind and live. In bind, jQuery scans the document and attach a function direct to the element. In live, jQuery attach the function to the document, along with the event and the element as parameters. Once a event bubbles, jQuery check the event and the element, and if it match, then a function executes.
After the first run, the dom has changed, and its gonna work using live.
something like that should work:
click
<div id="aaa0"> I'm the div - level1! </div>
<script>
$('a').live('click', function (e) {
e.preventDefault();
var id = this.id;
$(this).next('div').load('getdetails.php?fNumber=36&env=fun&id=' + id);
});
</script>
basically, what is done is a generic rule, which gives all tags the same behavior. (load next div content). ".live()" is used so that loaded tags work (check the jquery documentation for .live(), or event delegation in general).
I'm not certain about the preventDefault stuff. You might want to use somehting else than tag for the link.
click
made the day :) I don't know exactly why, but maybe its possible preventDefault made the bind and live thing for me. Its working fine, so ...
thanks for the hints! :D
I am using Flowplayer's jQuery Tools framework (specifically the tooltips plugin) in a table, in addition to jQuery UI calendar.
Each row of the table has the ability to insert a row above and below it.
When doing this I am cloning the clicked object (events and objects) and inserting it directly above or below.
After adding a new row, I refresh the table, generating new id's for my elements, reinitializing the datepicker, and attempting to reinitialize the tooltip.
I am searching for a way to destroy it altogether from the instance and reapply it.
I am looking for something similar to the datepicker('destroy') method.
$j($editRow).find('input.date').datepicker('destroy').datepicker({dateFormat: 'mm-dd-yy', defaultDate : defaultDateStr});
I have already attempted to :
to unbind the mouseover and focus events : when reinvoking tooltip, it automatically goes for the object it was made from.
hide the tooltip DOM element, remove the tooltip object from the target, and reapply it. The same thing happens as (1)
Is there way I can create a destroy method myself?
I tried kwicher's method, and could not get it to work. Although I was trying to alter the minified code, and I'm not entirely sure I found the right place to make the change.
I did, however, get this to work. ValidationFailed is a class I am applying to all the input fields that are also having tooltips applied to them.
$('.validationFailed').each(function (index) {
$(this).removeData('tooltip');
});
$('.tooltip').remove();
I tried this several different ways, and this was the only combination that allowed me to add additional tool tips post-removal, without preventing the new tooltips from working properly.
As best I can tell, the tooltip class removal handles getting rid of the actual tooltip divs, whose events are also wired up directly. The .removeData allows the tooltip to be re-bound in the future.
Building on your ideas and Tom's answer, I found that three things are necessary:
remove the 'tooltip' data from the tooltip target element
unbind event listeners from the tooltip target element
remove the element (assuming you're using this tooltip approach to allow for arbitrary HTML in your tip)
So, this simple function should do the trick. Just pass it a jQuery object containing the target elements of the tooltips you want to destroy.
function destroyTooltips($targets) {
$targets.removeData('tooltip').unbind().next('div.tooltip').remove();
}
If you still need it you can do as follows:
- in the tooltip implementation file add the following function
destroy: function(e) {
tip.detach();
}
somewhere in :
$.extend(self, {
...
I have after the last native function.
Then when you want to remove the tip, fire:
$(.tip).data('tooltip').destroy();
It should do the trick
K
if($element.data('ui-tooltip')) {//if tooltip has been initialized
$element.tooltip('destroy');
}
Maybe it's too late... ;-)
But here you can find all methods of 'tooltip': http://www.w3schools.com/bootstrap/bootstrap_ref_js_tooltip.asp
I leave the tip for someone who could pass by, having the same problem: dealing with different 'tooltip' actions/objs.
I am having a dilemma in the logic of this particular issue. Forgive me if this is quite newbie question but I'd rather have a solid bg on it.
There are a lot of examples of this all around the web where you click on an element to display another element. such case may be a menu that when you hover your mouse on it (or click on it) its get displayed. Later the element gets hidden either on mouse out, OR CLICKING ON ANY OTHER ELEMENT.. so, how is this achieved? I am sure the solution is not to bind a "hideElem" function on all the elements.
regards,
I haven't done it in a while, but an easy solution is to add a click event to the top of the DOM tree that will close the open element. Here's an example in psuedo-javascript:
document.body.onclick = function() {
element.style.display = "none";
}
If you need complex behaviors inside the "shown" element, make sure your preventing the necessary events from propagating up the DOM tree.
element.onclick = function(e) {
e.stopPropagation()
}
In general, the logic is the other way around (at least with menus) i.e. the element in question is hidden until a state-event unhides it, then hidden again as dictated. The point being that the hiding/unhiding logic is usually tied to the element itself, not everything else on the page.
As to how it's done, methods vary. There are lots of Javascript solutions, mostly along the lines of those already outlined, but menus can also be done purely with CSS - typically utilising the display: none; property, though you can also do stuff like setting/unsetting a negative margin so that the element is moved 'off and on the page'.
To use some of my own work by way of example:
Drop-down menu with Javascript
Drop-down menu with jQuery
Drop-down menu with CSS
$('#target').bind('click', function(event)
{
var $element = $('#element');
$element.show();
$(document).one('click', function()
{
$element.hide();
});
// If you don't stop the event, it will bubble up to the document
// and trigger the click event we just bound.
// This will hide the element right now just after showing it,
// we don't want that.
event.stopPropagation();
}
You have to keep in mind that a Javascript event goes up and down the whole tree when begin fired. So you can bind event listeners to any parent when you want to listen for an event on many elements.
This is called event delegation.
A cheap way to do it potentially is to bind an event handler to the "(on)blur" event of the clickable item and/or it's target. If your design allows.
That is one way to do it.
You could also write a method that traps (hooks into) all 'click' events regardless of the element, and hide your menu from there.
JQuery would make this task easier for you.
step 1- use a javascript library so you can have the code be as cross browser as possible - otherwise you have to cater to two different event models between internet explorer and gecko/webkit based browsers. JQuery, Mootools, YUI - all will handle this for you - there are more but those 3 are my favorite and are well documented.
step 2 - you prob would want to implement a clickshield for this - essentially a block-level dom element that is absolutely positioned over your entire page with a higher z-index than the rest of the page. attach a click event to that, and you can perform your logic for hiding elements on the page. The clickshield could easily have javascript code expand it to the width -height of your page post DOM rendering using the methods of any of the aforementioned javascript libraries.