jQuery Nested Plugins Selector Issue - javascript

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
});

Related

Add click event on div tag using JavaScript

I have a div tag in my form without id property. I need to set an on-click event on this div tag.
My HTML code:
<div class="drill_cursor" >
....
</div>
I don't want to add an id property to my div tag.
How can I add an on-click event on this tag using JavaScript?
Pure JavaScript
document.getElementsByClassName('drill_cursor')[0]
.addEventListener('click', function (event) {
// do something
});
jQuery
$(".drill_cursor").click(function(){
//do something
});
Try this:
var div = document.getElementsByClassName('drill_cursor')[0];
div.addEventListener('click', function (event) {
alert('Hi!');
});
Just add the onclick-attribute:
<div class="drill_cursor" onclick='alert("youClickedMe!");'>
....
</div>
It's javascript, but it's automatically bound using an html-attribute instead of manually binding it within <script> tags - maybe it does what you want.
While it might be good enough for very small projects or test pages, you should definitly consider using addEventListener (as pointed out by other answers), if you expect the code to grow and stay maintainable.
Recommend you to use Id, as Id is associated to only one element while class name may link to more than one element causing confusion to add event to element.
try if you really want to use class:
document.getElementsByClassName('drill_cursor')[0].onclick = function(){alert('1');};
or you may assign function in html itself:
<div class="drill_cursor" onclick='alert("1");'>
</div>
the document class selector:
document.getElementsByClassName('drill_cursor')[0].addEventListener('click',function(){},false)
also the document query selector https://developer.mozilla.org/en-US/docs/Web/API/document.querySelector
document.querySelector(".drill_cursor").addEventListener('click',function(){},false)
Separate function to make adding event handlers much easier.
function addListener(event, obj, fn) {
if (obj.addEventListener) {
obj.addEventListener(event, fn, false); // modern browsers
} else {
obj.attachEvent("on"+event, fn); // older versions of IE
}
}
element = document.getElementsByClassName('drill_cursor')[0];
addListener('click', element, function () {
// Do stuff
});

Using .on() and targeting elements with a specific attribute

I understand you can use .on() to attach a single click event to an element and then specify which child elements receive the click. So, for example:
$(this.el).on("click", "span", function () {
alert("Bloop!");
});
I need to be a bit more specific and target selectors with a particular attribute, like this:
$(this.el).on("click", "span[data-placeholder]", function () {
alert("Bloop!");
});
That doesn't seem to work, though. As soon as I add the attribute it stops working. No errors, just doesn't seem to find the elements.
Is that the expected behavior? Is there a way around it?
CLARITY
$(this.el) is just a div that contains a number of elements, some of which are <span data-placeholder="First Name"></span> tags. There could be dozens of those <span> tags and I didn't want that many event listeners, so I thought I'd use .on() to add the click to the parent container.
Here's JSFiddle showing your example working, with both existing <span>s and with newly created ones.
Just to be clear, this will work with your event delegation:
var span = $('<span>Test</span>');
span.attr('data-placeholder', 'test'); // declare as an attribute
$(this.el).append(span);
span.click();
This will not:
var span = $('<span>Test</span>');
span.data('placeholder', 'test'); // declare with .data()
$(this.el).append(span);
span.click();
jQuery's .data() method will read properties from data attributes if declared, but does not store them as attributes on the element when adding data.
Here's another JSFiddle.
try
$("span[data-placeholder]", this.el).on("click", function () {
alert("Bloop!");
});
You can choose to filter your spans
$('span', this.el).filter(function() {
return $(this).hasAttr('data-placeholder');
}).on('click', function() {
//This is for all the spans having data-placeholder
//...
});
Or if the placeholder is set via data api:
$(this.el).filter(function() {
return $(this).data('placeholder') != 'undefined';
}).on('click', function() {
//This is for all the spans having data-placeholder
//...
});
This functions above select those elements specifically, if event delegation on the OP is needed, then you can do the following:
$('span', this.el).on('click', 'span', function() {
if($(this).data('placeholder') != 'undefined') {
alert('bloop');
}
});
add a id to your span and pin point it using # tag

run a function when a user clicks on any list element

This is probably a very common question, but I was unable to find an answer myself;
All my list elements call the function setQuery like this
onClick="admin_stats.setQuery(this);"
rather than [hardcode] add this to every list element, is there a way to simply have it run when a list element is clicked?
I'm not very familiar with jQuery Live or binding/unbinding, but I think they would play a role here?
Before I reinvent a rather square-looking wheel I thought I might ask =)
edit: my list elements look like
<ul>
<li id="usersPerMonth" onClick="admin_stats.setQuery(this);">Users per Month</li>
<li id="statsByUser" onClick="admin_stats.setQuery(this);">Stats by User</li>
</ul>
the this.attr("id") is then used to look up what the actually SQL text looks like, from a json-style variable:
queries : {
usersPerMonth : " ... some sql ...",
statsByUser : " ... some sql ...",
...
}
so that's why I have the divs named that way (and I'm open to design suggestions)
$(function() {
$('#myList').delegate('li', 'click', function() {
admin_stats.setQuery( this );
});
});
This assumes your <ul> element has the ID myList. It will handle clicks inside of it on any <li> elements, calling your function, and passing this as the argument.
The .delegate() code is wrapped in $(function() {}); so that it doesn't run until the DOM is ready. This is a shortcut for jQuery's .ready() function.
Yes - use jQuery like this:
$('li').live("click", function(event){
admin_stats.setQuery(event.target);
});
This is assuming you want to set it to every li element. You can find the documentation for live here: http://api.jquery.com/live/
What live does makes sure that all elements passed to it will always have the click handler in the function specified.
jQuery is probably your best bet. Are you adding new elements on the fly, or will the elements be there when the attach is ready to go? If they're "satic" in the sense that once the page is loaded that's it, you could use:
$(document).ready(function(){
$('li').bind('click',function(e){ // may need to change the selector to be more specific
admin_stats.setQuery(this);
});
});
that needs to fire onClick... so:
$('li').click(function(){
admin_stats.setQuery($(this));
});
In jQuery you select elements using CSS-style selectors (can use this if more elements will not be added; this uses .click()):
$(function() {
$('#liContainer li').click(function() {
admin_stats.setQuery(this);
});
});
We put the whole thing inside $(function() { ... }); because the page needs to be ready before binding the event handler. Alternatively, you could do it like this if more elements are added. This uses .delegate():
$(function() {
$('#liContainer').delegate('li', 'click', function() {
admin_stats.setQuery(this);
});
});

Issue with selectors & .html() in jquery?

The function associated with the selector stops working when I replace it's contents using .html(). Since I cannot post my original code I've created an example to show what I mean...
Jquery
$(document).ready(function () {
$("#pg_display span").click(function () {
var pageno = $(this).attr("id");
alert(pageno);
var data = "<span id='page1'>1</span><span id='page2'> 2</span><span id='page3'> 3</span>";
$("#pg_display").html(data);
});
});
HTML
<div id="pg_display">
<span id="page1">1</span>
<span id="page2">2</span>
<span id="page3">3</span>
</div>
Is there any way to fix this??...Thanks
Not sure I understand you completely, but if you're asking why .click() functions aren't working on spans that are added later, you'll need to use .live(),
$("#someSelector span").live("click", function(){
# do stuff to spans currently existing
# and those that will exist in the future
});
This will add functionality to any element currently on the page, and any element that is later created. It keeps you have having to re-attach handlers when new elements are created.
You have to re-bind the event after you replace the HTML, because the original DOM element will have disappeared. To allow this, you have to create a named function instead of an anonymous function:
function pgClick() {
var pageno = $(this).attr("id");
alert(pageno);
var data="<span id='page1'>1</span><span id='page2'> 2</span><span id='page3'> 3</span>";
$("#pg_display").html(data);
$("#pg_display span").click(pgClick);
}
$(document).ready(function(){
$("#pg_display span").click(pgClick);
});
That's to be expected, since the DOM elements that had your click handler attached have been replaced with new ones.
The easiest remedy is to use 1.3's new "live" events.
In your situation, you can use 'Event delegation' concept and get it to work.
Event delegation uses the fact that an event generated on a element will keep bubbling up to its parent unless there are no more parents. So instead of binding click event to span, you will find the click event on your #pg_display div.
$(document).ready(
function()
{
$("#pg_display").click(
function(ev)
{
//As we are binding click event to the DIV, we need to find out the
//'target' which was clicked.
var target = $(ev.target);
//If it's not span, don't do anything.
if(!target.is('span'))
return;
alert('page #' + ev.target.id);
var data="<span id='page1'>1</span><span id='page2'>2</span><span id='page3'>3</span>";
$("#pg_display").html(data);
}
);
}
);
Working demo: http://jsbin.com/imuye
Code: http://jsbin.com/imuye/edit
The above code has additional advantage that instead of binding 3 event handlers, it only binds one.
Use the $("#pg_display span").live('click', function....) method instead of .click. Live (available in JQuery 1.3.2) will bind to existing and FUTURE matches whereas the click (as well as .bind) function is only being bound to existing objects and not any new ones. You'll also need (maybe?) to separate the data from the function or you will always add new span tags on each click.
http://docs.jquery.com/Events/live#typefn

Javascript + jQuery, click handler returning false to stop browser from following link

I'm trying to stop the browser from following certain links, rather I want to unhide some Divs when they are clicked.
I'm having trouble just getting the links to not be followed though.
Here's what I have:
var titles = $('a.highlight');
jquery.each(titles, function(){
this.click(function(){
return false;
});
});
It seems like the click handler is not being assigned. What am I missing?
Try
this.click(function(e){ e.preventDefault(); }
Actually, it looks like you might need to use the jQuery constructor on this:
$(this).click(function(){ return false; }
You could also try using parameters on the each function instead of using this:
jQuery.each( titles, function(index, elem) { $(elem).click( function() { return false; } ) } );
Personally, I would just do titles.each( ... though. In that instance you can use this to bind the click handler. I am not sure off the top of my head what this binds to with jQuery.each
Or just calling click on titles:
titles.click( function() { return false; } )
That will bind click to every element in titles. You don't need to loop through them.
You can compress that jquery a bit:
$('a.highlight').click(function() { return false; });
You should also make sure that:
There are no other click handlers registered for those elements later on.
The code you have is attaching after the elements have loaded. If they're not completely loaded, they won't be found in the $('a.highlight') selector. The easiest way to do this is to put your code in a $(document).ready(function() { *** code here *** }); block.
Edit: As per other responses - the problem was that this represents a DOM object, while $(this) is a jquery object. To use the .click function to attach a handler, you need a jquery object.
In short, using this inside the each loop won't work with what you're trying to do. You'll need to get a jquery representation by using $(this) instead.

Categories

Resources