I'm new to javascript. I created this div called colorme. I can successfully color it via javascript. Now assuming i want to change the background of <p>...</p>, or <span>,etc how do i reach it via Javascript? (no jquery).
Like document.getElementById() would work on the div and i reach it. Now i cannot keep giving unique id's to all the elements. How do i reach the inner elements like <p> or <span>, etc?
<div id="colorme">
<p>Blah Vblah Blah Content</p>
<span>Blah Vblah Blah Content</span>
</div>
You can use the element that you've found as a context for getElementsByTagName.
var colorme = document.getElementById('colorme'),
spans = colorme.getElementsByTagName('span');
Note that spans is a NodeList -- similar to an array -- containing all the span elements within colorme. If you want the first one (indeed, the only one in your code sample), use spans[0].
You should check out the many DOM traversal functions provided in standard javascript.
Tutorial: http://www.quirksmode.org/dom/intro.html
Reference: http://reference.sitepoint.com/javascript/Node
and http://reference.sitepoint.com/javascript/Element
Although the answers do give good ways to do it for this specific case....
The issue you're facing is called DOM-traversal. As you know, the DOM is a tree, and you can actually traverse the tree without knowing in advance the element id/type/whatever.
The basics are as follows
el.childNodes to access a list of children
el.parentNode to access the parent element
nextSibling and previousSibling for next and previous sibling nodes
For further info, see [MDC DOM pages](
Here are three ways:
If you only care about decent browsers, document.querySelector (returns the first matching node) and document.querySelectorAll (returns a NodeList) - e.g. document.querySelector('#colorme p').
HTMLElement.getElementsByTagName() (returns a NodeList) - e.g. document.getElementById('colorme').getElementsByTagName('p')[0]
HTMLElement.children, etc. - document.getElementById('colorme').children[0] (.firstChild will probably be a text node, lots of fun DOM stuff to get into there, the quirksmode DOM intro linked to is good stuff).
It's quite simple: getElementsByTagName()?
You could use getElementsByTagName()
Loop through the children:
var div = document.getElementById('colorme');
var i, l, elem;
for (i = 0, l = div.childNodes.length; i < l; i++) {
elem = div.childNodes[i];
// Check that this node is an element
if (elem.nodeType === 1) {
elem.style.color = randomColorGenerator();
}
}
In this case you can use:
var colormeDiv = document.getElementById('colorme');
var e1 = colormeDiv.getElementsByTagName('p');
var e2 = colormeDiv.getElementsByTagName('span');
to get the two elements inside 'colorme' div.
getElementById is just one of JavaScript's DOM methods. It returns an HTMLElement DOM object which you can then query to find child, parent and sibling elements. You could use this to traverse your HTML and find the elements you need. Here's a reference for the JavaScript DOM HTMLObject.
[after answering, I realised this is no answer to your fully explained question, but it is the answer to the question raised in the title of your post!]
One nice way of doing this is declaring a global var on the top of your Javascript that refers to the document, which can then be used everywhere (in every function):
<html>
<head>
<script type="text/javascript">
// set a global var to acces the elements in the HTML document
var doc = this;
function testIt()
{
doc.blaat.innerHTML = 'It works!!';
}
</script>
</head>
<body>
<div id="blaat">Will this ever work..?!</div>
<button onclick="testIt()">Click me and you'll see!</button>
</body>
</html>
As my first impression when I got to 'getElemenyById()' was that it sounds like a function that will iterate through the DOM's element list until it finds the element you need; this must take some time. With the above example, you simply access the element directly.
I'm not sure if I'm really saving CPU / adding speed this way, but at least it feels that way :)
Related
what's the different between using:
// assuming using elements/tags 'span' creates an array and want to access its first node
1) var arrayAccess = document.getElementsByTagName('elementName')[0]; // also tried property items()
vs
// assuming I assign an id value to the first span element/tag
// specifically calling a node by using it's id value
2) var idAccess = document.getElementById('idValue');
then if I want to change the text node....when using example 1) it will not work, for example:
arrayAccess.firstChild.nodeValue = 'some text';
or
arrayAccess.innerText/innerHTML/textContent = 'some text';
If I "access" the node through its id value then it seems to work fine....
Why is it that when using array it does not work? I'm new to javascript and the book I'm reading does not provide an answer.
Both are working,
In your first case you need to pass the tag name instead of the element name. Then only it will work.
There might be a case that you trying to set input/form elements using innerHTML. At that moment you need to use .value instead of innerHTML.
InnerHTML should be used for div, span, td and similar elements.
So your html markup example:
<div class="test">test</div>
<div class="test">test1</div>
<span id="test">test2</span>
<button id="abc" onclick="renderEle();">Change Text</button>
Your JS code:
function renderEle() {
var arrayAccess = document.getElementsByTagName('div')[0];
arrayAccess.innerHTML = "changed Text";
var idEle = document.getElementById('test');
idEle.innerHTML = "changed this one as well";
}
Working Fiddle
When you use document.getElementsByTagName('p'), the browser traverses the rendered DOM tree and returns a node list (array) of all elements that have the matching tag.
When you use document.getElementById('something'), the browser traverses the rendered DOM tree and returns a single node matching the ID if it exists (since html ID's are unique).
There are many differences when to use which, but one main factor will be speed (getElementById is much faster since you're only searching for 1 item).
To address your other question, you already have specified that you want the first element in the returned nodeList (index [0]) in your function call:
var arrayAccess = document.getElementsByTagName('elementName')[0];
Therefore, arrayAccess is already set to the first element in the returned query. You should be able to access the text by the following. The same code should work if you used document.getElementById to get the DOM element:
console.log(arrayAccess.textContent);
Here's a fiddle with an example:
http://jsfiddle.net/qoe30w2w/
Hope this helps!
I usually create variables for frequently reused DOM elements like this:
var $dom_element = $('#dom_element);
where this is my setup:
<div id="dom_element">
<div class="child_element">
<div class="child_element">
</div>
what I'm wondering is if I can mix this variable with a subselector to get child elements. I guess it would be something like this:
var $child_element = $($dom_element + ' .child_element);
And if so, is there any speed benefit to doing this versus just saying:
$('.child_element);
considering the fact that both of these elements might be deeply nested in a large site?
With
var $dom_element = $('#dom_element);
I would use the following to get the child elements
var $child_element = $dom_element.find(".child_element");//I prefer this one, it is easier to read.
or
var $child_element = $(".child_element", $dom_element);
From my research/reading, it appears that setting an element to a variable is best if you are going to reference it many times. That way jQuery does not have to search the DOM many times.
Regarding your selectors, you can get yourself into trouble when using a bare class as a child selector. What if you have multiple child nodes using that same class?
.find() works, as others have suggested. You could alternatively use .children():
var $kids = $dom_element.children('.child_element');
http://api.jquery.com/children/
The difference between .find() and .children() is that .children() will only look one level down the DOM tree. .find() will recursively run through all possible child nodes to match your selector.
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(' ');
For instance in the snippet below - how do I access the h1 element knowing the ID of parent element (header-inner div)?
<div id='header-inner'>
<div class='titlewrapper'>
<h1 class='title'>
Some text I want to change
</h1>
</div>
</div>
Thanks!
function findFirstDescendant(parent, tagname)
{
parent = document.getElementById(parent);
var descendants = parent.getElementsByTagName(tagname);
if ( descendants.length )
return descendants[0];
return null;
}
var header = findFirstDescendant("header-inner", "h1");
Finds the element with the given ID, queries for descendants with a given tag name, returns the first one. You could also loop on descendants to filter by other criteria; if you start heading in that direction, i recommend you check out a pre-built library such as jQuery (will save you a good deal of time writing this stuff, it gets somewhat tricky).
If you were to use jQuery as mentioned by some posters, you can get access to the element very easily like so (though technically this would return a collection of matching elements if there were more than one H1 descendant):
var element = $('#header-inner h1');
Using a library like JQuery makes things like this trivial compared to the normal ways as mentioned in other posts. Then once you have a reference to it in a jQuery object, you have even more functions available to easily manipulate its content and appearance.
If you are sure that there is only one H1 element in your div:
var parent = document.getElementById('header-inner');
var element = parent.GetElementsByTagName('h1')[0];
Going through descendants,as Shog9 showed, is a good way too.
It's been a few years since this question was asked and answered. In modern DOM, you could use querySelector:
document.querySelector('#header-inner h1').textContent = 'Different text';
<div id='header-inner'>
<div class='titlewrapper'>
<h1 class='title'>
Some text I want to change
</h1>
</div>
</div>
The simplest way of doing it with your current markup is:
document.getElementById('header-inner').getElementsByTagName('h1')[0].innerHTML = 'new text';
This assumes your H1 tag is always the first one within the 'header-inner' element.
To get the children nodes, use obj.childNodes, that returns a collection object.
To get the first child, use list[0], that returns a node.
So the complete code should be:
var div = document.getElementById('header-inner');
var divTitleWrapper = div.childNodes[0];
var h1 = divTitleWrapper.childNodes[0];
If you want to iterate over all the children, comparing if they are of class “title”, you can iterate using a for loop and the className attribute.
The code should be:
var h1 = null;
var nodeList = divTitleWrapper.childNodes;
for (i =0;i < nodeList.length;i++){
var node = nodeList[i];
if(node.className == 'title' && node.tagName == 'H1'){
h1 = node;
}
}
Here I get the H1 elements value in a div where the H1 element which has CSS class="myheader":
var nodes = document.getElementById("mydiv")
.getElementsByTagName("H1");
for(i=0;i<nodes.length;i++)
{
if(nodes.item(i).getAttribute("class") == "myheader")
alert(nodes.item(i).innerHTML);
}
Here is the markup:
<div id="mydiv">
<h1 class="myheader">Hello</h1>
</div>
I would also recommend to use jQuery if you need a heavy parsing for your DOM.
Let's say:
<div>
pre text
<div class="remove-just-this">
<p>child foo</p>
<p>child bar</p>
nested text
</div>
post text
</div>
to this:
<div>
pre text
<p>child foo</p>
<p>child bar</p>
nested text
post text
</div>
I've been figuring out using Mootools, jQuery and even (raw) JavaScript, but couldn't get the idea how to do this.
Using jQuery you can do this:
var cnt = $(".remove-just-this").contents();
$(".remove-just-this").replaceWith(cnt);
Quick links to the documentation:
contents( ) : jQuery
replaceWith( content : [String | Element | jQuery] ) : jQuery
The library-independent method is to insert all child nodes of the element to be removed before itself (which implicitly removes them from their old position), before you remove it:
while (nodeToBeRemoved.firstChild)
{
nodeToBeRemoved.parentNode.insertBefore(nodeToBeRemoved.firstChild,
nodeToBeRemoved);
}
nodeToBeRemoved.parentNode.removeChild(nodeToBeRemoved);
This will move all child nodes to the correct place in the right order.
You should make sure to do this with the DOM, not innerHTML (and if using the jQuery solution provided by jk, make sure that it moves the DOM nodes rather than using innerHTML internally), in order to preserve things like event handlers.
My answer is a lot like insin's, but will perform better for large structures (appending each node separately can be taxing on redraws where CSS has to be reapplied for each appendChild; with a DocumentFragment, this only occurs once as it is not made visible until after its children are all appended and it is added to the document).
var fragment = document.createDocumentFragment();
while(element.firstChild) {
fragment.appendChild(element.firstChild);
}
element.parentNode.replaceChild(fragment, element);
$('.remove-just-this > *').unwrap()
More elegant way is
$('.remove-just-this').contents().unwrap();
Use modern JS!
const node = document.getElementsByClassName('.remove-just-this')[0];
node.replaceWith(...node.childNodes); // or node.children, if you don't want textNodes
oldNode.replaceWith(newNode) is valid ES5
...array is the spread operator, passing each array element as a parameter
Replace div with its contents:
const wrapper = document.querySelector('.remove-just-this');
wrapper.outerHTML = wrapper.innerHTML;
<div>
pre text
<div class="remove-just-this">
<p>child foo</p>
<p>child bar</p>
nested text
</div>
post text
</div>
Whichever library you are using you have to clone the inner div before removing the outer div from the DOM. Then you have to add the cloned inner div to the place in the DOM where the outer div was. So the steps are:
Save a reference to the outer div's parent in a variable
Copy the inner div to another variable. This can be done in a quick and dirty way by saving the innerHTML of the inner div to a variable or you can copy the inner tree recursively node by node.
Call removeChild on the outer div's parent with the outer div as the argument.
Insert the copied inner content to the outer div's parent in the correct position.
Some libraries will do some or all of this for you but something like the above will be going on under the hood.
And, since you tried in mootools as well, here's the solution in mootools.
var children = $('remove-just-this').getChildren();
children.replaces($('remove-just-this');
Note that's totally untested, but I have worked with mootools before and it should work.
http://mootools.net/docs/Element/Element#Element:getChildren
http://mootools.net/docs/Element/Element#Element:replaces
I was looking for the best answer performance-wise while working on an important DOM.
eyelidlessness's answer was pointing out that using javascript the performances would be best.
I've made the following execution time tests on 5,000 lines and 400,000 characters with a complexe DOM composition inside the section to remove. I'm using an ID instead of a class for convenient reason when using javascript.
Using $.unwrap()
$('#remove-just-this').contents().unwrap();
201.237ms
Using $.replaceWith()
var cnt = $("#remove-just-this").contents();
$("#remove-just-this").replaceWith(cnt);
156.983ms
Using DocumentFragment in javascript
var element = document.getElementById('remove-just-this');
var fragment = document.createDocumentFragment();
while(element.firstChild) {
fragment.appendChild(element.firstChild);
}
element.parentNode.replaceChild(fragment, element);
147.211ms
Conclusion
Performance-wise, even on a relatively big DOM structure, the difference between using jQuery and javascript is not huge. Surprisingly $.unwrap() is most costly than $.replaceWith().
The tests have been done with jQuery 1.12.4.
if you'd like to do this same thing in pyjamas, here's how it's done. it works great (thank you to eyelidness). i've been able to make a proper rich text editor which properly does styles without messing up, thanks to this.
def remove_node(doc, element):
""" removes a specific node, adding its children in its place
"""
fragment = doc.createDocumentFragment()
while element.firstChild:
fragment.appendChild(element.firstChild)
parent = element.parentNode
parent.insertBefore(fragment, element)
parent.removeChild(element)
If you are dealing with multiple rows, as it was in my use case you are probably better off with something along these lines:
$(".card_row").each(function(){
var cnt = $(this).contents();
$(this).replaceWith(cnt);
});
The solution with replaceWith only works when there is one matching element.
When there are more matching elements use this:
$(".remove-just-this").contents().unwrap();