Sayantan Das
Sayantan Das

Reputation: 1641

replace all elements belonging to a specific class

I was trying to develop an embedded widget. User would include an anchor tag and a javascript in their page, and it would render the content. Similar to embedded tweets.

<a href="http://localhost:3000/user/13"
        target="_blank"
        class="my-widget" 
        data-widget-type="profile" 
        data-widget-identifier="id" 
        data-identifier-value="13"
   >Sayantan Das</a>
</div>
<script src="//localhost/my-widget/js/widget.js" async ></script>

And from widget.js, i would get all the elements with class="my-widget" and replace with an iframe.

widgets.js

!function(global, document) {
    global.MyWidgets = global.MyWidgets || {};

    for(let widgets = document.getElementsByClassName('my-widget'), i = 0; i < widgets.length; i++) {
        console.log(widgets)
        let element = widgets[i];
        let span = document.createElement('span');

        span.innerHTML = "Changed from widget " + i;
        element.parentNode.appendChild(span);
        element.parentNode.removeChild(element);
    }

}(window, document);

The problem is , when I remove the element the loop also runs for a shorter number. for example, if there are two elements with class my-widget, after first time the loop runs and one element is removed and the loop runs only once. How do I replace all the elements?

Upvotes: 0

Views: 56

Answers (2)

Abdullah Khan
Abdullah Khan

Reputation: 645

use document.querySelectorAll to the length of the widgets

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1074218

That's because getElementsByClassName returns a live HTMLCollection; when you remove the class="my-widget" element from the DOM, it's also removed from the collection.

Either work backward through the collection (so you're removing from the end, which doesn't affect the ones before it), or use querySelectorAll(".my-widget") instead, which returns a snapshot NodeList, not a live HTMLCollection.

Working backward:

for(let widgets = document.getElementsByClassName('my-widget'), i = widgets.length - 1; i >= 0; i--) {

Using qSA instead:

for(let widgets = document.querySelectorAll('.my-widget'), i = 0; i < widgets.length; i++) {

or if you don't need i (you seem only to be using it to get the element and for demo purposes), you can use for-of with NodeLists now (on most platforms; this answer has a polyfill for others):

for (const element of document.querySelectorAll('.my-widget')) {
    // ...and remove the `let element = ...` line

Upvotes: 1

Related Questions