How can I select an element based on its position? - javascript

How is it possible to select an element based on its position?
<div id="parent">
<p id="text">This is a text</p>
</div>
//CSS
#parent{width:100px;height:100px;position:absolute;left:100px;top:100px;}
Now using the left=100px,top=100px how can I select the div element using JQuery?

Something like this might work:
var el = document.elementFromPoint(100, 100);
var $el = $(el); // if you really want to use jQuery.
Documentation for document.elementFromPoint.
This might not be what you're looking for: it will only get the visible, topmost element at that point, and it will get whatever element occupies that space, not just one whose top-left corner is at that point. But, chances are, it'll help :).
It is supported by all major browsers, although you could get wacky results with Safari 4 or Opera 10.10.

The jQuery css() function allows you to retrieve the value of CSS properties, for example css('left') will return the left position of your element.
However, in your question you as about selecting the div element based on its location. I presume you mean something like $('left=100px'). This is not possible. jQuery uses CSS selectors which query the structure, not the style of the DOM.

You could loop through every element to check the position. Here is a quick sample function:
function getElementsByPosition(x,y) {
var elements=new Array();
$('*').each(function() {
if($(this).css('top')==x && $(this.css('left'))==y) {
elements[elements.length]=this;
}
});
return elements;
}
Update: Domenic's solution is much more elegant. Please consider his before using mine.

Related

How to simplify repeated and convoluted js/jquery code?

This piece of code appears in a js script I have been asked to modify - I'm not sure why it is written in this way, it doesn't make sense to me.
Can anyone help explain what it is doing, and if it can be simplified to be a little more meaningful?
var unformathtml = $(this).text();
if(unformathtml.trim().length>showChar) {
$(this).parent().parent().parent().parent().parent().find('.comment-footer').fadeOut();
}
Lets' pretend we have a DOM like this:
<parent-5>
<target-element>Content</target-element>
<parent-4>
<parent-3>
<parent-2>
<parent-1>
<focused-element>Some Text</focused-element>
</parent-1>
</parent-2>
</parent-3>
</parent-4>
</parent-5>
What this code is saying is "if the text inside of <focused-element> has more characters than showChar then fade out <target-element>.
A better way of doing this would be to give <parent-5> some kind of identifier, which could be an ID or a class, and target that instead of the repeated .parent() call.
Here's an example which showcases the idea:
$('#oldMethod').click(function() {
$(this)
.parent()
.parent()
.parent()
.parent()
.parent()
.find('.comment-footer')
.toggleClass('red');
});
$('#newMethod').click(function() {
$(this)
.closest('.comment-container')
.find('.comment-footer')
.toggleClass('red');
});
.red {
color: #F00;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="comment-container">
<div>
<div>
<div>
<div>
<button id="oldMethod">Old</button>
<button id="newMethod">New</button>
</div>
</div>
</div>
</div>
<div class="comment-footer">Footer</div>
</div>
Wow, that really doesn't make much sense. It is doing this:
1) Getting the raw contents out of an element
2) Checking to see if it is longer than a certain length
3) If so, fading out another element on the page
The parents() thing is very error-prone. It is going up a very precise number of levels in the HTML tree and then descending downwards to find an element with a class of '.comment-footer'. As a result a slight rearrangement of either element in the DOM might result in the code no longer working, because it can't find the specified element.
What you want is to find the tag-to-hide more directly. Ideally, the element-to-hide and the element-that-decides-to-hide would be next to eachother in the DOM (i.e. the element being hidden would be a child or sibling of the element that decides whether or not to hide it). This makes it very easy for the one to find the other. If that isn't possible, your next best bet would be to simply assign an id to the element you are trying to hide and then select on that id directly:
var unformathtml = $(this).text();
if(unformathtml.trim().length>showChar) {
$('#to_hide').fadeOut();
}
As a quick aside, .text() is used (instead of .html()), because the former removes any HTML tags. This way you are measuring the amount of "actual" text inside $(this) to determine whether or not you want to hide said element. So that part is probably fine.

How do you grab an element relative to an Element instance with a selector?

I am writing a small library where I am in need of selecting a relative element to the targeted element through querySelector method.
For example:
HTML
<div class="target"></div>
<div class="relative"></div>
<!-- querySelector will select only this .target element -->
<div class="target"></div>
<div class="relative"></div>
<div class="target"></div>
<div class="relative"></div>
JavaScript
var target = document.querySelectorAll('.target')[1];
// Something like this which doesn't work actually
var relativeElement = target.querySelector('this + .relative');
In the above example, I am trying to select the .relative class element relative only to the .target element whose value is stored in target variable. No styles should apply to the other .relative class elements.
PS: the selectors can vary. So, I can't use JavaScript's predefined methods like previousElementSibling or nextElementSibling.
I don't need solution in jQuery or other JavaScript libraries.
Well it should be ideally:
var relativeElement = target.querySelector('.relative');
But this will actually try to select something inside the target element.
therefore this would only work if your html structure is something like:
<div class="target">
<div class="relative"></div>
</div>
Your best bet would probably in this case be to use nextElementSibling which I understand is difficult for you to use.
You cannot.
If you insist on using the querySelector of the subject element, the answers is there is no way.
The spec and MDN both says clearly that Element.querySelector must return "a descendant of the element on which it is invoked", and the object element you want does not meet this limitation.
You must go up and use other elements, e.g. document.querySelector, if you want to break out.
You can always override Element.prototype.querySelector to do your biddings, including implementing your own CSS engine that select whatever element you want in whatever syntax you want.
I didn't mention this because you will be breaking the assumption of a very important function, easily breaking other libraries and even normal code, or at best slowing them down.
target.querySelector('.relative');
By using querySelector on the target instead of document, you scope the DOM traversal to the target element.
It is not entirely clear from your explanation, but by related i assume you mean descendant?
To get all target elements you can use
document.querySelectorAll('.target')
And then iterate the result
I found a way which will work for my library.
I will replace "this " in the querySelector with a unique custom attribute value. Something like this:
Element.prototype.customQuerySelector = function(selector){
// Adding a custom attribute to refer for selector
this.setAttribute('data-unique-id', '1');
// Replace "this " string with custom attribute's value
// You can also add a unique class name instead of adding custom attribute
selector = selector.replace("this ", '[data-unique-id="1"] ');
// Get the relative element
var relativeElement = document.querySelector(selector);
// After getting the relative element, the added custom attribute is useless
// So, remove it
this.removeAttribute('data-unique-id');
// return the fetched element
return relativeElement;
}
var element = document.querySelectorAll('.target')[1];
var targetElement = element.customQuerySelector('this + .relative');
// Now, do anything with the fetched relative element
targetElement.style.color = "red";
Working Fiddle

select html object without id

Edit: one missing piece of information - I can't use the class selector because there are more divs with the same class. I already thought of that, but I forgot to mention it. I have no idea why my post got downvoted, but it seems awfully silly considering I provided a lot of information, gave it honest effort, and tried to be verbose with code examples. People on this forum are ridiculous sometimes.
I'm trying to set the id of a div that doesn't have one and there's no way I can give it one upon generation of the page. I've tried using jquery (.each, .contains, .find, .filter, etc.) and I can't seem to get it right. I know a ton of people have asked this question, but none of the answers made sense to me.
I have the ability to set the text (html?) of the div, but nothing else. It ends up looking like this:
<div class="dhxform_note" style="width: 300px;">Remaining letters: 500</div>
I want a handle to the div object so I can show the user how many more letters they can type by updating the text.
Using this:
$("div")
returns a list of all divs on the page. I can see the target div in the list, but I can't get jquery to return a single object.
I know it can also be done with something like this:
var divs = document.getElementsByTagName("div");
for(var i = 0; i < divs.length; i++) {
if( /^Remaining letters/.test(divs[i].innerText) )
divs[i].id = "kudosMsgNote"
}
}
but I was hoping to complete this with a cleaner looking solution involving jquery. I also simply want to know how to do it with jquery, aesthetics not withstanding.
Use a class selector.
var theDivViaTheClass = $(".dhxform_note");
Class Selector (“.class”)
Description: Selects all elements with the given class.
version added: 1.0
jQuery( ".class" )
class: A class to search for. An
element can have multiple classes; only one of them must match.
For class selectors, jQuery uses JavaScript's native
getElementsByClassName() function if the browser supports it.
You seem to be targeting the <div> by its text. Try using the :contains selector:
$("div").filter(':contains("Remaining letters")').first().attr("id", "kudosMsgNote");
The .first() is to make sure you don't set the same id for multiple elements, in case multiple elements contain the text "Remaining letters".
Here's the docs for the :contains selector: http://api.jquery.com/contains-selector/
Be careful, the text you're looking for is case sensitive when using :contains!
Is that div the only one with the class dhxform_note? If so, you can use the class selector:
$('.dhxform_note').html();
With jQuery, you can specify any css selector to get at the div:
$(".dhxform_note").attr("id", "kudosMsgNote");
will get you this element as well.
Selecting on inner text can be a bit dicey, so I might recommend that if you have control over the rendering of that HTML element, you instead render it like this:
<div name="remainingLetters" class="dhxform_note" style="width: 300px">Remaining Letters: 500</div>
And get it like this:
$("[name=remainingLetters]").attr("id", "kudosMsgNote");
However, it's possible that you really need to select this based on the inner text. In that case, you'll need to do the following:
$("div").each(function() {
if ( /^Remaining letters/.test($(this).html()) ) {
$(this).attr("id", "kudosMsgNote");
}
});
If you cannot set id for whatever reason, I will assume you cannot set class either. Maybe you also don't have the exclusive list of classes there could be. If all those assumptions really apply, then you can consider down your path, otherwise please use class selector.
With that said:
$("div").filter(function() {
return /^Remaining letters/.test($(this).text())
}).attr('id', 'id of your choice');
For situations where there are multiple divs with the class dhxform_note and where you do not know the exact location of said div:
$("div.dhxform_note").each(function(){
var text = $(this).text();
if(/^Remaining letters/.test(text)){
$(this).attr("id", "kudosMsgNote");
}
});
EXAMPLE
If, however, you know that the div will always be the 2nd occurrence of dhxform_note then you can do the following:
$("div.dhxform_note").get(1).id = "kudosMsgNote";
EXAMPLE
Or do a contains search:
$("div.dhxform_note:contains('Remaining letters')").first().attr("id", "kudosMsgNote");
EXAMPLE

Need jQuery text() function to ignore hidden elements

I have a div set up something like this:
<div id="test"> <p>Hello</p> <p style="display: none">Goodbye</p> </div>
EDIT: To clarify, this is the simplest example. The div could have any arbitrary number of n deep nested children.
$('#test').getText() returns 'Hello Goodbye'. Here's a one liner to test in Firebug: jQuery('<div id="test"> <p>Hello</p> <p style="display: none">Goodbye</p> </div>').text()
This seems to be because what jQuery uses internally, textContent (for non IE), returns hidden elements as part of the text. Hrmph.
Is there a way to return the text content ignoring display:none'd elements? Basically I am trying to mimic the text you would get from highlighting the div with your mouse and copying to system clipboard. That ignores hidden text.
Interestingly, if you create a selection range and get the text from it, that also returns text inside display:none elements.
var range = document.body.createTextRange();
range.moveToElementText($('#test')[0]);
range.select();
console.log(range.toString()); // Also logs Hello Goodbye!
So creating a document selection range doesn't appear to do the same thing as highlighting with the mouse in terms of display:none elements. How do I get around this dirty pickle conundrum?
Edit: using .filter(':visible').text has been suggested, but it won't work for this scenario. I need the returned text to be EXACTLY what would come from a selection with the mouse. So for example:
$('<div>test1 <p>test2</p>\r\n <b>test3</b> <span style="display:none">none</span></div>').appendTo(document.body).children().filter(':visible').text()
returns
"test2test3"
When the output I actually want is
test1 test2
test3
linebreaks, whitespace and all, which come from the \r\n
Filter the elements using .filter(":visible").
Or use this:
$("#test :visible").text();
But the jQuery documentation advises us to use .filter() instead:
Because :visible is a jQuery extension and not part of the CSS specification,
queries using :visible cannot take advantage of the performance boost provided by the native DOM querySelectorAll() method. To achieve the best performance when using :visible to select elements, first select the elements using a pure CSS selector, then use .filter(":visible").
Use :visible in your selector as such:
$("#test > p:visible").text()
A Function example:
-- Edit:
http://jsfiddle.net/8H5ka/ ( Works on Chrome it displays "Hello" in Result )
If the above doesn't work:
http://jsfiddle.net/userdude/8H5ka/1/
If space isn't a major concern you could copy the markup, remove the hidden elements, and output that text.
var x = $('#test').clone();
x.filter(':not(:visible)').remove();
return x.text();
I had this problem and found this question, and it seems the actual solution is based on the provided answers but not actually written out. So here's a complete solution that worked for my situation, which is the same as the OP with the additional provision that elements may be invisible due to external styles based on DOM position. Example:
<style>.invisible-children span { display: none; }</style>
<div class="invisible-children">
<div id="test">Hello <span>Goodbye</span></div>
</div>
The solution is to:
Make a clone of the entire object.
Remove invisible objects in place; if we take #test out of the DOM before we remove invisible objects, jQuery might not know they're invisible because they will no longer match the CSS rules.
Get the text of the object.
Replace the original object with the clone we made.
The code:
var $test = $('#test');
// 1:
var $testclone = $test.clone();
// 2: We assume that $test is :visible and only remove children that are not.
$test.find('*').not(':visible').remove();
// 3:
var text = $test.text();
// 4:
$test.replaceWith($testclone);
// Now return the text...
return text;
// ...or if you're going to keep going and using the $test variable, make sure
// to replace it so whatever you do with it affects the object now in DOM and
// not the original from which we got the text after removing stuff.
$test = $testclone;
$test.css('background', 'grey'); // For example.
Here is how I did it with MooTools:
$extend(Selectors.Pseudo, {
invisible: function() {
if(this.getStyle('visibility') == 'hidden' || this.getStyle('display') == 'none') {
return this;
}
}
});
Element.implement({
getTextLikeTheBrowserWould = function() {
var temp = this.clone();
temp.getElements(':invisible').destroy();
return temp.get('text').replace(/ |&/g, ' ');
}
})
I search for that and found this question but without solution.
Solution for me is just get out of jquery to use DOM:
var $test = $('#test').get(0).innerText
or if more than on element in array of selector, you need a for loop and a merge but I guess that most of time it is the first version that you need.
var $test = $('#test').get().map(a => a.innerText).join(' ');

How to make a function repeat/loop in jQuery?

I have a function here for centering an element within it's parent.
Check out the demo: http://jsfiddle.net/kE9xW/1/
right now it's only applying the centering to the first element, how do i make the function loop itself so it centers every #element on the page. the demo is self explanatory, thanks!
There are a few things you need to do.
As already suggested, id's must be unique, so change the id="..." to class="...". You will also need to change your css to be based on the class not the id (change #element' to '.element')
<div class="container">
<p> ... </p>
<div class="element">
</div>
</div>
Use each in your method to loop over all elements selected by the selector $('.element').
element.each(function(){
// work here in $(this) for the current element
});
You forgot to take the top of the parent div into account, which made all elements overlap each other. So your yPas becomes:
var yPos = $(this).parent().position().top +
parseInt($(this).parent().css('height'))/2 -
parseInt($(this).css('height'))/2 - yPosFromCenter;
Check the working fiddle: http://jsfiddle.net/H99DT/
First, the easiest but most important part: change your IDs to classes. IDs must be unique per page so jQuery's ID selector and JavaScript's document.getElementById() function are only going to give you the first matching element:
Each id value must be used only once within a document. If more than one element has been assigned the same ID, queries that use that ID will only select the first matched element in the DOM.
Change
<div id="container">
...
<div id="element">
to
<div class="container">
...
<div class="element">
and change
$('#element')
to
$('.element')
Next, the more difficult part: you are currently issuing one centerDiv() call to your elements with coordinates from center of 0, 0. That's going to take all your .elements and position them at the exact same spot.
If that's not what you intend, you're going to have to loop through them using .each() and decide the xPosFromCenter and yPosFromCenter in each iteration. It's not clear to me yet how your function works so you may have to explore on your own and see what you can come up with.
Scratch that, see Jamiec's working example for the solution.
Change Id to class in you divs, then make container's position relative with css, and I'll suggest make jQuery plugin from your function. See results http://jsfiddle.net/kE9xW/1/

Categories

Resources