JQuery selector to exclude child elements - javascript

I'm using this click method to reveal additional classes inside a .tile
var $tiles = $('.tile');
$tiles.on('click', function(){
var $this = $(this);
$this.toggleClass('large'); // change layout of tile
$this.children('.details').toggle(); // display additional info
})
Since a .tile may contain links, I'd like to limit the click functionality. I don't want the function to trigger when a link inside .tile is clicked.
Edit: While #antyrant's answer is working for normal links, I noticed in my example the problem persists with on of the links that uses fancyBox.
This is what my HTML looks like:
<div class="tile">
<ul class="details">
<li><a class="download"> href="#">Download</a></li>
<li><a class="fancybox-media" href="#">Video</a></li>
</ul>
</div>
Clicking the download link works fine, but the fancyBox will not work!
Edit 2: See this jsFiddle for a working example.

You need to stop events propagation on child elements, for example:
$tiles.find( 'a' ).click( function( e ) {
e.stopPropagation();
});
UPDATE:
If this can be done as you use some plugins for example you can check if event target has your base class: for example:
$('.tile').on('click', function ( e ) {
if( !$( e.target ).hasClass( 'tile' ) ) {
return true;
}
//...
see jsFiddle demo.

stopPropagation() will stop your child event from bubbling up, and being caught by the listener on the parent element.
var $tiles = $('.tile');
$tiles.on('click', function () {
console.log('clicked');
})
$('a', $tiles).on('click', function (e) {
e.stopPropagation(); //Prevents event from bubbling up
})

#antyrat is correct, stopping propagation on the anchors will achieve your goal, but that requires binding another event that isn't really necessary. Another option would be to look at the target on the event passed in to your handler.
$tiles.on( 'click', function( e ) {
if( !$( e.target ).is( 'a' ) ) { // if the element clicked is not an anchor.
// do stuff
}
});
EDIT: Here's a fiddle demonstrating this working using the updated html provided above.

Related

click works with jQuery but not vanilla js

If I use jQuery and click on the icon it works fine:
$(document).on('click', '.fileuploader-action-sort', function() {
alert('clicked');
});
But if I use vanilla js, I have to literally click at the very bottom of the icon, like on the tip for it to actually work otherwise nothing happens when clicking anywhere else on the actual icon.
document.body.addEventListener("click", e => {
if (!e.target.matches(".fileuploader-action-sort")) return;
alert('clicked');
});
Here is the html:
<a class="fileuploader-action fileuploader-action-sort" title="Sort"><i></i></a>
I am generating the icons based on the number of database records I have which is why I didn't use document.querySelector('.fileuploader-action-sort');
Because e.target refers to the whole document in your vanilla js example if you click on the div, and it refers to the i tag if you click on any content inside of it. But in the jQuery example it refers to the .fileuploader-action-sort class.
One way to notice it is to console.log(e.target) :
document.body.addEventListener("click", e => {
console.log(e.target)
if (!e.target.matches(".fileuploader-action-sort")) return;
alert('clicked');
});
<a class="fileuploader-action fileuploader-action-sort" title="Sort"><i>Test</i></a>
So you need to listen to the i tag and not the class in itself :
document.body.addEventListener("click", e => {
if (!e.target.matches(".fileuploader-action-sort i")) return;
alert('clicked');
});
<a class="fileuploader-action fileuploader-action-sort" title="Sort"><i>Test</i></a>

Change li class in jquery

I want to change the class of the current li(list) which is selected
$('li.doBlokkeer').click(function(e) {
$(this).addClass('doDEBlokkeer').removeClass('doBlokkeer');
});
$('li.doDEBlokkeer').click(function(e) {
$(this).addClass('doBlokkeer').removeClass('doDEBlokkeer');
});
so if a current li is selected its class need to be changed (it needs to have doDEBlokkeer). The above code works..
The problem is that this only works once for each LI item..
when I click on li.doBlokkeer the class changes which is good, but when I press the same current li again, it calls the same function li.doBlokkeer instead of li.doDEBlokkeer function (despite the css class) . I tried so much stuff but i really can't find any solution. can you guys help me out? I have been searching for a solution for more then 14 hours, so frustrated right now...
Issue is that you are binding the event on the class selector (for the element existed in DOM at that time ) which gets changed dynamically so your binding is lost. You can consider using event delegation syntax or bind it to a different class/selector which doesn't change.
Using Event delegation (jq >=1.7) you can try:
$('ul').on('click', 'li.doBlokkeer', function(e) {
$(this).addClass('doDEBlokkeer').removeClass('doBlokkeer');
});
$('ul').on('click', 'li.doDEBlokkeer', function(e) {
$(this).addClass('doBlokkeer').removeClass('doDEBlokkeer');
});
Another shortcut:
$('.cls').click(function (e) { // add a common class to all lis and bind the click event to that.
var flg = $(this).is('.doBlokkeer'); //check if it is a specific class
$(this).addClass(function () {
return flg ? 'doDEBlokkeer' : 'doBlokkeer'; //based on flag return the other class
}).removeClass(function () {
return flg ? 'doBlokkeer' : 'doDEBlokkeer'; //based on flag return the other class
});
});
or just:
$('.cls').click(function (e) {
$(this).toggleClass('doDEBlokkeer').toggleClass('doBlokkeer');
});
Fiddle
Fiddle
Also, please consider this:
$('li').click(function()
{
var $this = $(this),
one = 'doBlokkeer',
two = 'doDEBlokkeer';
if ( $this.hasClass(one) ) {
$this.removeClass(one).addClass(two);
} else {
$this.removeClass(two).addClass(one);
}
});
Use .toggleClass
$('li.doBlokkeer').click(function(e) {
$(this).toggleClass('doDEBlokkeer');
});
There is no need for the second click event
The issue is as explained earlier, that you bind the event to an element with the given class name, then, on click you change the class name, so the handler doesn't listen to it any more...
I would recommend to stick with event delegation because it's lighter and you can also nest elements in your lis (like a link or a div etc.):
First add the class 'cls'to your <ul>, so <ul class="cls">. Your HTML could the look like:
<ul class="cls">
<li class="doBlokkeer"><div>Click on me</div></li>
<li class="doDEBlokkeer">Click on me</li>
<li class="doBlokkeer">Click on me</li>
<li class="doBlokkeer">Click on me</li>
</ul>
All you need for your javaScript is now:
$('.cls').on('click', '.doDEBlokkeer, .doBlokkeer', function (e) {
$(this).toggleClass('doDEBlokkeer doBlokkeer');
});
...which requires jQuery 1.7. If you have only jQuery 1.4.2 and up you can use '.delegate()`
$('.cls').delegate('.doDEBlokkeer, .doBlokkeer', 'click', function (e) {
$(this).toggleClass('doDEBlokkeer doBlokkeer');
});
The event (and only one) is now on the ul and the .on() pickes out the right elements defined by the class names you passed through.
I updated the fiddle from PSL

JavaScript prevent touch move on body element, enable on other elements

but very simply, I'd like to prevent the touchmove event on the body element but leave it enabled for another element. I can disable fine... but I'm not sure how to re-enable it somewhere else!
I imagine that the below theoretically works because return true is the opposite of preventDefault, but it doesn't work for me. Might be 'cause $altNav element is in $bod?
JS:
$bod.bind('touchmove', function(event){
event.preventDefault();
});
$altNav.bind('touchmove', function(event){
return true;
});
I'm not sure what lib you're actually using, but I'll asume jQuery (I'll also post the same code in browser-native-js if you're using something other than jQ)
$bod.delegate('*', 'touchstart',function(e)
{
if ($(this) !== $altNav)
{
e.preventDefault();
//and /or
return false;
}
//current event target is $altNav, handle accordingly
});
That should take care of everything. The callback here deals with all touchmove events, and invokes the preventDefault method every time the event was triggered on an element other than $altNav.
In std browser-js, this code looks something like:
document.body.addEventListener('touchmove',function(e)
{
e = e || window.event;
var target = e.target || e.srcElement;
//in case $altNav is a class:
if (!target.className.match(/\baltNav\b/))
{
e.returnValue = false;
e.cancelBubble = true;
if (e.preventDefault)
{
e.preventDefault();
e.stopPropagation();
}
return false;//or return e, doesn't matter
}
//target is a reference to an $altNav element here, e is the event object, go mad
},false);
Now, if $altNav is an element with a particular id, just replace the target.className.match() thing with target.id === 'altNav' and so on...
Good luck, hope this helps
Use a custom CSS class and test for it in the document handler, eg:
<div>
This div and its parents cannot be scrolled.
<div class="touch-moveable">
This div and its children can.
</div>
</div>
then:
jQuery( document ).on( 'touchmove', function( ev )
{
if (!jQuery( ev.target ).parents().hasClass( 'touch-moveable' ))
{
ev.preventDefault();
}
});
http://tinyurl.com/mo6vwrq
you can add a argument,like this
$bod.bind('touchmove', function(event,enable){
if(enable){
event.preventDefault();
}
});

Why does jQuery's one fire immediately when it's added to an element?

Here's a fiddle illustrating the problem. I am adding a jQuery one binding on the click of one element to the 'html' element. I am not expecting the 'one' event handler to fire until the next click, but it fires on the click that adds the binding. This seems to not be a problem if it is a more specific element that the 'one' event handler is added to, but it happens when I use 'html' or 'body' as the element, which is what I want to do.
This doesn't make sense to me, I'd think the first click would add the one for the next click and it wouldn't fire on the click on the link.
By the way, my actual problem could probably be solved in a better way, but I came across this and was curious why it didn't work as I expected.
Code:
html:
<div id='hello'>hello</div>
<a class="title" href="#">this example</a> is a test​
js:
$(function() {
$('a.title').click(function() {
var htmlClickBind = function (e) {
console.log('clicked on html, e.target = ' + e.target);
console.log(e.target == '');
if (!$(e.target).is('a') ) {
console.log('cleared click event');
}
else {
$('html').one('click', htmlClickBind);
}
};
$('html').one('click', htmlClickBind);
});
});​
The click event on the a.target element bubbles up to the html element, where your (just-added) handler sees it.
To prevent this, use event.stopPropgation in your a.target click handler (or return false, which does stopPropagation and preventDefault).
Updated code (see the comments): Live copy
$(function() {
// Accept the event arg ----v
$('a.title').click(function(e) {
// Stop propagation
e.stopPropagation();
var htmlClickBind = function (e) {
console.log('clicked on html, e.target = ' + e.target);
console.log(e.target == '');
if (!$(e.target).is('a') ) {
console.log('cleared click event');
}
else {
$('html').one('click', htmlClickBind);
}
};
$('html').one('click', htmlClickBind);
});
});​

jquery selector help. Everything but the specified selector

I have the following function to open an overlay menu:
$('.context-switch').click(function() {
$(".context-switch-menu").toggle();
});
To hide the menu, I would like the user to be able to click on any area outside ".context-switch-menu"
I am trying with :not() but with no success..
$('body').click(function(e) {
if ($(e.target).hasClass('context-switch')) {
return;
}
$(".context-switch-menu").hide();
});
$('.context-switch').click(function() {
$(".context-switch-menu").toggle();
return false;
});
The reason this can be difficult is because of event bubbling.
You can try something like this:
$('.context-switch').click(function(e) {
e.stopPropagation();
$(".context-switch-menu").toggle();
});
$(".context-switch-menu").click(function(e){
e.stopPropagation();
});
$("body").click(function(e){
$(".context-switch-menu").hide();
});
The e.stopPropagation() prevents the click event from bubbling to the body handlers. Without it, any click to .context-switch or .context-switch-menu would also trigger the body event handler, which you don't want, as it would nullify the effect of the .context-switch click half the time. (ie, if the state is hidden, and then you click to show, the event would bubble and trigger the body handler that would then hide the .context-switch-menu again.)
Without testing, would something like this work?:
$('.context-switch').click(function() {
$(".context-switch-menu").show();
});
$(document).click(function() {
$(".context-switch-menu").hide();
});
Instead of using document, 'html' or 'body' may work as well.
$(document).on('click', function(e) {
if (e.target.className !='context-switch-menu') {
$(".context-switch-menu").hide();
}
});
Just an idea here, based on what what others have suggested in the past:
$(document).click(function(e){
//this should give you the clicked element's id attribute
var elem = $(e.target).attr('classname');
if(elem !== 'context-switch-menu'){
$('.context-switch-menu').slideUp('slow');
//or however you want to hide it
}
});
try this, we don't want to call a function when you clicked on the element itself, and not when we click inside the element. That's why we need 2 checks.
You want to use e.target which is the element you clicked.
$("html").click(function(e){
if( !$(e.target).is(".context-switch-menu") &&
$(e.target).closest(".context-switch-menu").length == 0
)
{
alert("CLICKED OUTSIDE");
}
});
Live fiddle: http://jsfiddle.net/Xc25K/1/

Categories