ElementList Class
The ElementList class represents a collection of DOM elements in PyPositron. It's returned by methods like getElementsByClassName()
, getElementsByTagName()
, and querySelectorAll()
. ElementList provides a Python-like interface for working with multiple elements at once.
Overview
ElementList acts like a Python list but contains DOM elements. It allows you to:
- Access elements by index
- Iterate through all elements
- Get the count of elements
- Apply operations to multiple elements efficiently
Properties
length -> int
: The number of elements in the list. This property provides the count of elements contained in the ElementList.
Methods
__getitem__(index: int) -> Element
: Retrieves an element by index. Raises IndexError if index is out of range.__len__() -> int
: Returns the number of elements in the list. This allows you to use Python's built-inlen()
function.__iter__() -> Iterator[Element]
: Allows iteration over the elements in the list using Python's for-loop syntax.
Usage Examples
Basic Element Access
def main(ui):
# Get all buttons in the document
buttons = ui.document.getElementsByClassName("btn")
# Access by index
if len(buttons) > 0:
first_button = buttons[0]
first_button.innerText = "First Button"
# Check if elements exist
if buttons.length > 0:
print(f"Found {buttons.length} buttons")
# Get the last button
if len(buttons) > 0:
last_button = buttons[len(buttons) - 1]
last_button.style.backgroundColor = "red"
Iterating Through Elements
def main(ui):
# Get all paragraph elements
paragraphs = ui.document.getElementsByTagName("p")
# Iterate through all paragraphs
for i, paragraph in enumerate(paragraphs):
paragraph.innerText = f"This is paragraph {i + 1}"
paragraph.style.fontSize = "14px"
paragraph.style.margin = "10px 0"
# Alternative iteration with index
for i in range(len(paragraphs)):
paragraph = paragraphs[i]
if i % 2 == 0:
paragraph.style.backgroundColor = "#f0f0f0"
Bulk Operations
def main(ui):
# Get all input fields
inputs = ui.document.getElementsByTagName("input")
def clear_all_inputs():
for input_field in inputs:
input_field.value = ""
input_field.style.borderColor = "#ddd"
def validate_all_inputs():
all_valid = True
for input_field in inputs:
if input_field.value.strip() == "":
input_field.style.borderColor = "red"
all_valid = False
else:
input_field.style.borderColor = "green"
return all_valid
# Apply styles to all inputs
for input_field in inputs:
input_field.style.padding = "8px"
input_field.style.border = "1px solid #ddd"
input_field.style.borderRadius = "4px"
# Add event listeners to all inputs
for input_field in inputs:
def create_handler(field):
def on_change():
if field.value.strip():
field.style.borderColor = "green"
else:
field.style.borderColor = "red"
return on_change
input_field.addEventListener("change", create_handler(input_field))
# Clear button
clear_btn = ui.document.getElementById("clearBtn")
if clear_btn:
clear_btn.addEventListener("click", clear_all_inputs)
# Validate button
validate_btn = ui.document.getElementById("validateBtn")
if validate_btn:
validate_btn.addEventListener("click", lambda: print("Valid:", validate_all_inputs()))
Working with Forms
def main(ui):
# Get all form elements
forms = ui.document.getElementsByTagName("form")
for form in forms:
# Get all inputs within each form
form_inputs = form.getElementsByTagName("input")
form_selects = form.getElementsByTagName("select")
form_textareas = form.getElementsByTagName("textarea")
def create_form_handler(current_form):
def handle_submit():
# Collect all form data
data = {}
# Process inputs
inputs = current_form.getElementsByTagName("input")
for input_field in inputs:
if input_field.getAttribute("name"):
data[input_field.getAttribute("name")] = input_field.value
# Process selects
selects = current_form.getElementsByTagName("select")
for select in selects:
if select.getAttribute("name"):
data[select.getAttribute("name")] = select.value
# Process textareas
textareas = current_form.getElementsByTagName("textarea")
for textarea in textareas:
if textarea.getAttribute("name"):
data[textarea.getAttribute("name")] = textarea.value
print("Form submitted with data:", data)
return False # Prevent default submission
return handle_submit
form.addEventListener("submit", create_form_handler(form))
Dynamic List Management
def main(ui):
container = ui.document.getElementById("itemContainer")
def add_new_item(text):
# Create item element
item = ui.document.createElement("div")
item.className = "list-item"
item.innerText = text
item.style.padding = "10px"
item.style.margin = "5px 0"
item.style.backgroundColor = "#f9f9f9"
item.style.border = "1px solid #ddd"
item.style.borderRadius = "4px"
# Add remove button
remove_btn = ui.document.createElement("button")
remove_btn.innerText = "Remove"
remove_btn.style.marginLeft = "10px"
remove_btn.style.padding = "5px 10px"
remove_btn.style.backgroundColor = "#dc3545"
remove_btn.style.color = "white"
remove_btn.style.border = "none"
remove_btn.style.borderRadius = "3px"
remove_btn.style.cursor = "pointer"
def remove_item():
item.remove()
update_item_numbers()
remove_btn.addEventListener("click", remove_item)
item.appendChild(remove_btn)
container.appendChild(item)
update_item_numbers()
def update_item_numbers():
# Get all current items
items = container.getElementsByClassName("list-item")
for i, item in enumerate(items):
# Update the text to show item number
text_content = item.innerText.split(" - ")[0] if " - " in item.innerText else item.innerText
if text_content.endswith("Remove"):
text_content = text_content[:-6].strip() # Remove "Remove" button text
item.childNodes[0].nodeValue = f"Item {i + 1} - {text_content}"
def remove_all_items():
items = container.getElementsByClassName("list-item")
# Convert to regular list to avoid issues with live collection
items_to_remove = []
for item in items:
items_to_remove.append(item)
for item in items_to_remove:
item.remove()
# Add some initial items
for i in range(3):
add_new_item(f"Sample item {i + 1}")
# Add button to create new items
add_btn = ui.document.getElementById("addBtn")
if add_btn:
add_btn.addEventListener("click", lambda: add_new_item("New item"))
# Add button to remove all items
clear_btn = ui.document.getElementById("clearAllBtn")
if clear_btn:
clear_btn.addEventListener("click", remove_all_items)
Filtering and Searching
def main(ui):
# Get all items that can be filtered
all_items = ui.document.getElementsByClassName("filterable-item")
search_input = ui.document.getElementById("searchInput")
def filter_items(search_term):
search_term = search_term.lower()
visible_count = 0
for item in all_items:
item_text = item.innerText.lower()
if search_term in item_text:
item.style.display = "block"
visible_count += 1
else:
item.style.display = "none"
# Update results counter
results_counter = ui.document.getElementById("resultsCounter")
if results_counter:
results_counter.innerText = f"Showing {visible_count} of {len(all_items)} items"
def handle_search():
filter_items(search_input.value)
# Add search functionality
if search_input:
search_input.addEventListener("input", handle_search)
# Initial setup
for item in all_items:
item.style.transition = "opacity 0.3s ease"
# Category filtering
category_buttons = ui.document.getElementsByClassName("category-filter")
for button in category_buttons:
def create_category_handler(category_btn):
def handle_category_filter():
category = category_btn.getAttribute("data-category")
# Reset all button styles
for btn in category_buttons:
btn.style.backgroundColor = "#f8f9fa"
btn.style.color = "#495057"
# Highlight active button
category_btn.style.backgroundColor = "#007bff"
category_btn.style.color = "white"
# Filter items
if category == "all":
for item in all_items:
item.style.display = "block"
else:
for item in all_items:
item_category = item.getAttribute("data-category")
if item_category == category:
item.style.display = "block"
else:
item.style.display = "none"
return handle_category_filter
button.addEventListener("click", create_category_handler(button))
Batch Styling
def main(ui):
def apply_theme_to_elements(elements, theme):
if theme == "dark":
for element in elements:
element.style.backgroundColor = "#333"
element.style.color = "#fff"
element.style.borderColor = "#555"
elif theme == "light":
for element in elements:
element.style.backgroundColor = "#fff"
element.style.color = "#333"
element.style.borderColor = "#ddd"
elif theme == "accent":
for element in elements:
element.style.backgroundColor = "#007bff"
element.style.color = "#fff"
element.style.borderColor = "#0056b3"
# Apply themes to different element groups
buttons = ui.document.getElementsByClassName("btn")
cards = ui.document.getElementsByClassName("card")
inputs = ui.document.getElementsByTagName("input")
# Theme switcher
theme_selector = ui.document.getElementById("themeSelector")
def change_theme():
selected_theme = theme_selector.value
apply_theme_to_elements(buttons, selected_theme)
apply_theme_to_elements(cards, selected_theme)
# Special handling for inputs
if selected_theme == "dark":
for input_field in inputs:
input_field.style.backgroundColor = "#444"
input_field.style.color = "#fff"
input_field.style.borderColor = "#666"
else:
for input_field in inputs:
input_field.style.backgroundColor = "#fff"
input_field.style.color = "#333"
input_field.style.borderColor = "#ddd"
if theme_selector:
theme_selector.addEventListener("change", change_theme)
Working with Live Collections
ElementList represents a "live" collection, meaning it automatically updates when the DOM changes:
def main(ui):
# Get all elements with class "dynamic"
dynamic_elements = ui.document.getElementsByClassName("dynamic")
print(f"Initial count: {len(dynamic_elements)}") # e.g., 3
# Add a new element with the same class
new_element = ui.document.createElement("div")
new_element.className = "dynamic"
ui.document.body.appendChild(new_element)
print(f"After adding: {len(dynamic_elements)}") # Now 4!
# The collection automatically includes the new element
for element in dynamic_elements:
element.style.border = "1px solid red" # All 4 elements get the border
Error Handling
def main(ui):
elements = ui.document.getElementsByClassName("my-class")
# Always check if elements exist
if len(elements) > 0:
# Safe to access first element
first_element = elements[0]
first_element.innerText = "Found!"
else:
print("No elements found with class 'my-class'")
# Safe iteration (handles empty collections)
for element in elements:
element.style.color = "blue"
# Index bounds checking
try:
fifth_element = elements[4] # May throw IndexError
fifth_element.innerText = "Fifth element"
except IndexError:
print("No fifth element found")
Best Practices
-
Check collection length: Always verify that elements exist before accessing them by index.
-
Use iteration for bulk operations: When applying the same operation to multiple elements, use for-loops rather than accessing by index.
-
Be aware of live collections: Remember that ElementList updates automatically when the DOM changes.
-
Cache element references: If you're accessing the same element multiple times, store it in a variable.
-
Handle empty collections gracefully: Always account for the possibility that no elements match your query.
-
Use appropriate selection methods: Choose the most specific selection method for better performance:
getElementById()
for single elements by IDgetElementsByClassName()
for elements by classquerySelectorAll()
for complex CSS selectors
Common Patterns
Processing Form Data
def collect_form_data(form):
data = {}
inputs = form.getElementsByTagName("input")
for input_field in inputs:
name = input_field.getAttribute("name")
if name:
data[name] = input_field.value
return data
Batch Event Handling
def add_click_handlers(elements, handler):
for element in elements:
element.addEventListener("click", handler)
Conditional Styling
def highlight_errors(elements, validation_func):
for element in elements:
if not validation_func(element.value):
element.style.borderColor = "red"
else:
element.style.borderColor = "green"