I have the following setup:
var el = $("#overParent");
// Do whatever with el here.
var cls = $(".valueElement", "#parent"); // Get elements with class valueElement inside element with id parent
// Do whatever
This is working but I was wondering if I can make this faster. I know that #parent is an element inside the #overParent which is already selected. Can I somehow use this to scan only the el #overparent for #parent then get the elements with the specified class?
Something like: $(".valueElement", "#parent", el) but according to documentation $ takes only 2 parameters.
If you are finding an element by ID then using just:
var $element = $('#id');
without providing any context to search will always
be the fastest way.
Similarly here where you are providing an ID as a context for your search then "#parent" is the fastest selector. You could theoretically use "#overParent > #parent" to achieve what you mean but it would actually mean more work to do and would be slower.
Just stick with the regular id selector:
$('#element');
It's still the fastest, even if you already have the parent element selected: http://jsperf.com/id-test-123
I'm just guessing here, but I think browsers use a lookup table to find elements by their id attribute.
Selectors like '#theId' don't make jQuery scan the document, as it uses document.getElementById.
If you want to look for an element knowing its id, even if you know its parent, always use $('#theid').
In fact, if you provide the parent as context, jQuery will call document.getElementById and check after that that the parent contains the found element. This is thus much slower.
From the source code :
// Speed-up: Sizzle("#ID")
if ( (m = match[1]) ) {
if ( nodeType === 9 ) {
elem = context.getElementById( m );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
if ( elem && elem.parentNode ) {
// Handle the case where IE, Opera, and Webkit return items
// by name instead of ID
if ( elem.id === m ) {
results.push( elem );
return results;
}
} else {
return results;
}
} else {
// Context is not a document
if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
contains( context, elem ) && elem.id === m ) {
results.push( elem );
return results;
}
}
Similarly, if you want to select all elements with a class, don't specify the parent, this doesn't help.
In your case, as you seem to want to use the parent to restrict the set, simply use
$(".valueElement", "#parent");
according to the docs...
var el = $("#overParent");
this is extremly efficient thn having another selector attached to it....
For id selectors, jQuery uses the JavaScript function document.getElementById(), which is extremely efficient. When another selector is attached to the id selector, such as h2#pageTitle, jQuery performs an additional check before identifying the element as a match.
#Blender
If you are using classes and cache the context element it is faster providing the context:
http://jsperf.com/id-test-123/3
Related
This should be really simple but I'm having trouble with it. How do I get a parent div of a child element?
My HTML:
<div id="test">
<p id="myParagraph">Testing</p>
</div>
My JavaScript:
var pDoc = document.getElementById("myParagraph");
var parentDiv = ??????????
I would have thought document.parent or parent.container would work but I keep getting not defined errors. Note that the pDoc is defined, just not certain variables of it.
Any ideas?
P.S. I would prefer to avoid jQuery if possible.
You're looking for parentNode, which Element inherits from Node:
parentDiv = pDoc.parentNode;
Handy References:
DOM2 Core specification - well-supported by all major browsers
DOM2 HTML specification - bindings between the DOM and HTML
DOM3 Core specification - some updates, not all supported by all major browsers
HTML5 specification - which now has the DOM/HTML bindings in it
If you are looking for a particular type of element that is further away than the immediate parent, you can use a function that goes up the DOM until it finds one, or doesn't:
// Find first ancestor of el with tagName
// or undefined if not found
function upTo(el, tagName) {
tagName = tagName.toLowerCase();
while (el && el.parentNode) {
el = el.parentNode;
if (el.tagName && el.tagName.toLowerCase() == tagName) {
return el;
}
}
// Many DOM methods return null if they don't
// find the element they are searching for
// It would be OK to omit the following and just
// return undefined
return null;
}
Edit 2021
Element.closest is part of the DOM standard. It takes a selector as an argument and returns the first matching ancestor or null if there isn't one.
The property pDoc.parentElement or pDoc.parentNode will get you the parent element.
var parentDiv = pDoc.parentElement
edit: this is sometimes parentNode in some cases.
https://developer.mozilla.org/en-US/docs/Web/API/Node/parentElement
This might help you.
ParentID = pDoc.offsetParent;
alert(ParentID.id);
Knowing the parent of an element is useful when you are trying to position them out the "real-flow" of elements.
Below given code will output the id of parent of element whose id is provided. Can be used for misalignment diagnosis.
<!-- Patch of code to find parent -->
<p id="demo">Click the button </p>
<button onclick="parentFinder()">Find Parent</button>
<script>
function parentFinder()
{
var x=document.getElementById("demo");
var y=document.getElementById("*id of Element you want to know parent of*");
x.innerHTML=y.parentNode.id;
}
</script>
<!-- Patch ends -->
I had to do recently something similar, I used this snippet:
const getNode = () =>
for (let el = this.$el; el && el.parentNode; el = el.parentNode){
if (/* insert your condition here */) return el;
}
return null
})
The function will returns the element that fulfills your condition. It was a CSS class on the element that I was looking for. If there isn't such element then it will return null
In case somebody would look for multiple elements it only returns closest parent to the element that you provided.
My example was:
if (el.classList?.contains('o-modal')) return el;
I used it in a vue component (this.$el) change that to your document.getElementById function and you're good to go. Hope it will be useful for some people ✌️
If I do this:
function getAllTextNodes(root) {
root = $(root || "body");
return root.find("*:not(iframe)").contents().filter(function() {
return this.nodeType === 3 && //Node.TEXT_NODE = 3
$.trim(this.nodeValue) !== "";
});
}
getAllTextNodes($.parseHTML("<div><div>a<div>sub</div>b</div></div>"))
the result is an array with "a", "b" and "sub". So it seems that they traverse the structure and when they reach an element they work on that element entirely before they continue with the nested elements.
While this may make sense (or it doesn't matter in some cases) it causes some troubles on my end, because I need a logic that returns the elements in the exact same order they appear in the DOM-tree, i.e. I'd be happy to see "a", "sub" and "b" being returned.
Is that something that jQuery built on purpose? Can I change the order somehow? Or is this a bug?
Is that something that jQuery built on purpose? Or is this a bug?
I don't think it's done on purpose, but given that selector APIs and even most modification methods do have their results in DOM order, it might be considered a bug. From what you show, it looks like contents is implemented with a simple flatMap(el => el.childNodes).
Can I change the order somehow?
Yes, you can use jQuery.uniqueSort() on the jQuery object, which uses Node.compareDocumentPosition internally:
return $.uniqueSort(root.find("*:not(iframe)").contents().filter(function() {
return this.nodeType === 3 && $.trim(this.nodeValue) !== "";
}));
However, jQuery isn't great with text nodes anyway. It might be simpler to use a native DOM API here, such as NodeIterator:
const it = document.createNodeIterator(root[0], NodeFilter.SHOW_TEXT, node => node.data.trim() != ""),
res = [];
for (let node; node = it.nextNode(); )
res.push(node);
return res;
How can I get text from a node so that it returns it with whitespace formatting like "innerText" does, but excludes descendant nodes that are hidden (style display:none)?
UPDATE: As the OP points out in comments below, even though MDN clearly states that IE introduced innerText to exclude hidden content, testing in IE indicates that is not the case. To summarize:
Chrome: innerText returns text only from visible elements.
IE: innerText returns all text, regardless of the element's visibility.
Firefox: innerText is undefined (as indicated by the W3C, and in my testing).
Add all of this up, and you have a property to avoid like the plague. Read on for the solution....
If you want cross-browser compatibility, you'll have to roll your own function. Here's one that works well:
function getVisibleText( node ) {
if( node.nodeType === Node.TEXT_NODE ) return node.textContent;
var style = getComputedStyle( node );
if( style && style.display === 'none' ) return '';
var text = '';
for( var i=0; i<node.childNodes.length; i++ )
text += getVisibleText( node.childNodes[i] );
return text;
}
If you want to get painfully clever, you can create a property on the Node object so that this feels more "natural". At first I thought this would be a clever way to polyfill the innerText property on Firefox, but that property is not created as a property on the Node object prototype, so you would really be playing with fire there. However, you can create a new property, say textContentVisible:
Object.defineProperty( Node.prototype, 'textContentVisible', {
get: function() {
return getVisibleText( this );
},
enumerable: true
});
Here's a JsFiddle demonstrating these techniques: http://jsfiddle.net/8S82d/
This is interesting, I came here because I was looking for why the text of display:none elements was omitted in Chrome.
So, this was ultimately my solution.
Basically, I clone the node and remove the classes/styling that set display:none.
How to add hidden element's innerText
function getInnerText(selector) {
let d = document.createElement('div')
d.innerHTML = document.querySelector(selector).innerHTML.replaceAll(' class="hidden"', '')
return d.innerText
}
Let's say I got a DOM element, as a param of an event, for example click.
$(document).click(function() {
myElement = $(this);
});
How can I check later that myElement is still in the DOM?
I can't use .length or any other things like that because it still refer to the saved element and the state of the DOM at this moment, right?
You can check element parent:
function isInDom(obj) {
var root = obj.parents('html')[0]
return !!(root && root === document.documentElement);
}
if(isInDom(myElement)) {
...
}
Here's working fiddle: http://jsfiddle.net/vnxhQ/7/
You're probably looking for Node.isConnected.
The only reliable way I see so far is to check if the element is inside document.getElementsByTagName('*')
function isInDoc(element)
{
var elements=document.getElementsByTagName(element.tagName);
for(var i=0;i<elements.length;++i)
{
if(elements[i]===element)return true;
}
return false;
}
Demo: http://jsfiddle.net/doktormolle/hX8eN/
<edit>
Node.contains() seems to be supported by all major browsers, so I would finally suggest this:
if(document.documentElement.contains(myElement))
Demo: http://jsfiddle.net/doktormolle/LZUx3/
In order to test if an element still exists in the DOM, you need to crawl the DOM again:
// store it in some shared scope
var myElement;
$(document).click(function() {
myElement = $(this);
});
// sometime later...
if ($('#' + myElement.attr('id')).length > 0) {
// it still exists
} else {
// it no longer exists
}
Your clicked elements must all have ids for this to work, though. A class or any other selector could be used instead.
Edit: see this question for ideas on how to get a unique selector for any given DOM element.
This is assuming you would have the id of possible 'clickable' elements set
var idRef;
$(document).on('click', function() {
idRef = this.id;
});
later..
var exists = document.getElementById(idRef).length > 0;
You can the undocumented .selector property of a jQuery object to see if an element is still in the DOM, on the condition that it uses a unique ID. Obvious
http://jsfiddle.net/mblase75/CC2Vn/
$two = $('#two');
console.log($($two.selector).length); // 1
$two.remove();
console.log($($two.selector).length); // 0
See this question for more about how to get the selector of a jQuery object, which may or may not uniquely describe the DOM element(s) it contains.
Just to add something to the fray:
This is really Inferpse's answer slightly tweaked for Dr.Molle's corner case of creating another body element that might house the element removed from the general DOM tree (or, of course, maybe the element was never in the DOM in the first place.) Like Inferspe's answer, it takes a jQuery wrapped object, not the element itself.
function isInDom(jqobj) {
var someBody = jqobj.parents('body');
return someBody.length > 0 && someBody.get(0) === document.body;
}
I must admit I'm having trouble figuring out how I might try to break that.
Edit: Oh yeah... jsFiddle: http://jsfiddle.net/vnxhQ/5/
Edit II: of course, if we aren't talking about link or script elements (anything that might go into the head, not the body, I guess that might work fine :-/
Perhaps a better way of implementing Inferpse's function is by extending jQuery:
jQuery.fn.extend({
isInDom: function() {
var root = this.eq(0).parents('html')[0];
return !!(root && root === document.documentElement);
}
})
Usage:
$("div").isInDom() //returns true if your dom contains a div
$("<div />").isInDom() //returns false
$().isInDom() //returns false
This should be really simple but I'm having trouble with it. How do I get a parent div of a child element?
My HTML:
<div id="test">
<p id="myParagraph">Testing</p>
</div>
My JavaScript:
var pDoc = document.getElementById("myParagraph");
var parentDiv = ??????????
I would have thought document.parent or parent.container would work but I keep getting not defined errors. Note that the pDoc is defined, just not certain variables of it.
Any ideas?
P.S. I would prefer to avoid jQuery if possible.
You're looking for parentNode, which Element inherits from Node:
parentDiv = pDoc.parentNode;
Handy References:
DOM2 Core specification - well-supported by all major browsers
DOM2 HTML specification - bindings between the DOM and HTML
DOM3 Core specification - some updates, not all supported by all major browsers
HTML5 specification - which now has the DOM/HTML bindings in it
If you are looking for a particular type of element that is further away than the immediate parent, you can use a function that goes up the DOM until it finds one, or doesn't:
// Find first ancestor of el with tagName
// or undefined if not found
function upTo(el, tagName) {
tagName = tagName.toLowerCase();
while (el && el.parentNode) {
el = el.parentNode;
if (el.tagName && el.tagName.toLowerCase() == tagName) {
return el;
}
}
// Many DOM methods return null if they don't
// find the element they are searching for
// It would be OK to omit the following and just
// return undefined
return null;
}
Edit 2021
Element.closest is part of the DOM standard. It takes a selector as an argument and returns the first matching ancestor or null if there isn't one.
The property pDoc.parentElement or pDoc.parentNode will get you the parent element.
var parentDiv = pDoc.parentElement
edit: this is sometimes parentNode in some cases.
https://developer.mozilla.org/en-US/docs/Web/API/Node/parentElement
This might help you.
ParentID = pDoc.offsetParent;
alert(ParentID.id);
Knowing the parent of an element is useful when you are trying to position them out the "real-flow" of elements.
Below given code will output the id of parent of element whose id is provided. Can be used for misalignment diagnosis.
<!-- Patch of code to find parent -->
<p id="demo">Click the button </p>
<button onclick="parentFinder()">Find Parent</button>
<script>
function parentFinder()
{
var x=document.getElementById("demo");
var y=document.getElementById("*id of Element you want to know parent of*");
x.innerHTML=y.parentNode.id;
}
</script>
<!-- Patch ends -->
I had to do recently something similar, I used this snippet:
const getNode = () =>
for (let el = this.$el; el && el.parentNode; el = el.parentNode){
if (/* insert your condition here */) return el;
}
return null
})
The function will returns the element that fulfills your condition. It was a CSS class on the element that I was looking for. If there isn't such element then it will return null
In case somebody would look for multiple elements it only returns closest parent to the element that you provided.
My example was:
if (el.classList?.contains('o-modal')) return el;
I used it in a vue component (this.$el) change that to your document.getElementById function and you're good to go. Hope it will be useful for some people ✌️