204

Alright, I've dabbled in JavaScript before, but the most useful thing I've written is a CSS style-switcher. So I'm somewhat new to this. Let's say I have HTML code like this:

<div id="foo">
    <div class="bar">
        Hello world!
    </div>
</div>

How would I change Hello world! to Goodbye world!?

I know how document.getElementsByClassName and document.getElementById work, but I would like to get more specific. Sorry if this has been asked before.

Jakub Muda
  • 6,008
  • 10
  • 37
  • 56
Tanner Babcock
  • 3,232
  • 6
  • 21
  • 23

8 Answers8

349

Well, first you need to select the elements with a function like getElementById.

var targetDiv = document.getElementById("foo").getElementsByClassName("bar")[0];

getElementById only returns one node, but getElementsByClassName returns a node list. Since there is only one element with that class name (as far as I can tell), you can just get the first one (that's what the [0] is for—it's just like an array).

Then, you can change the html with .textContent.

targetDiv.textContent = "Goodbye world!";

var targetDiv = document.getElementById("foo").getElementsByClassName("bar")[0];
targetDiv.textContent = "Goodbye world!";
<div id="foo">
    <div class="bar">
        Hello world!
    </div>
</div>
Joseph Marikle
  • 76,418
  • 17
  • 112
  • 129
  • 1
    Getting the parent by ID is not necessary in this case. `document.getElementsByClassName` would work fine. – rvighne Dec 24 '13 at 05:05
  • 13
    @rvighne the exact requirements of OP was that he "would like to get more specific". The person asking the question was asking how he could be more specific in his DOM tree traversal. The use of getElementByClassName was not a point of confusion, but I can see how someone might easily think I was indicating both as necessary. They are not. If you want to target only elements of a certain class that are under a particular node of a given ID as opposed to elements of that same class under a different node, this is what you would use. – Joseph Marikle Dec 24 '13 at 14:30
  • 1
    Might be good to edit this answer and change `.innerHtml` to `.textContent` for the security benefit of copy/paste coders. – codescribblr Sep 24 '18 at 18:00
  • And in 2022, how about some optional chaining on pretty much every '.'? – danh Jan 23 '22 at 18:51
21

You can do it like this:

var list = document.getElementById("foo").getElementsByClassName("bar");
if (list && list.length > 0) {
    list[0].innerHTML = "Goodbye world!";
}

or, if you want to do it with with less error checking and more brevity, it can be done in one line like this:

document.getElementById("foo").getElementsByClassName("bar")[0].innerHTML = "Goodbye world!";

In explanation:

  1. You get the element with id="foo".
  2. You then find the objects that are contained within that object that have class="bar".
  3. That returns an array-like nodeList, so you reference the first item in that nodeList
  4. You can then set the innerHTML of that item to change its contents.

Caveats: some older browsers don't support getElementsByClassName (e.g. older versions of IE). That function can be shimmed into place if missing.


This is where I recommend using a library that has built-in CSS3 selector support rather than worrying about browser compatibility yourself (let someone else do all the work). If you want just a library to do that, then Sizzle will work great. In Sizzle, this would be be done like this:

Sizzle("#foo .bar")[0].innerHTML = "Goodbye world!";

jQuery has the Sizzle library built-in and in jQuery, this would be:

$("#foo .bar").html("Goodbye world!");
jfriend00
  • 683,504
  • 96
  • 985
  • 979
7

If this needs to work in IE 7 or lower you need to remember that getElementsByClassName does not exist in all browsers. Because of this you can create your own getElementsByClassName or you can try this.

var fooDiv = document.getElementById("foo");

for (var i = 0, childNode; i <= fooDiv.childNodes.length; i ++) {
    childNode = fooDiv.childNodes[i];
    if (/bar/.test(childNode.className)) {
        childNode.innerHTML = "Goodbye world!";
    }
}
user113716
  • 318,772
  • 63
  • 451
  • 440
John Hartsock
  • 85,422
  • 23
  • 131
  • 146
  • `getElementsByClassName` doesn't work in IE8 either, but you can use `querySelectorAll` in its place (pretty much anyway). – user113716 Oct 19 '11 at 01:04
  • @Ӫ_._Ӫ ... lol...your correct but you just prove my answer even more. You cannot rely on getElementsByClassName if your page needs to function across multiple browsers. jQuery would definately be an option but so whould the above code. – John Hartsock Oct 19 '11 at 01:10
  • Yes, I meant that comment in support of your answer, but then also to point out that it's possible to use `qSA` in a shim so that you can still use native code in IE8. Although taking a closer look at your solution, you've got a few things that need fixing. – user113716 Oct 19 '11 at 01:18
  • @Ӫ_._Ӫ ... curious what do you see...because I am not seeing it. – John Hartsock Oct 19 '11 at 01:20
  • `var i = 0, var child = fooDiv.childNodes[i]` is invalid. `i <= fooDiv.childNodes` doesn't really make sense. `childNode.className.test("bar")` There's no `childNode` variable, and a string doesn't have a `test()` method. – user113716 Oct 19 '11 at 01:22
  • @Ӫ_._Ӫ your correct about i<= fooDiv.childNodes as it should be i <= fooDiv.childNodes.length and my test() is screwed up but...var i = 0, var child = fooDiv.childnodes[i] is completely valid – John Hartsock Oct 19 '11 at 01:26
  • `var i = 0, var child = fooDiv.childnodes[i]` Nope, it's a syntax error. ;) You need to drop the second `var` because `var` is a statement, not an expression. – user113716 Oct 19 '11 at 01:27
  • @Ӫ_._Ӫ...wow my eyes are getting to me...this is what happens when you look at code too much with out testing. – John Hartsock Oct 19 '11 at 01:31
  • That's much better, but your code is only ever testing the element at index `0`, so the loop isn't necessary. If you meant to iterate over all child nodes, you'd need to do `child = childNodes[i]` inside the loop, otherwise you'll just test the first one over and over. In the example in the question, the first `childNode` will be a text node in many browsers, so there's that issue too. ;) – user113716 Oct 19 '11 at 01:35
  • ...fixed it by changing `child` to `childNode`, and moving the assignment inside the loop. Having the assignment at the top would only work if you did `for (var i = 0, childNode; childNode = fooDiv.childNodes[i]; i ++) { ... }` – user113716 Oct 19 '11 at 02:06
6

The easiest way to do so is:

function findChild(idOfElement, idOfChild){
  let element = document.getElementById(idOfElement);
  return element.querySelector('[id=' + idOfChild + ']');
}

or better readable:

findChild = (idOfElement, idOfChild) => {
    let element = document.getElementById(idOfElement);
    return element.querySelector(`[id=${idOfChild}]`);
}
Alex Nolasco
  • 18,750
  • 9
  • 86
  • 81
Benjamin Werner
  • 105
  • 1
  • 4
  • 1
    If the OP had asked about IDs, they could have just used `getElementById` on the `idOfChild`, since IDs must be unique to the document. But the OP asked about a class within an ID. And if you're using `querySelector` anyway, it would be "easier" to just use an appropriate selector: `document.querySelector('#id .class')` – Heretic Monkey May 19 '22 at 12:40
6

We can also make use of CSS selectors with querySelector

var targetDiv = document.querySelector('#foo .bar');
targetDiv.textContent = "Goodbye world!";
rajesh kumar
  • 1,578
  • 16
  • 14
4

you can use getElementById or getElementsByClassName on DOM Elements too. and not just the document

var targetDiv = document.getElementById("foo").getElementsByClassName("bar")[0];
targetDiv.textContent = "Goodbye world!";
<div id="foo">
    <div class="bar">
        Hello world!
    </div>
</div>
Abilogos
  • 4,777
  • 2
  • 19
  • 39
Jhonny SAN
  • 41
  • 4
  • While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. – Jean-Francois Gagnon Nov 15 '21 at 22:24
3

Recursive function :

function getElementInsideElement(baseElement, wantedElementID) {
    var elementToReturn;
    for (var i = 0; i < baseElement.childNodes.length; i++) {
        elementToReturn = baseElement.childNodes[i];
        if (elementToReturn.id == wantedElementID) {
            return elementToReturn;
        } else {
            return getElementInsideElement(elementToReturn, wantedElementID);
        }
    }
}
  • There is a minor bug in the sample above. Instead of instantly returning in the else path you have to check if something was found first. Otherwise the other child nodes will not be looped through. Something like this: if(elementToReturn) { return elementToReturn; } – Jannik Apr 20 '18 at 11:23
-8

You should not used document.getElementByID because its work only for client side controls which ids are fixed . You should use jquery instead like below example.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>                                                                                                             
<div id="foo">
   <div class="bar"> 
          Hello world!
     </div>
</div>

use this :

$("[id^='foo']").find("[class^='bar']")

// do not forget to add script tags as above

if you want any remove edit any operation then just add "." behind and do the operations

Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • 18
    Using a thermonuclear weapon to kill a deer will work, but is slight overkill. Using a multi-kb jQuery library to select an element when Javascript offers that functionality in it's base code is slightly more overkill. – Danejir Jul 25 '16 at 03:09
  • This answer only makes sense if you are using a framework like ASP.NET WebForms or some other server-side framework that creates IDs for you, ending with original, user-entered ID. No framework I'm aware of modifies CSS classes, so there is no reason to use an attribute selector on `class`. – Heretic Monkey May 19 '22 at 12:45