How to select all the elements in the DOM except one element? - javascript

I have an jQuery events) that are triggering on every DOM element on the page.
That's fine for now, but I need a way to exclude a div (with child divs as well) from DOM selection in order to prevent triggering event on them.
I've tried jQuery's unbind(), not() and off() directly on $('#myDiv'), but the events are still triggering.
The code that triggers the event(s):
$("*").each(function () {
// here are events bound to every DOM in the page
$(this).mouseover(mouseover).mouseout(mouseout).click(click);
});
I've also tried with
$("*:not(#myDiv)").each(function () {
// events
$(this).mouseover(mouseover).mouseout(mouseout).click(click);
});
and still wasn't able to remove events from #myDiv.
The HTML:
<div id="myDiv">
<div class="data">
<!-- -->
</div>
<div class="debug">
<!-- -->
</div>
<div class="form">
<!-- -->
</div>
</div>
What is the best way to make full DOM selection but excluding #myDiv, so I would be able to use bind() or on() for binding events?
NOTE: I don't want to remove #myDiv from the DOM tree, but rather exclude it from selection.

You may try this
$(function(){
$('body > *').not('#myDiv')
.on('mouseover', function(){ //... })
.on('click', function(){ //... });
});
An Example.

Try .not()
$("*").not("#myDiv").each(function () {
$(this).mouseover(mouseover).mouseout(mouseout).click(click);
});

Try the following:
$("*:not(#myDiv):not(#myDiv *)")
.mouseover(mouseover)
.mouseout(mouseout)
.click(click)
This will exclude not only #myDiv, but any child elements of #myDiv (which you seem to be going for).

If you want to exclude the children as well, you'll need to do so explicitly:
$("*:not(#myDiv):not(#myDiv *)")

Related

Sortover and sortout events not triggered after element is replaced

The middle column should change width when dragging element over it. It does work until I click on replace button which replaces the html of the middle column. Then the events stop being triggered. As far as I know .on() should be able to handle such cases, or am I wrong?
This is a simple demo which demonstrates the issue: https://jsfiddle.net/shuetvyj/3/
HTML:
<div class="members-column">
<div class="sort"> 1 </div>
<div class="sort"> 1 </div>
</div>
<div id="groupB" class="members-column">
</div>
<div class="members-column">
<div class="sort"> 2 </div>
</div>
replace groupB
Javascript:
$(".members-column").sortable({
items: ".sort",
connectWith: ".members-column"
});
$("#groupB").on("sortover", function() {
console.log('overB');
$('#groupB').css('min-width','80px');
});
$("#groupB").on("sortout", function() {
console.log('outB');
$('#groupB').css('min-width','');
});
$("#replace").on("click", function(e) {
e.preventDefault();
$("#groupB").replaceWith('<div id="groupB" class="members-column"></div>');
});
Instead using replaceWith use .html as below
$("#replace").on("click", function(e) {
e.preventDefault();
$("#groupB").html("");
});
https://jsfiddle.net/shuetvyj/5/
Using replaceWith
$('body').on('sortover', '.members-column', function() {
$('#groupB').css('min-width','80px');
});
$('body').on('sortout', '.members-column', function() {
$('#groupB').css('min-width','');
});
$("#replace").on("click", function(e) {
e.preventDefault();
$("#groupB").replaceWith('<div id="groupB" class="members-column"></div>');
$(".members-column").sortable({ items: ".sort",connectWith: ".members-column" });;
});
https://jsfiddle.net/shuetvyj/8/
when you replace the html, the listeners you had attached to the old html are not reattached to the new html.
As a result, your handlers will no longer get called.
To solve this, simply attach the listeners to the new html when you have replaced it.
(jQuery solved this for a while with live instead of on, which would automatically attach listeners after dom changes) http://api.jquery.com/live/
But live() has been deprecated since jQuery 1.7 and was removed in 1.9
additionally, removing the column probably also breaks sortable's behavior (since it does not store the selector but the result of the selector when it was first called, and thus retains a reference to the div you removed from the DOM.
In short ; do not remove the column div once you have sortable listening to it, just clear or replace its contents

Jquery one() function fires twice on click

My Jquery one() function dies after second click instead of first click. Here is my HTML
<div class="box">
<div class="call" data-tai="5">CLICK</div>
</div>
and heres my Jquery
$('body div').one('click', '.call', function() {
var mother = $(this).parent();
if(mother.css('position') === 'static')
mother.css('position', 'relative');
var tai = $(this).data('tai');
$.ajax({
method: 'GET',
url: '/bootstrap/call.php',
data: 'tai='+tai,
dataType: 'html',
success: function(ret) {
mother.append(ret);
},
});
return false;
});
Interesting thing is, if i don't use return false;, it dies after first click. However bubbling occurs and it appends 2 html tags instead of 1, inside box element. Thanks for help
$('body div')
would select both the divs and attach click handlers to both of them. When you click on the nested div then, both clicks will be fired. Use a specific selector to avoid this.
$('.call')
could perhaps achieve this.
That's because event handlers bound by using .one will be fired once for each element in the jQuery collection. Since the return false stops the propagation of the event, if you click on the .call element, click handler of the parent element is not executed but the parent element still has an active click handler. You should use a more specific selector for selecting the target element. If the click handler should be bound to the div.call elements:
$('.box div.call').one(...);
Now, if .box elements have 9 div.call descendants then you have 9 click handlers! After clicking on each element jQuery unbinds the handler for that specific element.
It's not once for all elements, it's once for each element.
If the handler should be called once for all the matching elements you can use the delegation version of the .one method:
$(document).one('click', '.box div.call', function() {
// ...
});
And if you want to delegate the event and have the handler working once for dynamically generated elements you can use the .on method and :not selector:
$(document).on('click', '.box .call:not(.clicked)', function() {
$(this).addClass('clicked');
// ...
});
Now the handler is called once for each .call element. Since :not excludes the elements that have .clicked class the selector doesn't match the already-clicked elements.
Events bubble in JavaScript. Your code
$('body div').one('click', '.call', function() {
}
wires up on both
<div class="box"> <!-- This -->
<div class="call" data-tai="5">CLICK</div> <!-- And this -->
</div>
You need a more specific selector. If this div is a parent element in the body like this:
<body>
<div class="box">
<div class="call" data-tai="5">CLICK</div>
</div>
<div class="box">
<div class="call" data-tai="5">CLICK</div>
</div>
</body>
then you can use a selector like this:
$('body > div').one('click', '.call', function() {
}
The question is - where do you expect your click event to be placed? Perhaps the div with the box class?
$('div.box').one('click', '.call', function() {
}
This assumes that the .call divs are being added dynamically to the .box div.
P.S. - if you want to stop the bubbling, I suggest you pass in the event object to your click event and call stopPropagation()
$('div.box').one('click', '.call', function(evt) {
evt.stopPropagation(); // no bubbling
}

Selecting specific elements when using the body as the delegate

I understand that its recommended to use the body as the delegate for .on() events:
$('body').on('click', 'button', function() {
$('input[type="checked"]').removeAttr('checked');
});
Where I run into trouble is when trying to select specific elements but of the same type. So for instance if I have a button with a unique class, id, etc I end up selecting the first button within the DOM.
$('body').on('click', $('button#clear'), function() {
$('input[type="checked"]').removeAttr('checked');
});
Is there any workaround for this when using .on()?
Bonus: Is there a time where you shouldn't use the body as the delegate? If so what would be the second best alternative?
body isn't the recommended per se, it's just a safe bet that it will work since your target element is probably a child of body - in reality, you should use the static container for your AJAX content as your initial selector, eg:
<div id="container">
<!--I GET POPULATED WITH AJAX CONTENT-->
<input id="ajaxInput" /> <!--I came from an AJAX call-->
</div>
Now you could use
$("body").on("click", "#ajaxInput", function() { });
But this will run the check each time the body is clicked. A much better selector would be:
$("#container").on("click", "#ajaxInput", function() { });

jQuery Nested Plugins Selector Issue

I'd like the ability to nest one plugin within another. However my selectors are too aggressive and keep retrieving the elements for the nested plugin aswell.
For example given the following HTML:
<div class="my-plugin">
...
<div class="my-plugin">
...
<button class="select">Select</button>
</div>
</div>
With the following code to create the plugin:
$(function() {
$('.my-plugin').myPlugin();
});
When I say the following (within my plugin):
// element is the element the plugin is attached to
$('.select', element);
This will retrieve the select element from the nested plugin within the outer plugin but I'd like it not to. Also I'd like to do the same when attaching click events. For example the following code should only attach the click event in the nested plugin and not within the outer plugin.
element.on('click', '.select', function(e) {
...
});
I hope I've explained that clearly. I'd appreciate if someone could show me how my selector can be improved to handle this. Thanks
The problem is, selectors work against the context they're given. If you tell jQuery to search a div, it will search everything in that div for what it's looking for. It's just how jQuery works.
If you want to exclude the inner plug-in, give it an id and exclude it using .not(). Or you could give it a class or data-* attribute as well. We just need something to tag it as "do not include".
So, do this:
$('.select', element).not('#mySecondPlugin');
or:
$('.select', element).not('.mySecondPlugin');
or:
$('.select', element).not('[mySecondPlugin="true"]');
This selector will select everything within your outer element EXCEPT the inner one and its contents.
And finally:
$('.select', element).not('[mySecondPlugin="true"]').on('click', function(e) {
...
});
You can use jQuery .closest() to find the first occurrence of a selector from an element. So you could target the nested div with #('.select').closest('.my-plugin').
Using jQuery .filter():
var myPlugin = this;//or whatever is representing your plugin jQuery object.
var selectsYouWant = $('.my-plugin .select').filter(function(index){
if(this.closest('.my-plugin') === myPlugin) {
return true;
} else {
return false;
}
});
You need to understand events. When you click on the element, event bubbles up the DOM tree. You need to stop propagation, so that it would not reach outer plugin handler. Depending on the logic you may also need to prevent default action:
element.on('click', '.select', function(e) {
e.stopPropagation();
e.preventDefault();
// ...
});
Also, not sure what is the logic inside plugin, but you can filter out inside items:
var button = $('.my-plugin').find('.select').not('.my-plugin .my-plugin *');
button.css('color', 'red');
See: FIDDLE
This is the approach I recommend.
At initialization:
$(element).addClass('my-plugin');
var $selects = $(element).find('select')
.not( $(element).find('.my-plugin select') );
You would have to make sure that the element and $selects variables are accessible to all functions in the plugin.
On the note about on(), here's what I would suggest:
element.on('click', '.select', function(){
// see if the closest .my-plugin is the matching element, and not
// a child plugin
if ( ! $(this).closest('.my-plugin').is( element ) )
return;
...
});
Try to start outside of your first plugin:
for example:
<div class="plugin-wrapper">
<div class="my-plugin">
...
<button class="select">Select</button> //We want this one
<div class="my-plugin">
...
<button class="select">Select</button> //Without this one
</div>
</div>
</div>
You would then be able to use something like $('.plugin-wrapper > .my-plugin > .select') which would get ONLY the first .select without the second. Which I believe is what you are trying to accomplish
For the onclick
$('.plugin-wrapper > .my-plugin > .select').on('click', function () {
//Your code here
});

jQuery delegateing inside div > divs > a also :(

html:
<div id="a">
<a id="a0" href="#"></a>
<div id="b"><a id="b0" href="#"></a><div>
<div>
jQuery:
$('#a').delegate('a', 'click', function(){ //do stuff });
delegates #b0 as well. Is there any clever selector way of ways to ignore #b ?
NB /// links inside divs are added and detached dynamically ///
ty
if you know it's only direct descendants try
$('#a').delegate('#a>a', 'click', function(){ alert('a') });
if you know you want to ignore children of b try
$('#a').delegate('a:not(#b>a)', 'click', function(){ alert('a') });
EDIT: a fiddle
This is working as expected. Your delegate() call will set event handler for all children under #a that match selector a.
If you are trying to set handler for a specific element then do that
$('#a0').click(function(){ ... });
Also your html is pretty broken. Make sure closing tags actually closing.
The first field in the delegate method is the selector. As of now, you are selecting all the anchors. Use id instead.
jQuery1.7 or above
$('#a').on('click', '#a0', function(event) {
alert(event.target.id);
});

Categories

Resources