@@ -82,15 +82,6 @@ def remove(self, *classes: str) -> "DOMElement":
8282 self ._el .classList .remove (cls )
8383 return self
8484
85- def toggle (self , * classes : str ) -> "DOMElement" :
86- if IS_BROWSER and self ._el :
87- for cls in classes :
88- if cls in self ._el .classList :
89- self ._el .classList .remove (cls )
90- else :
91- self ._el .classList .add (cls )
92- return self
93-
9485 def on (self , event : str , handler : Callable ) -> "DOMElement" :
9586 """Attaches a Python event listener."""
9687 if IS_BROWSER and self ._el :
@@ -113,6 +104,87 @@ def raw(self):
113104 """Returns the underlying JS element."""
114105 return self ._el
115106
107+ def attr (self , name : str , value : Any = None ) -> Union [str , "DOMElement" , None ]:
108+ """
109+ Get or Set an HTML attribute.
110+ - attr("src") -> returns value
111+ - attr("src", "img.jpg") -> sets value and returns self
112+ """
113+ if IS_BROWSER and self ._el :
114+ if value is None :
115+ return self ._el .getAttribute (name )
116+ self ._el .setAttribute (name , str (value ))
117+ return self
118+
119+ # Mock return for server-side safety
120+ return self if value is not None else None
121+
122+ def prop (self , name : str , value : Any = None ) -> Any :
123+ """
124+ Get or Set a JavaScript property (e.g. checked, disabled, valueAsDate).
125+ Distinct from attributes (html).
126+ """
127+ if IS_BROWSER and self ._el :
128+ if value is None :
129+ return getattr (self ._el , name , None )
130+ setattr (self ._el , name , value )
131+ return self
132+
133+ return self if value is not None else None
134+
135+ def toggle (self , cls : str , force : Optional [bool ] = None ) -> "DOMElement" :
136+ """
137+ Toggles a class.
138+ If 'force' is True, adds it. If False, removes it.
139+ If None, inverts current state.
140+ """
141+ if IS_BROWSER and self ._el :
142+ if force is True :
143+ self ._el .classList .add (cls )
144+ elif force is False :
145+ self ._el .classList .remove (cls )
146+ else :
147+ # Standard toggle behavior
148+ if self ._el .classList .contains (cls ):
149+ self ._el .classList .remove (cls )
150+ else :
151+ self ._el .classList .add (cls )
152+ return self
153+
154+ def serialize (self ) -> dict :
155+ """
156+ Scrapes all named input, select, and textarea elements within this element
157+ and returns a dictionary of their values.
158+ Handles checkboxes and radio buttons correctly.
159+ """
160+ data = {}
161+ if IS_BROWSER and self ._el :
162+ inputs = self ._el .querySelectorAll ("input, select, textarea" )
163+
164+ for i in range (inputs .length ):
165+ el = inputs .item (i )
166+ name = el .name
167+ if not name :
168+ continue
169+
170+ # Handle Checkbox
171+ if el .type == "checkbox" :
172+ if el .checked :
173+ data [name ] = True
174+ # Optional: Handle unchecked state if needed,
175+ # but usually omitted in serialization
176+
177+ # Handle Radio
178+ elif el .type == "radio" :
179+ if el .checked :
180+ data [name ] = el .value
181+
182+ # Handle standard inputs
183+ else :
184+ data [name ] = el .value
185+
186+ return data
187+
116188
117189class DOM :
118190 """
0 commit comments