Issues with iterating HTMLCollection proxyobject

What I’m trying to do:

I’m trying to programmatically remove classes from dom elements.

I have the following elements with their respective classes in my standard-page.html:

<a class="sidebar-toggle frame-hidden" anvil-drop-slot="top-left-btn" href="javascript:void(0)"><i class="fa fa-bars"></i></a>
<div class="left-nav frame-hidden">
<div class="content frame-hidden">

As you can see, I’ve added the .frame-hidden class to the Rally template’s .sidebar-toggle, .left-nav and .content.

At some point in my app I want to remove the .frame-hidden class from these 3 elements. I do it in the following manner:

frame_hidden_elements = window.document.getElementsByClassName('frame-hidden')

for element in frame_hidden_elements:
  element.classList.remove("frame-hidden")

What’s the problem

The issue is that this loop does not remove the .frame-hidden class from .left-nav even though it successfully removes it from .content and sidebar-toggle

Here are some prints added to the code and the output I am seeing:

Code

frame_hidden_elements = window.document.getElementsByClassName('frame-hidden')
print(len(frame_hidden_elements))

for element in frame_hidden_elements:
  print(element)
  element.classList.remove("frame-hidden")

frame_hidden_elements = window.document.getElementsByClassName('frame-hidden')
print(len(frame_hidden_elements))

Output

3
<HTMLAnchorElement proxyobject>
<HTMLDivElement proxyobject>
1

As you can see, for whatever reason the .left-nav is not accessed in the for loop. It is neither printed nor removed.

Debugging

During the course of my debugging, I also tried to index the HTMLCollection manually in the following manner:

for i in [0,1,2]:
  element = frame_hidden_elements[i]
  print(element)
  element.classList.remove("frame-hidden")

This produced the following error on the third iteration of the loop:

<HTMLAnchorElement proxyobject>
<HTMLDivElement proxyobject>
LookupError: 2
at Frame, line 68

I.e., trying too get the the item at index 2 of the HTMLCollection (frame_hidden_elements[2]) produces this error, even though the length of frame_hidden_elements is 3.

When I try to manually get the .left-nav element outside of the loop, it works:

window.document.getElementsByClassName('left-nav')[0].classList.remove("frame-hidden")

So the problem seems to be with iterating the HTMLCollection.

From MDN

Warning: This is a live HTMLCollection. Changes in the DOM will reflect in the array as the changes occur. If an element selected by this array no longer qualifies for the selector, it will automatically be removed. Be aware of this for iteration purposes.

So you should create a copy of the list like object before iterating. Calling list(frame_elements) should work.

You have an array like object that’s live. You change the dom in the first iteration and this gives you 2 elements in the live list. So by the time the third iteration occurs there’s no 3rd element in your live list.

2 Likes

Ahah, my bad :sweat_smile:. Thanks Stu!

1 Like

In situations where the current list element can be deleted from the list, I try to iterate in reverse. Where that’s easy, there’s no need to make a copy.

Either way, it’s a special case to be remembered and programmed around.

3 Likes