A Python library for creating and manipulating HTML documents using an elegant DOM API
—
Core DOM tree manipulation with element creation, attribute management, content addition/removal, searching, and rendering with customizable formatting. Includes context manager support and decorator functionality.
Foundation class providing core DOM manipulation functionality for all elements.
class dom_tag:
def __init__(self, *args, **kwargs):
"""
Create a new DOM element.
Parameters:
- *args: Child elements, text content, or iterables
- **kwargs: Element attributes and special rendering options
Special kwargs:
- __inline (bool): Render children inline
- __pretty (bool): Enable pretty printing
"""
# Class attributes
is_single: bool = False # Self-closing tag (e.g., <br/>, <img/>)
is_pretty: bool = True # Pretty print content by default
is_inline: bool = False # Inline rendering
# Instance attributes
attributes: dict # Element attributes
children: list # Child elements and text
parent: dom_tag | None # Parent elementAdd, remove, and manipulate element content and children.
def add(self, *args):
"""
Add child elements or content to this element.
Parameters:
- *args: Elements, text, numbers, dicts (attributes), or iterables
Returns:
- Single element if one arg, tuple of elements if multiple args
"""
def remove(self, obj):
"""
Remove a child element.
Parameters:
- obj: Child element to remove
"""
def clear(self):
"""
Remove all child elements and reset parent references.
"""
def add_raw_string(self, s):
"""
Add raw string content without escaping.
Parameters:
- s (str): Raw string to add
"""from dominate.tags import div, p, a, ul, li
from dominate.util import text
# Basic content addition
container = div()
container.add(p('First paragraph'))
container.add(p('Second paragraph'))
# Multiple elements at once
heading, content = container.add(h1('Title'), div(cls='content'))
# Different content types
element = div()
element.add('Plain text') # String
element.add(42) # Number (converted to string)
element.add(p('Paragraph')) # Element
element.add({'class': 'my-class'}) # Attributes dict
element.add([li('Item 1'), li('Item 2')]) # Iterable
# Using += operator
nav = ul()
nav += li(a('Home', href='/'))
nav += li(a('About', href='/about'))
# Clear content
container.clear() # Removes all children
# Raw string (no escaping)
div_element = div()
div_element.add_raw_string('<em>This will not be escaped</em>')Set, get, and delete element attributes with dictionary-like interface.
def set_attribute(self, key, value):
"""
Set an element attribute.
Parameters:
- key (str|int): Attribute name or child index
- value: Attribute value or child element
"""
def delete_attribute(self, key):
"""
Delete an attribute or child element.
Parameters:
- key (str|int): Attribute name or child index
"""
def __getitem__(self, key):
"""
Get attribute value or child element.
Parameters:
- key (str|int): Attribute name or child index
Returns:
- Attribute value or child element
"""
def __setitem__(self, key, value):
"""Dictionary-style attribute/child setting."""
def __delitem__(self, key):
"""Dictionary-style attribute/child deletion."""# Attribute management
element = div()
# Set attributes
element.set_attribute('class', 'container')
element['id'] = 'main-div'
element.data_value = '123' # Using attribute access
# Get attributes
class_name = element['class']
element_id = element.id
# Delete attributes
del element['class']
element.delete_attribute('id')
# Child element access
list_element = ul(li('Item 1'), li('Item 2'))
first_item = list_element[0] # Get first child
list_element[1] = li('New Item 2') # Replace second child
del list_element[0] # Remove first childFind elements within the DOM tree using various criteria.
def get(self, tag=None, **kwargs):
"""
Recursively search for child elements.
Parameters:
- tag (str|class|None): Tag type to search for
- **kwargs: Attribute filters
Returns:
- list: Matching elements
"""
@property
def parent(self):
"""Get the parent element."""
def __contains__(self, item):
"""
Check if element contains a specific tag type.
Parameters:
- item (str|class): Tag type to check for
Returns:
- bool: True if found in children tree
"""# Search examples
document = div(
div(p('Content 1'), id='section1', cls='content'),
div(p('Content 2'), id='section2', cls='content'),
p('Footer text', cls='footer')
)
# Find by tag type
paragraphs = document.get('p') # All <p> elements
divs = document.get(div) # All div elements
# Find by attributes
content_divs = document.get(cls='content') # class="content"
section1 = document.get(id='section1') # id="section1"
footer_p = document.get('p', cls='footer') # <p> with class="footer"
# Check containment
has_paragraphs = 'p' in document # True
has_tables = 'table' in document # False
# Navigation
first_div = document[0] # First child
parent_element = first_div.parent # Back to documentUse elements as context managers with Python's with statement for clean, hierarchical markup creation.
def __enter__(self):
"""
Enter context manager mode.
Returns:
- self: The element instance
"""
def __exit__(self, type, value, traceback):
"""
Exit context manager, adding any unassigned elements to this element.
Parameters:
- type, value, traceback: Exception information (if any)
"""# Context manager usage
container = div(cls='main-container')
with container:
h1('Page Title')
p('This paragraph will be added to the container.')
with ul(cls='nav-menu'):
li(a('Home', href='/'))
li(a('About', href='/about'))
li(a('Contact', href='/contact'))
# Nested contexts
page = div()
with page:
with header():
h1('Site Title')
nav(
a('Link 1', href='/1'),
a('Link 2', href='/2')
)
with main():
article(
h2('Article Title'),
p('Article content here.')
)
footer('© 2023 Site Name')Use elements as decorators for functions to create reusable widgets and components.
def __new__(cls, *args, **kwargs):
"""
Handle decorator usage when element class is used with single callable argument.
Returns:
- Decorated function or new element instance
"""
def __call__(self, func):
"""
Use element instance as decorator.
Parameters:
- func: Function to decorate
Returns:
- Decorated function that returns element with function result
"""# Class decorator (creates new instance each call)
@div
def create_card(title, content):
h3(title)
p(content)
card1 = create_card('Card 1', 'Content for card 1')
card2 = create_card('Card 2', 'Content for card 2')
# Instance decorator (copies instance each call)
@div(cls='alert alert-info')
def info_message(text):
p(text)
message1 = info_message('This is an info message')
message2 = info_message('Another info message')
# More complex widget
@div(cls='product-card')
def product_card(name, price, description):
with div(cls='card-header'):
h4(name)
span(f'${price}', cls='price')
with div(cls='card-body'):
p(description)
button('Add to Cart', cls='btn btn-primary')
product = product_card('Widget', 19.99, 'A useful widget for your needs.')Control how elements are rendered to HTML with various formatting options.
def render(self, indent=' ', pretty=True, xhtml=False):
"""
Render element to HTML string.
Parameters:
- indent (str): Indentation string for pretty printing
- pretty (bool): Enable pretty printing with indentation
- xhtml (bool): Use XHTML-style self-closing tags
Returns:
- str: Rendered HTML
"""
def _render(self, sb, indent_level, indent_str, pretty, xhtml):
"""
Internal rendering method.
Parameters:
- sb (list): String buffer for output
- indent_level (int): Current indentation level
- indent_str (str): Indentation string
- pretty (bool): Pretty printing enabled
- xhtml (bool): XHTML mode
Returns:
- list: Updated string buffer
"""
def __str__(self):
"""String representation using render()."""
def __unicode__(self):
"""Unicode representation using render()."""# Rendering options
element = div(
h1('Title'),
p('Content here')
)
# Default pretty printing
html = element.render()
# <div>
# <h1>Title</h1>
# <p>Content here</p>
# </div>
# Compact rendering
compact = element.render(pretty=False)
# <div><h1>Title</h1><p>Content here</p></div>
# Custom indentation
tabbed = element.render(indent='\t')
# <div>
# <h1>Title</h1>
# <p>Content here</p>
# </div>
# XHTML mode
xhtml = element.render(xhtml=True)
# Self-closing tags get /> instead of >
# String conversion
html_string = str(element) # Same as element.render()Elements support standard Python collection operations.
def __len__(self):
"""
Get number of child elements.
Returns:
- int: Number of children
"""
def __iter__(self):
"""
Iterate over child elements.
Returns:
- iterator: Child element iterator
"""
def __bool__(self):
"""
Boolean evaluation (always True for elements).
Returns:
- bool: Always True
"""# Collection operations
menu = ul(
li('Item 1'),
li('Item 2'),
li('Item 3')
)
# Length
num_items = len(menu) # 3
# Iteration
for item in menu:
print(item) # Prints each <li> element
# Boolean evaluation
if menu: # Always True for elements
print('Menu exists')
# List-like operations
first_item = menu[0]
last_item = menu[-1]
menu.append(li('Item 4')) # Error: use add() insteadAutomatic conversion of Python attribute names to HTML-compatible names.
@staticmethod
def clean_attribute(attribute):
"""
Convert Python attribute names to HTML attribute names.
Parameters:
- attribute (str): Python attribute name
Returns:
- str: HTML-compatible attribute name
"""
@classmethod
def clean_pair(cls, attribute, value):
"""
Clean attribute name and handle boolean values.
Parameters:
- attribute (str): Attribute name
- value: Attribute value
Returns:
- tuple: (cleaned_attribute, processed_value)
"""# Attribute name conversions
element = div(
cls='my-class', # 'cls' -> 'class'
class_name='other', # 'class_name' -> 'class'
fr='input-id', # 'fr' -> 'for'
html_for='input-id', # 'html_for' -> 'for'
data_value='123', # 'data_value' -> 'data-value'
aria_label='Description', # 'aria_label' -> 'aria-label'
_private='hidden', # '_private' -> 'private'
http_equiv='refresh' # 'http_equiv' -> 'http-equiv'
)
# Boolean attribute handling
checkbox = input_(
type='checkbox',
checked=True, # becomes checked="checked"
disabled=False # omitted from output
)W3C DOM Level 1 Core specification compliance for standard DOM operations.
@property
def parentNode(self):
"""
DOM API: Get the parent element.
Returns:
- dom_tag|None: Parent element or None if root
"""
def getElementById(self, id):
"""
DOM API: Get element with specified ID.
Parameters:
- id (str): Element ID to search for
Returns:
- dom_tag|None: Element with matching ID or None
Raises:
- ValueError: If multiple elements have the same ID
"""
def getElementsByTagName(self, name):
"""
DOM API: Get all elements with specified tag name.
Parameters:
- name (str): Tag name to search for
Returns:
- list: All matching elements
"""
def appendChild(self, obj):
"""
DOM API: Add child element to the end of children list.
Parameters:
- obj: Element to add as child
Returns:
- self: The parent element (for chaining)
"""# DOM Level 1 Core API usage
document = div(
div(p('Section 1'), id='section1'),
div(p('Section 2'), id='section2'),
p('Footer', id='footer')
)
# Get element by ID
section1 = document.getElementById('section1')
print(section1) # <div id="section1">...</div>
# Handle multiple/missing IDs
try:
missing = document.getElementById('nonexistent') # Returns None
print(missing)
except ValueError as e:
print(f'Error: {e}') # If multiple elements have same ID
# Get elements by tag name
all_paragraphs = document.getElementsByTagName('p')
print(len(all_paragraphs)) # 2
all_divs = document.getElementsByTagName('div')
print(len(all_divs)) # 2
# Append child (DOM-style)
new_section = div(p('New section'), id='section3')
document.appendChild(new_section)
# Parent navigation
child = document.getElementById('section1')
parent = child.parentNode # Returns the document divGlobal functions for working with context managers and current element state.
def get_current(default=None):
"""
Get the current element in context manager scope.
Parameters:
- default: Value to return if no current context
Returns:
- dom_tag|default: Current context element or default
Raises:
- ValueError: If no current context and no default provided
"""
def attr(*args, **kwargs):
"""
Set attributes on the current context element.
Parameters:
- *args: Dictionaries of attributes
- **kwargs: Attribute key-value pairs
"""# Context functions
with div() as container:
p('Content here')
# Get current context
current = get_current() # Returns the div
# Set attributes on current context
attr(id='main-container', cls='wrapper')
attr({'data-value': '123'})
# Outside context
try:
current = get_current()
except ValueError:
print('No current context')
# With default
current = get_current(default=None) # Returns None if no contextInstall with Tessl CLI
npx tessl i tessl/pypi-dominate