Observing click of ajax code checks elements in order - javascript

I have a problem where I need the document observe click function (or something similar) to get elements in order from child out. The reason being is I want to have e.stopPropagation() on the child element, but the parent calls anyways because it is first in line.
For example:
<script type="text/javascript">
document.observe('click', function(e, el) {
if (el = e.findElement('.parent a')) {
e.stopPropagation();
}
if (el = e.findElement('.parent')){
alert('parent');
}
});
</script>
<div class="parent">
Child Click Function
Parent Click Function
</div>
In this example, parent gets found first, so the alert gets called. the reason I have to to do it this way rather than the element.observer is that its Ajax generated content and Prototype doesn't appear to have a live() function like jQuery.
Does anyone have a work around for this?
Thanks.

Prototype definitely does have a .live() equivalent and it is called Event.on(). It also works as an instance method, Element#on().
document.on('click', '.parent', function(event, element) {
if (!event.findElement('.parent a')) {
alert('parent');
}
});

You're observing on "document", the currentTarget of your event e is document.
'.parent a' and '.parent' exists in your document, so el.findElement('.parent a') and e.findElement('.parent') will always return an element.
.parent is not found first in your script, but you dont stop the process ( or use an if/else statement ) when you enter in your first if. So the process continue and the script find the .parent element in document.
Can you explain what you're trying to do ? Why do you want the event to stop bubbling ?
btw :
1) el is undefined in your script.
2) you use "=" and not "==" in your script when you're using findElement();

Related

jQuery - performance of element detection

I'm reading code of my collegues and I'm wondering if the performance could be improved.
For any button-event there is code like that:
a)
$("body").on("click", ".aButtonName", function() { ....});
$("body").on("click", ".aButtonName", function() { ....});
$("body").on("click", ".aButtonName", function() { ....});
....
$("body").on("click", ".aButtonName", function() { ....});
b) Would it be faster to analyse each target-event, after the body is clicked:
$(document.body).on('click', function( e ){
var trg = $(e.target).closest('button');
if(!trg || !trg.attr('class'))
return;
if ( trg.attr('class').indexOf('my_button') > -1) {
....
It would most likely reduce performance. In the second scenario, you end up executing the callback function, passing parameters, and performing several DOM interactions just to exit. In the first, jQuery performs a selector match and only executes the function if there is a match.
In both styles, the event is handled once it "bubbles up" (propagates) to the body. This "delegate-style" checks to see if the selector matches the target (or a parent).
As others suggest, caching the $("body") would save a number of DOM queries and you could get better performance by using a closer/smaller delegate than body.
Question is not clear. If you want to raise an event whenever we click on body Simply u can follow the below code.
<body>
<ul>
......
</ul>
</body>
In js file
$('body').click(function(event){
$(this).find('ul').addClass('selected');
});

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

how to check parent in jquery

I bind a click event over the document. I have a div with id parent. Now whenever the click event occur over the document than i am trying to check the target element is the child of div ( id = parent ) or not.
<div id="parent">
<div id="c1">
<div id="gc1">
</div>
<div id="gc2">
</div>
...
</div>
...
</div>
For this purpose i wrote the following jquery code :
$(document).click( function(e) {
if($(e.target).parents("#parent").length > 0) //this condition is not
working as expected
{ }
});
What i am doing wrong here?
If the code you posted is verbatim, you forgot to declare e as a parameter to the callback function.
Apart from that, your code looks like it should work fine. However, for efficiency you could use .closest() rather than .parents() since that'll only return 0 or 1 elements, and not the entire chain of elements.
You could use jQuery.contains()
if( $.contains( document.getElementById('parent'), e.target) ) {
}
This will check if the target is contained within the parent container.
From the docs:
Note: The first argument must be a DOM element, not a jQuery object or plain JavaScript object.
This is the reason for the getElementById..
EDIT Use closest... Working FIDDLE
First...this may not completely answer your question but you need the (e) in the click function.
$(document).click( function(e) {
if($(e.target).closest("#parent").length > 0)
{ }
});

jquery - using event.stopPropagation()

When the document is ready, I'm fetching some datas from the server through post request and I'm filling it in the HTML as tags by append. When you click that tag, a comment textarea will be displayed. When you click in the document section, the textarea will be closed. The problem here is I can't enter the text in the textarea, when I click inside, it is hiding. I tried using event.stopPropagation() but no use.
Here is my jquery code:
$.post("/person/keywords/get/", function(data){
for(i=0; i<data.length; i++)
{
count = count + 1;
$(".keywords-set").append('<div class="keyword-item"><span class="keyword" id="keyword-'+count+'">'+data[i]+'</span><textarea class="comment" id="comment-'+count+'"></textarea></div>');
}
});
$(".keywords-set").on('click', "[id^=keyword]", function(event) {
event.preventDefault();
i = $(this).attr("id");
i = i.split('-').pop();
$("#comment-"+i).show();
return false;
});
$(".comment").click(function(event) {
event.stopPropagation();
});
$(document).click(function() {
$(".comment").hide();
});
For complete HTML and javascript code, please check here: https://gist.github.com/3024186
It is working in jsfiddle
but not in my localhost. Could you tell the reason, why is it so?
Thanks!
UPDATE
I've also tried this
$(".keywords_set").on('click', ".comment", function(event) {
event.stopPropagation();
});
event.stopPropagation() is not working for HTML elements updated through ajax. But is working for normal(already given) elements.
When doing this:
$(".keywords_set").on('click', ".comment", function(event) {
You must understand that you're catching the event in the element ".keywords_set", and there you will be checking if it bubbled up through ".comment"
This means that any other "click" events set between ".keywords_set" and ".comment" will also activate.
doing stop propagation or returning false will only take affect from the bubbling of ".keywords_set" to the document.
You can do this:
$(document).click(function() {
if($(".comment:hover").length==0){
$(".comment").hide();
}
});
Edit: reply to: "Hey, that code works, I don't know how you are doing it by mentioning .comment.length could you be more descriptive about that?"
When you do any jquery selector, an array is returned. so if you do $(".comment") all html nodes with the class ".comment" will be returned to you in a list [obj1, obj2, ..., objn]
When you do $(".comment:hover") you are asking jquery to select you any element with the class "comment" which also have the mouse currently on top of it. Meaning if the length of the list returned by $(".comment:hover") is bigger than zero, then you caught a bubble from a click in a ".comment".
Although either returning false or stoping propagation should also work. (dunno why in your case it is not working, although i didn't look much at the full code)
Edit 2:
i was lazy to read the full code. but when you are setting the click event for the comment, the comment doesn't exist yet. so the new comment you are adding will not be be caught by your click handler. add it inside the ajax callback and it will work :)
Edit 3: one more thing:
you are not getting side effects because the click even you are re-defining only has the the stop propagation, but you should add the stop propagation before returning false in the
$(".keywords_set").on('click', ".comment", function(event) {
because in practice all other comments you have will be proccessing N times the click event that you are adding to be processed multiple times
Since post method is a asynchronous. You are binding $(".comment") before it exist.
moving
$(".comment").click(function(event) {
event.stopPropagation();
});
after
$(".keywords-set").append('<div class="keyword-item"><span class="keyword" id="keyword-'+count+'">'+data[i]+'</span><textarea class="comment" id="comment-'+count+'"></textarea></div>');
should work.

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