I am running some tests on a DOM element,
the result of the tests is one of the element descendants.
for example:
<div id="myelement" class="some-class">
<div class="some-child-class"></div>
<div class="some-other-child-class">
<div class="grandchild-class"></div>
<div class="another-grandchild"></div>*
</div>
</div>
let's assume that:
test(document.getElementById('myelement'));
will return the Element marked with asterisk
Now my problem is:
The test procedure is heavy and resource consuming.
I don't want to run it when i don't have to.
And sometimes, I clone an element that has already been tested (meaning - i KNOW the result of the test), but since I am getting an object reference as a result I can't use it to access the relevant child on the cloned element.
Is there any efficient way of somehow "save" the relative path from a parent Element to a specific descendant DOM element and then "apply" it on another element?
So you could assign unique ids to each element and cache the test results in an Object at the Elements id. However, i dont know if this is useful. An example implementation:
var test=function(el){
return this[el.id] || (this[el.id]=advancedtesting(el));
}.bind({});
So you could do:
test(document.getElementById('myelement'));//caches
test(document.getElementById('myelement'));//returns the cached
You could use jQuery for that.
The selectors they use can take the form of 'nth-child' or 'nth-of-type' (see documentation here).
What is does is that targets child element from the position they have relative from where you start from.
In your case if you want to start from your first element and go down to the starred one you can do:
$('#myelement').find('div:nth-child(2) > div:nth-child(2)')
What this does is that it takes #myelement as a base from which you will begin the search, and after that it goes down to the second child element that is a div, and again into this div's second child element.
You could reuse that selector with a different base.
Related
For example, document.getElementsByClassName("whatever") returns a list of elements, and each element has an index (so the element x is the [3] in that list, for example).Do HTML elements save that index inside the element, somehow? Or they're 'unaware' of their position?
Example of the usage I'd do with that property:
You click an element with class "People", using event.target when onclick. So you want to know which position it has, in the 'People' list. Let's say it's event.target.classNameIndex. So once you know the index, you can do things in JavaScript.
Obviously the simple alternative I can think of this is simply picking event.target and searching it inside the getElementsByClassName list. Or simply giving IDs to all elements. But avoiding that would be nice.
Hope you understand my question. :)
No
The elements are generated either dynamically or statically and are independent from everything done with them after being displayed. There are pure javascript ways of obtaining the index of an element in a array-like structure but they will most likely depend on the use of a element.onClick function and pairing them with other elements via some sort of selector.
No, for lots of reasons.
First of all, you are doing a query on the internal DOM structure, and the DOM tree itself might change immediately after your query. Elements can be added, moved or removed.
Furthermore, two very different queries might have overlapping results. E.g. query 1 might return:
[ <div id="a">, <div id="b"> ]
While query 2 could return:
[ <div id="b">, <div id="c"> ]
(for simplicity I am representing the results as arrays)
In the above, how would the element <div id="b"> know its unique and unchanging "index", given the truly infinite amount of possible queries, not the mention the possibly variable DOM again?
For draggable divs in JS on my page, I want to store the last location in local storage so that when a user refreshes, the draggable elements on the page stay put.
My HTML is in general like this:
<div id="some_id" class="draggable">
<p>I am a draggable thing.</p>
</div>
I then use the id of the div as a key in local storage so that having multiple draggable objects on the page doesn't result in them all being given the same position on refresh.
However, templates like this are sometimes used inside a template which handles visibility, so sometimes they'll be like this:
<div class="visibility_container draggable">
<button class="close_button">Close</button>
<div id="some_id">
<p>I am a draggable thing.</p>
</div>
</div>
Note that the draggable class is added programmatically each time.
These templates may vary but will never have ids within them – they'd be pretty terrible templates if they did – so I only need to find the first descendant element which has an id and use the value of that id as my local storage key.
How can I find the nearest element with JS? I'm aware that jQuery has a .closest() method which finds the nearest ancestor – I need to go in the opposite direction. I'm also aware of jQuery's .find() which can find me all descendants matching a selector, but I'm unsure I can guarantee the order in which jQuery returns these children as the API docs were not clear on that point.
I'm also aware of jQuery's .find() which can find me all descendants matching a selector, but I'm unsure I can guarantee the order in which jQuery returns these children as the API docs were not clear on that point.
find lists elements in document order (aka "DOM order") (and you're right, I'm surprised not to see a clear statement of that in the docs). "Document order" is a well-defined DOM term, it means a depth-first search of the descendant elements. Or put it another way: The one whose text is first in the markup.
So for instance:
<div id="container">
<div>
<div>
<div id="one"></div>
</div>
<div id="two"></div>
</div>
...then
console.log($("#container").find("[id]").first().attr("id"));
...will log one, not two.
This document order thing is common across most of the jQuery API and DOM methods like querySelectorAll, getElementsByTagName, and such. I'm not having any luck finding a clear statement of it in the jQuery documentation, though, which seems like an oversight. The closest I've found so far is a bit documenting an exception to that (e.g., saying here that "The second and third...create a jQuery object using one or more DOM elements that were already selected in some other way...unlike most other multi-element jQuery operations, the elements are not sorted in DOM order." [my emphasis].) The multiple selector docs also point out that the results will be in document order (not the order of the selectors).
html segment:
<div class="container">
<div id="one">Div #1</div>
<div id="two">Div #2</div>
</div>
if I set $p as a jQuery object, as below:
$p = $('<p>whatever you like</p>');
//$p = '<p>whatever you like</p>';
$('#one').after($p);
$('#two').after($p);
then the result would be:
Div #1
Div #2
whatever you like
While I set $p as htmlString, as below:
//$p = $('<p>whatever you like</p>');
$p = '<p>whatever you like</p>';
$('#one').after($p);
$('#two').after($p);
then the result would be:
Div #1
whatever you like
Div #2
whatever you like
Seems while I use object, jQuery doesn't clone the object, but just move it; while I use htmlString, it create a new object accordingly each time.
I want to know the exact reason. It would be more appreciated if any reference could be provided as well.
Many thanks!
Seems while I use object, jQuery doesn't clone the object, but just move it; while I use htmlString, it create a new object accordingly each time.
Correct. The first time you call after, you put the elements inside the jQuery object into the DOM. The second time, they get moved. This is the standard DOM behavior.
It's documented, somewhat indirectly, on the after page:
If an element selected this way is inserted into a single location elsewhere in the DOM, it will be moved rather than cloned:
This echoes the standard behavior of the DOM methods appendChild and insertBefore.
Note that if you call after on a jQuery set that has multiple elements in it, the elements you're passing in will get moved to after the first target element and then cloned to go after the remaining target elements:
Important: If there is more than one target element, however, cloned copies of the inserted element will be created for each target except for the last one.
Example
This second behavior is jQuery-specific (although with sufficient hand-waving one might argue it's similar to how the DOM handles document fragments and, on more modern browsers, template elements).
When $p is a string '<p>whatever you like</p>' and you append it using .after(), the string is added to the HTML. It's never a jQuery object.
When $p is a jQuery object $('<p>whatever you like</p>'); and you're appending it using one of the append functions like .after() you're moving it around the page. If you don't want to move it, clone it.
$('#one').after($p.clone());
$('#two').after($p.clone());
Demo
I have a bunch of generated divs that I'd like to grab a specific child of.
Each one is roughly set up like this:
<div id="generated-row-N>
<div class="seriously-generated-crap">
<div id="still-seriously-generated-crap-N">
<input name="child-to-select-N">
</div>
</div>
</div>
Where N is a counter
I'm not well versed in all the different kinds of jQuery selectors, what I've attempted is a combination of the "begins with" selector and the "child" selector.
$("div[id^=generated-row-] > input[name^=child-to-select-]").each(function(){
// stuff I'm going to do to each input child
});
I assume my error is it's thinking the child is a direct child of the parent with no elements in between. Is there a way to get through the two other divs and grab a child regardless of how many levels deep it is?
note: I'm not listing the other childs because the generated class and id names are not reliable to use.
Thanks in advance. I haven't had much luck finding similar topics on SO, if this is a duplicate, please let me know.
> is the immediate children selector, just omit it.
And also wrap the values of your attributes with single quotes ''
$("div[id^='generated-row-'] input[name^='child-to-select-']")
You need to use descendant selector instead of child selector
$("div[id^=generated-row-] input[name^=child-to-select-]").each(function(){
// stuff I'm going to do to each input child
});
when you use a child selector it will search only the direct children of the target parent, in your case the input element is coming at the 3rd level means it is an descendant element not a child
Also to make it much easier apply class like
<div id="generated-row-N" class="generated-row">
<div class="seriously-generated-crap">
<div id="still-seriously-generated-crap-N">
<input name="child-to-select-N" class="child-to-select">
</div>
</div>
</div>
then
$(".generated-row input.child-to-select").each(function(){
// stuff I'm going to do to each input child
});
Use descendant selector,
$("div[id^=generated-row-] input[name^=child-to-select-]").each(function(){
// stuff I'm going to do to each input child
});
or use .find()
$("div[id^=generated-row-]").find("input[name^=child-to-select-]").each(function(){
// stuff I'm going to do to each input child
});
Please keep this in your mind, the elements which are located in the first level of the parent is said to be child elements, on the other hand the elements which are located more than one level deeper to the parent is called as descendants, Additionally children are also comes under the category of descendant.
You do not need immediate child selector.Use:
$("div[id^=generated-row-] input[name^=child-to-select-]").each(function(){
// stuff I'm going to do to each input child
});
If you assign a common css class to-process to your input elements, your code simplifies to
$(".to-process").each(function(){
// stuff I'm going to do to each input child
});
other advantages:
no need to worry about the actual embedding html structure of the elements of interest
common styling simplified
var test= $("div[id^='generated-row-'] input[name^='child-to-select-']")
Basically I need to grab the first child of a parent element, usually but not limited to an <iframe>.
<div>
<iframe src="www.asource.com">GRAB THIS AFTER A BUTTON IS CLICKED</iframe>
</div>
I have tried the following
$("#parent").eq(0).prop("tagName");
$("#parent").first().prop("tagName");
$("#parent:first").prop("tagName");
$("#parent:nth-child(1)").prop("tagName");
$("#parent:lt(1)").prop("tagName");
All of these return a "div" when I alert them. The reason I am getting the prop name was because I wanted to use the find() to get the element.
$("parent").find(ONE OF THE PROP CALLS ABOVE).animate({.....
because when I do:
$("parent").find('**iframe**').animate({.....
that is the only scenario that I can grab and properly animate an iframe.
however since we don't know if the first child is an iframe or not, i used .prop() to get the first element tag name and find it.
(please note, i tried all the above attempts directly with .animate(...) instead of .prop(...) but that also didn't grab the iframe.
Would really appreciate some guidance
You need find the first child so
$("#parent").children().first().prop("tagName");
$("#parent > :first-child").prop("tagName");
$("#parent > :eq(0)").prop("tagName");
Assuming you are using the correct parent selector - the above code assumes the parent element has the id parent like
<div id="parent">
<iframe src="www.asource.com">GRAB THIS AFTER A BUTTON IS CLICKED</iframe>
</div>