JavaScript or JQuery: get innermost element on position - javascript

I want to get the innermost element on a certain cursor position, i.e.:
<div>
<div>
<span>Text</span>
</div>
</div>
If I point my mouse cursor over the text, I want to retrieve the span tag, not the outermost div which is the default of JavaScript's elementFromPoint(x,y).
(if it is of any help: I want to retrieve the element inside a JQuery keydown event handler)

A keydown event does not provide properties like .pageX and .pageY, so you cannot apply those values on document.elementFromPoint().
You need to have an event listener on your window or document.body and provide the event data information to a more public context, so you can access that data in your keydown handler.
Demo: http://jsfiddle.net/1ztf2p9b/

you can do something like this:
$('div').hover(function(element){
element.find('span').dosomestuff();
});
I hope it helps.

Demo: http://jsfiddle.net/mspocq9h/1/
var hoverEleme = null;
$('*').hover(function(){
hoverEleme = $(this);
});
$(window).on('keydown', function( e ) {
// Get hoverEleme here
$('span').css('color', 'black');
$(hoverEleme).closest('span').css('color','red');
});
Using jQuery's .closest() call with traverse up the element's ancestors to find the first element matching the selector, in this case the closest span.
Here there are no filters for what key was pressed, and it just sets the color to red so you can see it finds the one you're looking for.

Related

jQuery selector precedence when using pattern matching

I am creating a form that implements a bunch of similar elements. They are custom select boxes, created out of <ul>s.
Some of these elements are slightly different in the way I want the mousedown event to be handled though.
The way I have it set up currently is that, by appending _custom_select to the end of an elements class name, it will be treated as one of these special elements as far as CSS is concerned.
However, when the string selections is found inside a class name (that will coincidentally also end with _custom_select in order to apply the proper styling) I want to use a different mousedown event handler.
This is the relevant section of my event listener set up:
$('[class$="_custom_select"] li').mousedown(function(event){
var opt= event.target;
if(opt.className!='li_disabled' && event.which==1)
{
if(opt.className=='li_unselected'){
opt.className= 'li_selected';
}
else{
opt.className= 'li_unselected';
}
update_selections(opt.parentElement);
}
});
$('[class*="selections"]').mousedown(function(event){
var opt=event.target;
if(event.which==1){
if(opt.className=='li_unselected'){
opt.className= 'li_selected_2';
}
else{
opt.className= 'li_unselected';
}
}
});
This code works, but notice how, in the second binding, I had to bind the event listener to the ul that holds the li that is actually being clicked.(The ul is the element whose class name matches the pattern) In the first one however, I can bind the event listener directly to the li elements contained within the ul.
If I change the second jQuery selector to $('[class*="selections"] li') the event listener is never bound to the corresponding lis.
What is causing this behavior?
I am aware that I can just check event.target.tagName to ensure the event is bubbling up from an <li>, but that is not what the question is about.
I originally thought it had something to do with precedence and that the listeners weren't being bound because the lis that would have matched the second selector already matched against the first selector.
However, after implementing logging and looking at the DOM I have determined that when I change the second selector to: $('[class*="selections"] li') neither event listener is bound to the lis that match the second selector.
Here is a link to a JS fiddle of the 'working version'. If you add ' li' to the second selector and then try to click the <li>s in the box to the right, you will see that they no longer become green.
jsFiddle
https://jsfiddle.net/6sg6z33u/4/
Okay, thanks for posting the jsFiddle. This is an easy fix!
The elements in your second li are being added dynamically. When you bind to elements using the shortcut methods like .click() it only binds to the elements on the page when it initially bound
The fix: use the .on() method, which is the preferred method per jQuery foundation. This method allows for live binding meaning it will pick up on dynamic elements.
$('[class*="selections"]').on( 'mousedown', 'li', function(event) {
var opt = event.target;
if (event.which == 1) {
if (opt.className == 'li_unselected') {
opt.className = 'li_selected_2';
} else {
opt.className = 'li_unselected';
}
}
});

Why does 'selector' return value differ from 'this'

Inside an event handler, why does $(this) return something else than $('.selector')?
Example:
$('.container').click(function () {
console.log($(this));
console.log($('.container'));
});
jsFiddle
When you look in the console the results are different.
this is always the element on which the event originated, in other words which of the .container elements you clicked exactly.
e.g.:
<div class="container">container1</div>
<span class="container">container2</span>
as Jonathan Lonowski notes, $(".container") selects both .container elements but this is the one you clicked, either the span or the div.
Also, $(this) just wraps that element into a JQuery object, the this keyword itself is native javascript.
Inside the of the event handler, this will normally refer to the single .container element that captured the event.
While the selector will once again find all of the .containers throughout the document.

Get any element tagName on click

I need to take $('this') information from any element i click on the document.
I tried the following code:
$('body').click(function(){
var element = this.tagName; // or var element = $(this).prop('tagName');
alert(element);
});
The problem is that wherever i click i get only BODY element. If i click on a button or a div i want to get that element. How can i create something general to take every element i click ?
Because you are attaching your event handler to the body element, this will always be the body. Instead, interrogate the event.target property:
$('body').click(function(e){
var element = e.target.tagName;
alert(element);
});
Example fiddle
nodeName
$('body').click(function(e){
alert(e.target.nodeName);
});
http://quirksmode.org/dom/core/#t23
My advice is not to use tagName at all. nodeName contains all
functionalities of tagName, plus a few more. Therefore nodeName is
always the better choice.
it also looks like the performance is slightly better on some versions of chrome and firefox.
http://jsperf.com/tagname-vs-nodename/2
this always refers to the element where the event handler is assigned, not where the event originated (well, you can change it, but it's pretty unusual to do so). For that, you need Event.target:
$('body').click(function(event){
var element = event.target.tagName; // or var element = $(this).prop('tagName');
alert(element);
});

How to select element relative to 'this' in javascript

I have the following:
<div class="tab-pane" id="message">
<textarea rows="4" cols="50" id="send_message" placeholder="Enter text ..."> </textarea>
OK
Cancel
I want to bind the click method to the 'div' element , and when one of the child 'a' elements is clicked do separate things. I am trying to distinguish between them using the button text, but the following is not working:
$(function(){
$('#message').click(function(){
if($(this + ">a").is(":contains(OK)")) {
console.log("OK!!");
How can I fix this?
Okay there are two ways of doing this:
.find(selector)
if(this).find("a").is(":contains(OK)")) {
console.log("OK!!");
OR
$(selector,context)
if("a",this).is(":contains(OK)")) {
console.log("OK!!");
In javascript, this is essentially the context of the current function. In jQuery event callbacks, this is set to be the source element of the event - not the selector string, which is what you are treating it as.
Instead, you want to do a test like: if($("a", this).is(":contains(OK)")) {
This works because the second parameter to the jQuery selector is the context to search in, so you are only searching for the a tags under the source element of the click.
Binding the click element to the Div, then checking the text string of the A tags will make both events happen on every click. You want to bind 2 separate click events on each A tag. Add an ID to each A tag, then try this code
$('#okLinkID').click(function(){
console.log("OK!!");
});
$('#cancelLinkID').click(function(){
console.log("Cancel!!");
});
//Attaches only one listener to the #message div and listens for any 'a' element within it to be clicked.
$('a','#message').on('click',function(){
var $this = $(this),
btnText = $this.text();
console.log(btnText);
});
http://jsfiddle.net/YA7Ds/

Prevent "bubbling"? [duplicate]

This question already has answers here:
jquery stop child triggering parent event
(7 answers)
Closed 8 years ago.
I am not sure this is really bubbling, I will explain.
I have this:
<div>
<div>
text here
</div>
</div>
How to bind an on click event so that it will affect only the enclosed div? If I set it like this:
jQuery('div').bind('click', function() {
jQuery(this).css('background','blue');
});
it makes blue all the divs. If I add false as the third argument(prevent bubbling) to the bind function it does nothing.
How can I solved this?
http://api.jquery.com/event.stopPropagation/
Add event.stopPropagation(); inside the hander.
(It might be better, though, to assign an ID or class to the nested DIV so you can be sure it's the only one affected.)
You should really use identifiers like IDs or classes, but for your example, you could do this:
jQuery('div > div').bind('click', function() {
jQuery(this).css('background','blue');
});
...which will bind the handler to any div that is a direct descendant of another div.
So either make your initial selection specific to the element(s) you want to affect, or use event delegation, placing a handler on an ancestor, and testing for the element you want.
Delegation example: http://jsbin.com/ehemac/edit#javascript,live
<div id="container">
<div class="outer">
<div>
text here
</div>
</div>
<div class="outer">
<div>
text here
</div>
</div>
</div>
jQuery('#container').delegate( '.outer > div', 'click', function() {
jQuery(this).css('background','blue');
});
This uses the delegate()[docs] method that places a handler on the ancestor with the ID #container.
The first argument to .delegate() is a selector. Any elements that are clicked inside #container will have that selector compared against the element clicked. If the selector matches, the handler will be invoked.
http://jsfiddle.net/vol7ron/WzSkj/
Targeting the last descendant
Credit to Patrick DW:
jQuery('div:not(:has(div))').bind('click', function() {
jQuery(this).css('background','blue');
});
This should be all you need as it will look at all div and find those that don't have child divs (thus, they will be the last descendant of that element type. You could further filter this to make sure they have a parent that is a div, if you wanted to exclude those divs that are standalone.
Older Answer:
This is not by any means meant to be a complete/robust plugin. It serves as only an example of how to target the last element in a chain. See the revision history for a way to do it w/o the plugin. This should be modified if you wish to use it for production.
Plugin:
(function($){
$.fn.lastDescendant = function(el){
var found = jQuery(el + ':first').siblings(el).andSelf();
var prev, curr;
var stack = this;
for (var i=0,n=found.length; i<n; i++){
curr = found.eq(i).find(el);
while (curr.length){
prev = curr;
curr = curr.find(el);
}
stack = stack.add(prev);
}
return stack;
};
})( jQuery );
Example Call:
jQuery.fn.lastDescendant('div')
.click(function(){
jQuery(this).css("background","#09c");
});
Note:
this will not select the first (ancestor) element. If you want to select that as well, you could wrap the whole thing in a new div, and then do the above.
if I were to make this a production plugin, I would include checking the parameter, and allow you to be able to pass in an object and a starting point (so that siblings are not selected)
To fix this just use a more specific selector
jQuery('div > div').bind('click', function() {
jQuery(this).css('background','blue');
})
The best way to solve it would be to give your inner div an identifiable feature such as a class, e.g., <div class="inner"></div>.
Alternatively, change your selector:
$('div > div').click(function() {
$(this).css('background', 'blue');
}
try giving the inner div an id tag and refer to it...
<div><div id=foo>text goes here</div></div>
...
$('#foo').bind('click', function() {
$(this).css('background','blue');
});
HTH
-- Joe

Categories