Reputation: 7977
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
Upvotes: 3
Views: 251
Reputation: 4259
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;
...
});
Upvotes: 1
Reputation: 8413
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
Upvotes: 1
Reputation: 13775
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) {
...
});
Upvotes: 2
Reputation: 4887
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;
}
});
Upvotes: 1
Reputation: 13347
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
});
Upvotes: 0