Stopping links inside of a table from marking table row as selected - javascript

I've ran into a minor annoyance with a webpage that I am building where by clicking on a link within a table cell is triggering the row to be marked as clicked.
I do however want the rest of that specific cell from being allowed to be clicked.
You can checkout the jsfiddle via: https://jsfiddle.net/r00ftuo1/
Notes:
Usually my jquery code will save the selected row to a database so on returning the page it would restore the selected row.
Usually the link will redirect to a different page; it would NOT go to #.

It is recommended to assign event handler to the table rather that individual rows or cells this is because each event binding counts towards memory not significant for small number of rows but really matter if rows are in hundreds
$("table").on("click", "td", function(e){
if($(e.target).is(':not(a)')){
$(this).closest('tr').toggleClass("selected");
}
});

https://jsfiddle.net/r00ftuo1/4/
I think this is what you are looking for. Also if you need support IE9 or earlier you need to also do the cancelBubbling to stop the event from bubbling up
$("tr").click(function(evt) {
if ($(this).hasClass("selected")) {
$(this).removeClass("selected");
} else {
$(this).addClass("selected");
}
});
$("a").click(function(evt){
evt.stopPropagation();
window.location.href = $(this).attr("href");
});
Event bubbles out so it starts with the deepest element in the DOM hierarchy and if you don't stop the event from propagating it will bubble out to the cell then the row then the table. So here we need a click handler on the link to stop the event from propagating and you can get the href from the element and navigate using something like this:
window.location.href = $(this).attr("href");
EDIT: forgot the "href"

This is my super lazy hacks solution;
$("tr").click(function(e) {
if (e.target.tagName !== "A") {
$(this).toggleClass("selected");
}
}
So the class toggles only if the user clicked somewhere in the td - but not on an A element.
I also simplified your class setting to use toggle to save a few lines of code.
Here is an updated version of your jsfiddle using this solution https://jsfiddle.net/r00ftuo1/2/

So as i understand , you want to make a distinction between the click event on the anchor tags and the click event taking place on the table cell.
Your best bet would be to use event.target and write a little bit of logic as per your requirements . A quick code snippet.
$("tr").click(function(event) {
if (event.target == this) { /** your logic **/ }
});
i hope that answers the question. Further reading. event.target vs event.currentTarget in jquery

Related

jQuery toggle on click out

I have a series of spans (togglers) and a series of divs (toggled). I created a make_toggle function that receives the toggler and its corresponding toggled as arguments.
Everything seems to work kind of ok up to the point where I try to implement a "toggle on click out". What I've tried is to attach to the html click event a function that checks whether the target of the click is contained within the toggled element. On toggle "back", I would then detach the handler so I am only checking when I need.
var check_if_clickingout = function(e) {
if (!toggled[0].contains(e.target)) {
toggle();
}
};
See fiddle: https://jsfiddle.net/andinse/65o211nc/11/
It doesn't even seem to work anymore but when it used to, it was triggering many more times than necessary (which was the reason for me to come here ask for help).
What am I doing wrong? What is the most effective way to go about this kind of situation where I am giving functionality to a series of independent DOM elements?
Just putting this out here that this seems to do the same thing.
$("span").click(function() {
$(this).siblings("div").toggleClass("blue");
});
Maybe I am missing something more that I am not seeing in your example.
See more: http://api.jquery.com/toggleclass/

DOM Listener on Static Table Change

I'm trying to add listener to my table in order to call another jJavascript function.
My app is a simple upload script, when user chooses a file it uploads and creates new row in the table. Therefore at each creation of new row I would like to call a Javascript function.
My sample code is the following:
$("#fayls").bind("DOMNodeInserted", function() {
$(".chzn-select").chosen();
alert('ha cambiato')
});
Where fayls is the id of my table.
When I run this code, it calls this event infinitely instead of just one.
How should I solve this problem ?
Thanks.
I think the problem is in that Chosen plugin converts, among all others, elements inside that container with "#fayls" ID. Consider this:
HTML:
<div id="something"></div>
<div id="completely_different"></div>
<button id="change_something" type="button">Change!</button>
JS:
$('#change_something').click(function() {
$('#something').append($('<p>Internal P</p>'));
});
$('#something').bind("DOMNodeInserted", function(event) {
alert(event.target);
$("#completely_different").append($('<p>SOme p</p>'));
});
JS Fiddle
It works as expected (each click on the button adds a <p> element into boths divs).
Now let's change our HTML slightly:
<div id="something">
<div id="completely_different"></div>
</div>
<button id="change_something" type="button">Change!</button>
JS Fiddle
... and now the event will be fired indefinitely - because when we update the inner div, the DOMNodeInserted still bubbles up to the outer one.
Also note that DOMNodeInserted event is, well, considered deprecated now (but still its support is buggy in IE9, as described here).
You has the problem because the event will be dispatched also at the parent nodes (bubbles)
From W3C
DOMNodeInserted:
- Fired when a node has been added as a child of another node.
- This event is dispatched after the insertion has taken place.
- The target of this event is the node being inserted.
- Bubbles: Yes
- Cancelable: No
- Context Info: relatedNode holds the parent node
try to dispatch a custom event when add the row and bind it for update the combobox
to bind event use:
$('#something').bind('row.added', function(event) {
event.stopPropagation();
//your code here
}
to trigger the event (after row is added) use:
$('#something').trigger('row.added');
As you all mentioned, the problem is caused from chosen, for each drop box item in chosen, it fires the DOM event, therefore I'll have infinite loop.
I found a simple solution by just filtering the unnecessary events:
$("#listenMe").bind("DOMNodeInserted", function(event) {
var targetName = event.target.nodeName.toString() ;
if (targetName == "TR") {
$(".chzn-select").chosen();
//alert('ha cambiato - Event Node Name:' + event.target.nodeName)
}
});
Therefore it only runs when a new row added into a table

Document click not in elements jQuery

Using jQuery how does one detect clicks not on specific elements, and perform an action consequently?
I have the following JavaScript
$('#master').click(function() {
$('#slave').toggle();
});
$(document).not('#master','#slave').click(function() {
$('#slave').hide();
});
and I cannot see where I am going wrong logically. You can see a live example here
Since you're binding to the click event on the document, you can use event.target to get the element that initiated the event:
$(document).click(function(event) {
if (!$(event.target).is("#master, #slave")) {
$("#slave").hide();
}
});
EDIT: As Mattias rightfully points out in his comment, the code above will fail to identify click events coming from descendants of #master and #slave (if there are any). In this situation, you can use closest() to check the event's target:
$(document).click(function(event) {
if (!$(event.target).closest("#master, #slave").length) {
$("#slave").hide();
}
});
Does this code do what you want? (not entirely sure if I understood correctly)
$('body').on('click', '*:not( #master, #slave )', function() {
$('#slave').hide();
});
http://jsfiddle.net/gZ4Hz/8/
Event delegation has long been supported natively by jQuery. The difficulty lies in creating the appropriate selector. Originally, delegate was used, but more recently the delegate form of on should be used.
The purpose of event delegation is to listen to events on child elements and invoke the bound event handlers on those elements as though they had been bound to the child element, instead of the parent. This means that instead of binding handlers to every element in the DOM, you're binding a handler to every element in the initial selection (document is a single element). This also makes for a simple way to use a single selector to bind to an ever changing set of elements, as new elements will propagate their events to document whether or not they existed when the initial event handler was bound:
$(document).on('click', '*:not(#master, #master *, #slave, #slave *)', function (e) {
//this will reference the clicked element
});
Additionally, note that I not only said the elements must not be #master or #slave, they must not be children of #master or #slave either.
Another thought, it may not be working because your browser may not be rendering body at 100% height; Try adjusting your base css to fix height of body and then a couple other thoughts.
e.stopPropagation(): Prevents the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event.
So if you change the first click code to the following:
$('#master').click(function(e) {
e.stopPropagation();
$('#slave').toggle();
});
Then you could change the call sign of the second too:
$("body, body *").not('#master, #slave').click(function(e) {
$('#slave').hide();
});
And that should cover it. Give it a try! or see this jsFiddle
Fredrik's answers works for elements already present in the document, but it didn't work for elements fetched by ajax calls.
I tried the following and it works for me. Sharing the code for future ajax coders.
$(document).on('click',function(event) {
if (!$(event.target).closest("#selector").length) {
if ($('#selector').is(":visible"))
$('#selector').slideUp();
}
});
Would have posted it as a comment but I don't have enough reputation for that.
$('.clickable-row').on("click",function(){
window.location = $(this).data('href');
return false;
});
$("td > a").on("click",function(e){
e.stopPropagation();
});
or
jQuery(document).ready(function($) {
$('.clickable-row').on("click",function(){
window.location = $(this).data('href');
return false;
});
$("td > a").on("click",function(e){
e.stopPropagation();
});
});

how to prevent mutiple registration of a single event handler in jquery

I have a <div> box displaying search message and some radio button for recent message. There is link option for slide toggle.
When you click on that link it will show some input field and check box and radio button. And at the same time the text of link change to hide option. If you click on that it will hide all the input and checkbox option.
When I refreash the whole page its working properly but when that paticular box or part is refreashing then the box is hiding and imediately hides. If you refresh that portion n number of times the box is going on toggling continously. I think the problem is in registration of event handler. So please give me some solution.
CODE :
$(document).ready(function() {
$(".SideBar-blockheader1").click(function() {
e.preventDefault();
$(".SideBar-blockcontent1").slideToggle("fast");
});
$(".SideBar-optionheader").click(function() {
$(".SideBar-optioncontent").slideToggle("fast");
$(this).text($(this).text() == $("#hideopt").attr('value') ? $("#showopt").attr('value') : $("#hideopt").attr('value'));
});
$(".SideBar-optionheader").text($("#showopt").attr('value'));
$(".SideBar-optioncontent").hide();
});
jQuery has a method, called data() which can be used to extract the attached handler information of an HTML element. You can see if the element has already a click handler, and if it has, then stop re-attaching another handler to it.
if(typeof $('#id').data('events').click == 'object')
{
// A click handler is already attached
}
else
{
// No click handler; Attach one;
}
Although you haven't provided code, I suspect you are using .click(). For jQuery 1.7+ you should be using .on() in delegate mode (the element you bind to is an ancestor, not the clickable element itself) or .delegate() if pre jQ 1.7.
For example:
$('someAncestor').on('click', 'a.specialLink', function(event) {
event.preventDefault();
// the rest of your code for the click handler
})
"someAncestor" is any valid selector that is an ancestor of your link that will not be destroyed, rebuilt, or otherwise manipulated after the DOM is built. It doesn't have to be the direct ancestor.
[updated below after seeing code sample and comments]
There are a few things going on. First, .on() will only work if you're using jQuery 1.7+. Next, .on() can be invoked a few different ways (I wrote about it here: http://gregpettit.ca/2011/jquery-events-its-on/) and you need to be invoking it while delegating an ancestor listener, not simply as a substitute for click. Next, you haven't provided code for your attempted update, only for the original code; it's hard to tell what "didn't work" about trying to use .on(). Moving along, I'm not actually sure what this line is meant to do:
$(this).text($(this).text() == $("#hideopt")...etc...
I can't think of why you would want to try to treat a jQuery object as a variable. I'm not saying the code is wrong, I'm just saying I don't get it. Also, I hate ternary operators... which is part of the reason I don't get it. I much prefer readable conditionals. ;-)
Next, you're calling preventDefault() on "e" but you're not passing "e" into your functions. You might just be getting a JavaScript error, period. (e is undefined)
Then there's attr("value") which I believe should actually work. But why not use .val() if it is indeed a node that HAS a value attribute?
Finally, there is tonnes of room for caching your objects. Every time you see that an object is being used more than once, you can benefit (to varying degrees of performance and legibility) from caching it. I have not updated the code with any caching, though-- that's really something for a whole other "How can I best cache my objects?" question.
Anyhow... to solve the problem, you first have to choose a valid ancestor. This can be any ancestor that isn't destroyed during the process of loading in your new data. This could be anything, but the closest ancestor is the best. It might be a section wrapper, but if you're truly desperate it could be a page wrapper or even the body tag. If you bind to document, you're reproducing the deprecated .live() function, which I definitely recommend against. I have used a placeholder selector, ".section" but you need to figure out what an appropriate ancestor is on your page.
$(document).ready(function()
{
$(".section").on("click", ".SideBar-blockheader1", function(e)
{
e.preventDefault(); // probably not necessary if there's no default click behaviour
$(".SideBar-blockcontent1").slideToggle("fast");
});
$(".section").on("click", ".SideBar-optionheader", function(e)
{
e.preventDefault(); // probably not necessary if there's no default click behaviour
$(".SideBar-optioncontent").slideToggle("fast");
$(this).text($(this).text() == $("#hideopt").val() ?$("#showopt").val() : $("#hideopt").val());
});
$(".SideBar-optionheader").text($("#showopt").val());
$(".SideBar-optioncontent").hide();
});

jQuery different binds on containing div and button inside

So I have a button inside a list row that is used to delete the row from the page (calls ajax stuff to delete the object represented by the row, but that's not important for my question). The whole row is bound to a click event which would redirect to another page.
In other words, the containing row is click bound and the inner button is click bound, which is causing me problems since clicking the inner button also triggers the containing row click event (as it should).
I've tried binding a hover event for all delete buttons that unbinds the row click on mouseover, and rebinds it on mouseout, like this pseudocode below:
$('.delete-button').hover(
function() {
$('.list-row').unbind();
$('.delete-button').bind('click', function() { /* delete action */ });
},
function() {
$('.delete-button').unbind();
$('.list-row').bind('click', function() { /* list row action */ });
}
);
This isn't working very well, and I'm convinced there is a better way to approach it. Should I take the button out of the containing list-row? It's way easier to have it in there since my list row contains custom attributes that have data I need for the ajax calls and I can just var rid = $('.delete-button).parent().attr('row-id'); to get the data, but I'm not opposed to change :)
Thanks!
In your click event handler for the button, you need to call e.stopPropagation(). This will prevent the event from bubbling up the DOM tree. More info here: http://api.jquery.com/event.stopPropagation/
edit: you already accepted (thanks!), but maybe this code snippet would help explain some of the concepts better:
$('.list-row').click(function() {
/* list row action */
});
$('.delete-button').click(function(e) {
// die, bubbles, die
e.stopPropagation();
// if you also need to prevent the default behavior for the button itself,
// uncomment the following line:
// e.preventDefault();
// note that if you are doing both e.stopPropagation() AND e.preventDefault()
// you should just `return false;` at the end of the handler (which is jQuery-
// sugar for doing both of these at once)
/* delete action */
})
There's a few ways of approaching this. As #jmar777 has already said you may attach an altered event to the click handler on the button, stopping propagation.
If you want to do this with the same function as you're applying to the div then you can approach it as such:
if($(event.target).is("input")) {
event.stopPropagation();
}
Another approach is to actually not bind the click event to the button, for any time the browser supports clicks on the containing element. As you will always trigger that, then you don't actually need the button to handle it too! This does require you to handle IE6 etc a little differently from everything else though...
Let your handler function return false

Categories