0

I wrote some code which extract the count from all chars of the strings in a class.

Is there a simple way with Vanilla JavaScript to get alle Characters of the strings without call every element of the class?

let allNews = "";
    allNews = allNews + document.getElementsByClassName("news-link")[0].innerHTML;
    allNews = allNews + document.getElementsByClassName("news-link")[1].innerHTML;
    allNews = allNews + document.getElementsByClassName("news-link")[2].innerHTML;
    allNews = allNews + document.getElementsByClassName("news-link")[3].innerHTML;
    allNews = allNews + document.getElementsByClassName("news-link")[4].innerHTML;
    allNews = allNews + document.getElementsByClassName("news-link")[5].innerHTML;
    allNews = allNews + document.getElementsByClassName("news-link")[6].innerHTML;
const allChars = allNews.length;
console.log(allChars)
<div class="ticker">
    <div class="news">
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
    </div>
</div>
PlatoRG
  • 15

2 Answers2

1

Yes: Use a loop, and count the thing you actually want (allChars). That also has the advantage of being more robust -- it'll work with fewer rows, or more rows.

const elements = document.getElementsByClassName("news-link");
let allChars = 0;
for (const {innerHTML} of Array.from(elements)) {
    allChars += innerHTML.length;
}
console.log(allChars)
<div class="ticker">
    <div class="news">
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
    </div>
</div>

I'd suggest using textContent rather than innerHTML in the general case, but it depends on what you're doing.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    [`document.getElementsByClassName("news-link")[0]` is really not good.](https://stackoverflow.com/questions/54952088/how-to-modify-style-to-html-elements-styled-externally-with-css-using-js/54952474#54952474) ;) – Scott Marcus Feb 05 '21 at 16:10
  • Thank You! Can I ask you for a part of your loop? I don't know how this part works "const {innerHTML}" – PlatoRG Feb 05 '21 at 16:12
  • 1
    @ScottMarcus - It's absolutely fine to use `getElementsByClassName` if you want to. It's not going anywhere, doesn't require the browser to parse a CSS selector, and the browser may well already have the list lying around to give you. **I** almost never do, because I usually want a snapshot rather than a live list, but... – T.J. Crowder Feb 05 '21 at 16:13
  • 1
    @PlatoRG - It's called *destructuring*, basically it picks out that property from the object. So `const {b} = a;` is effectively the same as `const b = a.b;`. Details [on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) (and in Chapter 7 of my book ;-) ). – T.J. Crowder Feb 05 '21 at 16:15
0

A couple of things first:

.getElementsByClassName() returns a "live" node list (a list that is kept up do date for all matching elements every time you access it) and as a result has a higher performance overhead than a "static" node list (a list that finds the matching elements at the time you perform the search), so don't use a live node list when you don't need one. Additionally, because of how a live node list works, accessing one from within a loop can really hurt performance. Instead, use .querySelectorAll(), which returns a static node list and you can immediately loop over its results with .forEach().

Also, .getElementsByClassName() (like many other methods) returns a collection, which certainly can be indexed, but if that's your goal, only do the search once and get the collection stored with a variable reference and then you can continuously index that collection as often as you like without creating the collection over and over.

Next, avoid .innerHTML when you can because there are security and performance implications in using it. In your case, you don't even have any HTML in the strings you want to look at, so there's no need to invoke the HTML parser here. Instead, use .textContent to get the string data.

Putting all these things together and using modern code, it's actually much simpler than what you were doing:

let allNews = "";
// Locate all the elements that use the news-link class and 
// loop over the collection of them
document.querySelectorAll(".news-link").forEach(function(link){
  allNews += link.textContent; // Add the character count to the total
});

console.log(allNews.length);
<div class="ticker">
    <div class="news">
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
        <span><a href="#" class="news-link">OTHER NEWS</a></span>
    </div>
</div>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • *"...without creating the collection over and over..."* Calling `getElementsByClassName` a second time gives you back the **same** collection. It doesn't create a new one. `document.getElementsByClassName("foo") === document.getElementsByClassName("foo")` is true. Personally, I think it's awful style, :-) but it doesn't create a new collection each time. – T.J. Crowder Feb 05 '21 at 16:23
  • @T.J.Crowder That just broke my brain! Must be because of the live nature of the collection as doing that with a static node list returns `false`. Still though, I think it's good advice even if not actually the case here because you still have to check on the live node list over and over and then referencing it within the loop add to the chaos. – Scott Marcus Feb 05 '21 at 16:28
  • Thanks a lot! That was very instructive. – PlatoRG Feb 05 '21 at 16:28
  • @ScottMarcus - Blew my mind the first time I learned it too. :-) Yes, `querySelectorAll` has to build a new `NodeList` every time because it's a snapshot, but the `getElementsBy...` functions reuse the existing live collections (which may or may not *already* exist for the browser's own purposes). This reuse is not **required** by the spec (it just says it "[may](https://dom.spec.whatwg.org/#concept-getelementsbyclassname)" be the same collection) but it's universal as far as I know...probably because an early browser implemented `getElementsByTagName` that way and people relied on it. – T.J. Crowder Feb 05 '21 at 16:35