nfplee
nfplee

Reputation: 7977

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

Upvotes: 3

Views: 251

Answers (5)

redolent
redolent

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

Tomas Kirda
Tomas Kirda

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

Yatrix
Yatrix

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

km6zla
km6zla

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

SnareChops
SnareChops

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

Related Questions