Question is simple but confusing for me that when i console.log(document.body) or (document.head) both are working fine but when i do with document.script or document.html these two are not working why ? although all these things are in the document ?
Q2) i can write
document.getElementById('something')
but why i can't write
document.body.getElementById('something')
although body is in the document and element in the body tag as well, while sometime document.body works at different stages in script
getElementById is a method on document, which is an object that uses the Document interface from the DOM. It's not a method on elements (the Element interface from the DOM and its specialization the HTMLElement interface from HTML). document.body is an element (an HTMLBodyElement, which is an HTMLElement, which is an Element), not a document.
Some methods (like querySelector) are methods on both document and elements, because it makes sense for them to be (on an element, querySelector only looks within the element, not throughout the document). But getElementById isn't. (It could be, but it would be a bit odd to scope it to just an element when IDs are meant to be unique throughout the document.)
Related
I have a custom-element with shadow DOM, which listens to attribute target change.
target is supposed to be the ID of the element which my component is supposed to be attached to.
I've tried using querySelector and getElementById to get the element of the outer DOM, but it always returns null.
console.log(document.getElementById(target));
console.log(document.querySelector('#' + target));
Both of the above return null.
Is there a way to get a reference to the element in the parent document from within shadow DOM?
You just have to call ShadowRoot.
this.shadowRoot.getElementById('target') should work.
Here's an example, the get syntax will bind an object property to a function.
get target() {
return this.shadowRoot.getElementById('target');
}
There are two use cases of shadow DOM as far as I can see:
You control the the shadow DOM solely through your hosting custom element (like in the answer of #Penny Liu). If you want make sure no other script should call and alter the nodes than this is your choice. Pretty sure some banking websites use this method. You give up on flexibility though.
You just want to scope some parts of your code for styling reasons but you like to control it via document.getElementById than you can use <slot>. After all, many libraries rely on the document object and will not work in shadow DOM.
Back to the problem, what you probably did was something like this:
shadowRoot.innerHTML = `...<script>document.getElementById('target')</script>`
// or shadowRoot.appendChild
This is NOT working! And this is not how shadow DOM was anticipated to work either.
Recalling method 2, you SHOULD fill your shadow DOM solely by <slot> tags. Most minimal example:
<!-- Custom Element -->
<scoped-playground>
<style>some scoped styling</style>
<div id="target"></div>
<script>const ☝☝☝☝ = document.getElementById('target')</script>
</scoped-playground>
<!-- Scoped playground has a shadowRoot with a default <slot> -->
...
this.shadowRoot.innerHTML = "<slot>Everything is rendered here</slot>";
...
More advanced <slot> examples can be found at:
https://developers.google.com/web/fundamentals/web-components/shadowdom#composition_slot
I am just beginning to learn client-side JavaScript and using an online tutorial, so please bear with me.
This question is based on my understanding of the following:
To access the properties of the document's body, the syntax is "document.body", which returns all the elements in the body.
Similarly when you access the head, you use "document.head". Makes sense and most importantly, it works.
However, when I attempt to access elements WITHIN the body or head following the same logic, I get a return value of "undefined". For example, document.body.h1, returns "undefined", in spite of there being an h1 element inside the body element.
Further, when I enter document.head.title -- "undefined".
Strangely, however, when I enter "document.title", it returns the string value associated with the title tag.
I thought in order to access the title, you would have to access it through the head, since it is an element nested inside the head. But ok, that's fine. Using the same logic, I should then be able to enter document.h1 and get its value. Nope, instead, I get undefined.
Would someone be kind enough to explain to me why this behavior is so inconsistent. Thanks in advance.
You've really asked two questions:
Why document.title rather than document.head.title?
and
Why doesn't document.body.h1 return an element if there's an h1 in the body?
document.title
document.title is historical. Various parts of the browser environment were developed somewhat ad hoc by multiple different people/organizations in the 1990s. :-) That said, it's the title of the document, so this isn't an unreasonable place to put it, even if you use the title tag in head.
document.body.h1
One answer is: Because no one decided to design it that way. There were some early things like document.all (a list of all elements in the document) and even tag-specific ones (I forget exactly what they were, but they weren't a million miles off your document.body.h1 — I think document.tags.h1 or something, where again it was a list.)
But another answer is: Because the DOM is a tree. body can have multiple h1 elements, both as direct children and as children of children (or deeper); collectively, descendants. Creating automatic lists with all of these proved not to be scalable to large documents.
Instead, you can query the DOM (either the entire document, or just the contents of a specific element) via a variety of methods:
getElementById - (Just on document) Get an element using its id attribute value.
querySelector - Find the first element matching a CSS selector (can use it on document or on an element). Returns null if there were no matches.
querySelectorAll - Get a list of all elements matching a CSS selector (can use it on document or on an element). You can rely on getting back a list; its length may be 0, of course.
getElementsByTagName - Get a list of all elements with a given tag name (such as "h1").
getElementsByClassName - (No support in IE8 and earlier) Get a list of all elements with a given class.
There are many more. See MDN's web documentation and/or the WHAT-WG DOM Standard for more.
Some of the automatic lists persist (they got so much use that they had to be maintained/kept), such as document.forms, document.links, the rows property on HTMLTableElement and HTMLTableSectionElement instances, the cells property on HTMLTableRowElement instances, and various others.
document.head.title is a thing... but not what you might think.
title is an attribute that is applicable to all html elements; that is, it is a global attribute. It's meaning is 'advisory information'; one use is to display a tooltip:
<span title="hover over me and you'll see this">information</span>
So, all elements have a title attribute - including head. The title element - which is completely different - should be a child of the head though. So you might be tempted to set its value via document.head.title = "my title" , but document.head.title is not the head's title element, it's a property of the head element.
What you're actually doing is setting the title property on the head element:
<head title="my title">.... </head>
... which isn't what you want at all.
The correct way to set the title is document.title, which is a shortcut way of doing
document.querySelector("title").innerText = "my title"
I have a jquery element. and I remove it from DOM by using remove(), but jQuery still keep a reference of it.
and later I still can use it and insert it into DOM.
How to detect that this "var p" is in DOM or off DOM ?
var p=$('p');
p.remove();
console.log(p);
p.insertAfter($('body'));
I think p.parent() is more easy way to go. if it in DOM it will get another DOM node
use javascript length to check if dom element exists or not
if($("p").length>0)
{
// p exists
}
var p=$('p');
p.remove();
So when you did p.remove(), it is removed from the dom but it still exists in memory as a stand alone dom node object with all its contents intact.
You can perform any operation as in normal dom element like append anywhere, change contents or change attributes.
The only difference is that it is not part of the document unless you append it in the html.
jQuery has contains method to check if the element is part of the document
jQuery.contains(document, $foo[0]));
p.parent() in case of removed p will return a 0 length jquery object because p is independent node and has no parent.
I got puzzled about it .
1 Is document equal to document.documentElement? I think they are both root node.
2 Why I can use document.documentElement.getElementsByTagName() but I can not use
document.documentElement.getElementById()?
There is a difference between the document object and the document element.
When an HTML document is loaded into a web browser, it becomes a document object.
The document object is the root node of the HTML document and the common ancestor of all other nodes, such as element nodes (including the document element), text nodes and attribute nodes.
One of the differences is that an element has getElementsByTagName() but not getElementById(), which is part of the document itself.
To successfully use an element to get another one based on ID, you need to go through its document:
var elem2 = elem1.ownerDocument.getElementById(whatever)
I have the following jQuery line:
$('<html>hi</html>').find('a')
I expect the result to be a wrapped set of one element. However the result is an empty array ([]). Why?
-- EDIT --
For some reason the code below works.
$('<html><div>hi</div></html>').find('a');
Why is this happening?
That's because the html element is stripped when the string is parsed:
> $('<html>hi</html>')
[hi]
i.e. the current collection contains an element that you are trying to find(). As the top-level a element doesn't (and can't) have a descendants the find() call will return an empty collection.
From jQuery documentation:
When passing in complex HTML, some browsers may not generate a DOM that exactly replicates the HTML source provided. As mentioned, jQuery uses the browser's .innerHTML property to parse the passed HTML and insert it into the current document. During this process, some browsers filter out certain elements such as <html>, <title>, or <head> elements. As a result, the elements inserted may not be representative of the original string passed.
edit: The second snippet can find() a element as when the html element is stripped the top-level element of the collection is a div element that does have a descendant.
As in the Documentation of .find() descriped
Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element.
$('<html>hi</html>')
will just provide an Object of your a-tag.
Demo
If there are multiple anchor-tags inside your html-string you can filter them, e.g.:
var elem = $('<html>hihi</html>');
var filter = elem.filter(function(){
return $(this).attr('href') === "cnn.com";
});
Demo
Edit
When passing in complex HTML, some browsers may not generate a DOM
that exactly replicates the HTML source provided. As mentioned, jQuery
uses the browser's .innerHTML property to parse the passed HTML and
insert it into the current document. During this process, some
browsers filter out certain elements such as <html>, <title>, or
<head> elements. As a result, the elements inserted may not be
representative of the original string passed.
Source: http://api.jquery.com/jQuery/#jQuery2 down to the Paragraph Creating New Elements
So jQuery uses .innerHTML. According to the docs
Removes all of element's children, parses the content string and
assigns the resulting nodes as children of the element.
So the html-string <html>test</html> gets stripped to <a></a>.
When wrapping a div around the anchor, the anchor stays a descendat of an elemnt and therefore gets found by the .find()-function.
You should read the documentation at Jquery docs about find()
$('html').find('a');
Check this jsfiddle