docs
Loading...
Searching...
No Matches
Element.h
Go to the documentation of this file.
1///
2/// Copyright (C) 2026 Ultralight, Inc. All rights reserved.
3/// A license is required for commercial use. https://ultralig.ht
4///
5#pragma once
6// X11's headers define None as a macro, which would break the None enumerators.
7#pragma push_macro("None")
8#undef None
9
10#include <Ultralight/CAPI/CAPI_DOMElement.h>
11#include <Ultralight/CAPI/CAPI_DOMEvent.h>
18#include <Ultralight/dom/Node.h>
20#include <Ultralight/dom/detail/Properties.h>
21#include <Ultralight/dom/detail/Receivers.h>
22
23#include <cstddef>
24#include <cstdint>
25#include <optional>
26#include <string>
27#include <string_view>
28#include <type_traits>
29#include <utility>
30#include <vector>
31
32namespace ultralight {
33namespace dom {
34
35class Document;
37class Element;
38class ElementList;
43class HTMLFormElement;
47
48/// \cond INTERNAL
49namespace detail {
50
51// The dom::Checked result of a delegated registration (Element::On(), Document::On()). The C
52// call refuses a malformed selector, the only refusal left once the receiver is alive and the
53// signal isn't aborted.
54inline Result<EventListener> CheckedDelegation(EventListener listener, bool receiver_alive,
55 const AddEventListenerOptions& options,
56 std::string_view selector) {
57 if (listener.raw() || options.signal.aborted())
58 return listener;
59 if (!receiver_alive)
60 return Unexpected<Error>(Error::PageGone());
61 std::string message = "'" + std::string(selector) + "' is not a valid selector.";
62 ErrorScope error;
63 error.out()->code = kULDOMErrorCode_SyntaxError;
64 error.out()->message = ulCreateStringUTF8(message.data(), message.size());
65 return Unexpected<Error>(error.TakeError());
66}
67
68} // namespace detail
69/// \endcond
70
71///
72/// A handle to an element on a page.
73///
74/// dom::Element is a safe handle to an element on a page, providing direct access to document
75/// content and events from C++. Its syntax matches JavaScript's DOM, and it doesn't require
76/// JavaScript to be enabled.
77///
78/// You can store handles in native objects to update your interface when application state changes,
79/// or attach callbacks to respond to user interactions.
80///
81/// This example updates an element and listens for a click:
82///
83/// ```
84/// dom::Element hud = document.querySelector("#hud");
85///
86/// hud.textContent = "Ready"; // properties you can set are members
87/// hud.classList.add("visible");
88/// hud.style.width = dom::StyleValue::Pct(42);
89/// dom::Element wrapper = hud.parentElement(); // read-only ones are methods
90///
91/// hud.addEventListener("click", [] { Log("clicked"); });
92/// ```
93///
94/// ## Handle Lifetime
95///
96/// Copying a handle creates another reference to the same element rather than duplicating the
97/// element itself.
98///
99/// A handle keeps its element alive in memory for as long as its page lives, even after calling
100/// remove() to take it out of the document tree.
101///
102/// ## Handle States
103///
104/// A handle's status depends on whether it holds an element and whether its page remains active:
105///
106/// - **Valid handles point to an element on an active page.** Operations on them run normally.
107/// - **Empty handles hold no element.** Default-constructed handles, moved-from handles, and
108/// queries that match nothing are empty, and calls on them do nothing.
109/// - **Gone handles hold elements whose page was destroyed, navigated away, or had its frame
110/// removed.** Calls on them do nothing.
111///
112/// Every call is safe in any state, so you can chain operations without null checks:
113///
114/// ```
115/// document.querySelector("#save").click(); // does nothing if there's no match
116/// ```
117///
118/// A handle whose page is gone never becomes valid again, even if that page comes back from the
119/// back-forward cache. Every DOM handle follows these lifetime and safety rules.
120///
121/// ### Inspecting Failures
122///
123/// To find out why an operation failed, pass dom::Checked as an extra argument to return a
124/// dom::Result holding a dom::Error. A misspelled selector yields an empty handle, so subsequent
125/// calls do nothing silently unless Config::diagnostics.dom is set to DiagnosticsLevel::Strict.
126///
127/// ## Typed Element Views
128///
129/// You can narrow a generic element to a specific control type using helper methods like AsInput(),
130/// AsTextArea(), and AsSelect(). An `As*()` view returns an empty handle if the element has a
131/// different tag.
132///
133/// Converting an element to an input gives access to tag-specific properties:
134///
135/// ```
136/// dom::HTMLInputElement name_input = document.getElementById("name").AsInput();
137/// name_input.placeholder = "Your name";
138/// ```
139///
140/// @see dom::Error, dom::Checked, dom::Node, dom::EventListener, dom::getComputedStyle()
141///
142class Element : public Node {
143 public:
144 ///
145 /// The element's inline style (style).
146 ///
147 /// Set a property through its member, by name, or all at once with cssText:
148 ///
149 /// ```
150 /// el.style.width = "50%"; // a string
151 /// el.style.opacity = 0.5; // a number
152 /// el.style.left = dom::StyleValue::Px(x); // a number with a unit
153 /// el.style["fontSize"] = "12px"; // by name ("font-size" works too)
154 /// el.style.setProperty("--accent", "teal"); // by CSS name (custom properties too)
155 /// el.style.cssText = "color: red; margin: 0";
156 /// ```
157 ///
158 /// A value that doesn't parse is ignored and the old value stays, like the web. That includes
159 /// a number the property doesn't take (eg, StyleValue::Px() for `opacity`), which also logs a
160 /// warning. A mistyped numeric literal (eg, `"50pxx"`) is a compile error instead. An empty
161 /// string, an Empty dom::StyleValue (or `{}`), or an unset Color removes the property, and an
162 /// Invalid StyleValue or an invalid Color is ignored.
163 ///
164 /// Reading a member (or a property by name) gives you its value as a string. AsStyleValue() and
165 /// AsColor() convert it. A property that isn't set gives an Empty StyleValue or an unset Color,
166 /// and a value that doesn't convert gives an Invalid StyleValue or an invalid Color:
167 ///
168 /// ```
169 /// std::string width = el.style.width; // eg, "50%"
170 /// dom::StyleValue left = el.style.left.AsStyleValue(); // eg, 12 (px)
171 /// Color accent = el.style["--accent"].AsColor(); // any CSS color text
172 /// ```
173 ///
174 /// @note Reads return what's set inline on this element, not the value that applies to it. Use
175 /// dom::getComputedStyle() for that.
176 ///
177 /// @see <Ultralight/dom/StyleValue.h>
178 ///
179 detail::StyleProxy style;
180
181 ///
182 /// The element's classes (classList).
183 ///
184 /// Use contains(), add(), remove(), toggle(), replace(), length(), and item(). add() and remove()
185 /// take several classes at once:
186 ///
187 /// ```
188 /// el.classList.add("card", "selected");
189 /// bool open = el.classList.toggle("open");
190 /// ```
191 ///
192 /// @note An empty class or one containing whitespace makes the whole call do nothing (the web
193 /// throws instead).
194 ///
195 detail::ClassListProxy classList;
196
197 ///
198 /// The element's `data-*` attributes by camelCase name (dataset).
199 ///
200 /// ```
201 /// el.dataset["userId"] = "42"; // sets data-user-id="42"
202 /// std::string id = el.dataset["userId"]; // "" if the attribute is missing
203 /// el.dataset["userId"].Remove(); // removes the attribute
204 /// ```
205 ///
206 /// @note A name with a `-` followed by a lowercase letter (eg, `"user-id"`) can't be converted.
207 /// Writes through it do nothing (the web throws) and reads return an empty string.
208 ///
209 detail::DatasetProxy dataset;
210
211 ///
212 /// The text of this element and all its descendants (textContent).
213 ///
214 /// Assign to replace the element's children with the text (an empty string removes them).
215 ///
216 detail::StringProp<detail::TextContentTag> textContent;
217
218 ///
219 /// The element's text as it's rendered (innerText).
220 ///
221 /// Assign to replace the element's children with the text. Each line break becomes a `<br>`
222 /// element.
223 ///
224 /// \parblock
225 /// @note Reading this updates the layout first if the page has pending changes. Use textContent
226 /// when you don't need the rendered form.
227 /// \endparblock
228 ///
229 /// \parblock
230 /// @note Assigning does nothing on an element that isn't an HTML element (eg, SVG). Assign
231 /// textContent there.
232 /// \endparblock
233 ///
234 detail::StringProp<detail::InnerTextTag> innerText;
235
236 ///
237 /// The markup of the element's children (innerHTML). Assign to replace the children with the
238 /// parsed markup.
239 ///
240 /// \parblock
241 /// @note Scripts in assigned markup never run. Event handler attributes (eg, `onclick`) are
242 /// kept and work when JavaScript is enabled.
243 /// \endparblock
244 ///
245 /// \parblock
246 /// @note In an XML document (eg, XHTML), assigning markup that isn't well-formed does nothing
247 /// (the web throws a SyntaxError).
248 /// \endparblock
249 ///
250 detail::StringProp<detail::InnerHTMLTag> innerHTML;
251
252 ///
253 /// The markup of the element and its children (outerHTML).
254 ///
255 /// Assign to replace the element with the parsed markup (scripts in it never run). This handle
256 /// then refers to the removed element.
257 ///
258 /// @note Assigning does nothing if the element's parent isn't an element (eg, it's detached or
259 /// it's the `<html>` root). The web throws a NoModificationAllowedError there. Use
260 /// ulDOMElementSetOuterHTML() if you need the error.
261 ///
262 detail::StringProp<detail::OuterHTMLTag> outerHTML;
263
264 ///
265 /// The element's `id` attribute (id). Reads as an empty string if there's none.
266 ///
267 detail::StringProp<detail::IdTag> id;
268
269 ///
270 /// The element's `class` attribute as one string (className). Use classList to work with
271 /// single classes.
272 ///
273 detail::StringProp<detail::ClassNameTag> className;
274
275 ///
276 /// The element's `title` attribute (title). Reads as an empty string if there's none.
277 ///
278 detail::StringProp<detail::TitleTag> title;
279
280 ///
281 /// Whether or not the element has a `hidden` attribute (hidden). Assign to add or remove it.
282 ///
283 /// A hidden element isn't displayed, unless a CSS rule gives it a `display` value.
284 ///
285 detail::BoolProp<detail::HiddenTag> hidden;
286
287 ///
288 /// The element's position in the keyboard focus order (tabIndex).
289 ///
290 /// This reads the `tabindex` attribute. Without it, elements that take focus by default (eg,
291 /// links and form controls) read 0 and the rest read -1. Assign to set the attribute (-1 makes
292 /// the element focusable with focus() but skipped by the Tab key).
293 ///
294 detail::IntProp<detail::TabIndexTag> tabIndex;
295
296 ///
297 /// The current value of a form control (value).
298 ///
299 /// This works on `<input>`, `<textarea>`, `<select>`, and `<option>`. Other elements read an
300 /// empty string and ignore writes.
301 ///
302 /// @note Assigning fires no events, like the web. Use SetValue() on HTMLInputElement,
303 /// HTMLTextAreaElement, or HTMLSelectElement to fire `input` and `change` like a user
304 /// edit.
305 ///
306 detail::StringProp<detail::ValueTag> value;
307
308 ///
309 /// The element's `name` attribute (name). Reads as an empty string if there's none.
310 ///
311 /// Form controls use it as the field name when their form is submitted.
312 ///
313 detail::StringProp<detail::NameTag> name;
314
315 ///
316 /// Whether or not a checkbox or radio button is checked (checked). Other elements read false.
317 ///
318 /// @note Assigning fires no events, like the web. click() toggles it like a user would, firing
319 /// `input` and `change`.
320 ///
321 detail::BoolProp<detail::CheckedTag> checked;
322
323 ///
324 /// Whether or not the element has a `disabled` attribute (disabled). Assign to add or remove it.
325 ///
326 /// @note A control inside a disabled `<fieldset>` is disabled without the attribute. Use
327 /// `matches(":disabled")` to check for that.
328 ///
329 detail::BoolProp<detail::DisabledTag> disabled;
330
331 ///
332 /// The index of the selected option in a `<select>` (selectedIndex), as a
333 /// `std::optional<size_t>`.
334 ///
335 /// It reads std::nullopt when no option is selected (the web reads -1), and other elements read
336 /// std::nullopt and ignore writes. Assigning std::nullopt or an index that's out of range clears
337 /// the selection. Assigning fires no `change` event.
338 ///
339 /// ```
340 /// std::optional<size_t> index = select.selectedIndex;
341 /// select.selectedIndex = 2;
342 /// ```
343 ///
344 detail::ValueProp<detail::SelectedIndexTag> selectedIndex;
345
346 ///
347 /// How far the element's content is scrolled down, in CSS pixels (scrollTop).
348 ///
349 /// Assign to scroll. The position is clamped to the scrollable range, and scrolling is instant
350 /// (CSS `scroll-behavior` is ignored). For the root `<html>` element (`<body>` in a quirks-mode
351 /// page), this scrolls the page.
352 ///
353 /// @note The library currently scrolls elements in whole pixels, so a fraction is dropped.
354 /// Reads and writes update the layout first if the page has pending changes.
355 ///
356 detail::ValueProp<detail::ScrollTopTag> scrollTop;
357
358 ///
359 /// How far the element's content is scrolled right, in CSS pixels (scrollLeft).
360 ///
361 /// Works like scrollTop.
362 ///
363 detail::ValueProp<detail::ScrollLeftTag> scrollLeft;
364
365 ///
366 /// Create an empty Element.
367 ///
369
370 ///
371 /// Copy constructor (both handles refer to the same element).
372 ///
373 /// @param other The Element to copy.
374 ///
375 Element(const Element& other) : Node(other) {}
376
377 ///
378 /// Move constructor (`other` becomes empty).
379 ///
380 /// @param other The Element to move from.
381 ///
382 Element(Element&& other) noexcept : Node(std::move(other)) {}
383
384 ///
385 /// Assignment (copies or moves).
386 ///
387 /// @param other The Element to assign from.
388 ///
389 /// @return Returns this Element.
390 ///
391 Element& operator=(Element other) noexcept {
392 std::swap(detail_.handle, other.detail_.handle);
393 return *this;
394 }
395
396 // Validity (operator bool, IsEmpty, IsAlive), IsSame(), destruction, and the node-level
397 // surface (nodeType(), nodeValue, firstChild(), before(), remove(), ...) are inherited from
398 // Node: an Element IS a Node, and the handle families share one object underneath.
399
400 // --- Lookup -----------------------------------------------------------------------------
401
402 ///
403 /// Find the first element below this one that matches a CSS selector (querySelector).
404 ///
405 /// @param selectors One or more CSS selectors (eg, `.item > a[href]`).
406 ///
407 /// @return Returns the first match in document order (empty if nothing matches, the selector
408 /// is malformed, or the page is gone).
409 ///
410 Element querySelector(std::string_view selectors) const {
411 detail::CString s(selectors);
412 return Element(ulDOMElementQuerySelector(raw(), s.c_str(), nullptr));
413 }
414
415 ///
416 /// Same as querySelector(), but returns a Result with the reason for a failure (eg, a
417 /// SyntaxError for a malformed selector).
418 ///
419 /// @return Returns the first match in document order (empty if nothing matches). Fails with a
420 /// SyntaxError if the selector is malformed.
421 ///
422 [[nodiscard]] Result<Element> querySelector(std::string_view selectors, Checked_t) const {
423 if (IsEmpty())
424 return detail::EmptyHandleError();
425 detail::CString s(selectors);
426 detail::ErrorScope error;
427 ULDOMElement result = ulDOMElementQuerySelector(raw(), s.c_str(), error.out());
428 if (result)
429 return Element(result);
430 if (error.has_exception())
431 return Unexpected<Error>(error.TakeError());
432 if (IsAlive())
433 return Element(); // No match on a live element: success with an empty handle.
434 return Unexpected<Error>(Error::PageGone());
435 }
436
437 ///
438 /// Find every element below this one that matches a CSS selector (querySelectorAll).
439 ///
440 /// This is defined in `<Ultralight/dom/ElementList.h>`. Include that header (or
441 /// `<Ultralight/DOM.h>`) to call it.
442 ///
443 /// @param selectors One or more CSS selectors (eg, `.item > a[href]`).
444 ///
445 /// @return Returns the matches in document order (an empty list if nothing matches, the
446 /// selector is malformed, or the page is gone).
447 ///
448 /// @note The list doesn't change when the document does. Query again to see later changes.
449 ///
450 ElementList querySelectorAll(std::string_view selectors) const;
451
452 ///
453 /// Same as querySelectorAll(), but returns a Result with the reason for a failure (eg, a
454 /// SyntaxError for a malformed selector).
455 ///
456 /// @return Returns the matches in document order (an empty list if nothing matches). Fails with
457 /// a SyntaxError if the selector is malformed.
458 ///
459 [[nodiscard]] Result<ElementList> querySelectorAll(std::string_view selectors, Checked_t) const;
460
461 ///
462 /// Whether or not this element matches a CSS selector (matches).
463 ///
464 /// @param selectors One or more CSS selectors (eg, `.item > a[href]`).
465 ///
466 /// @return Returns whether the element matches (false if the selector is malformed or the page
467 /// is gone).
468 ///
469 bool matches(std::string_view selectors) const {
470 detail::CString s(selectors);
471 return ulDOMElementMatches(raw(), s.c_str(), nullptr);
472 }
473
474 ///
475 /// Same as matches(), but returns a Result with the reason for a failure (eg, a SyntaxError for
476 /// a malformed selector).
477 ///
478 /// @return Returns whether the element matches. Fails with a SyntaxError if the selector is
479 /// malformed.
480 ///
481 [[nodiscard]] Result<bool> matches(std::string_view selectors, Checked_t) const {
482 if (IsEmpty())
483 return detail::EmptyHandleError();
484 detail::CString s(selectors);
485 detail::ErrorScope error;
486 bool result = ulDOMElementMatches(raw(), s.c_str(), error.out());
487 if (error.has_exception())
488 return Unexpected<Error>(error.TakeError());
489 if (!result && !IsAlive())
490 return Unexpected<Error>(Error::PageGone());
491 return result;
492 }
493
494 ///
495 /// Find the closest ancestor that matches a CSS selector, starting with this element itself
496 /// (closest).
497 ///
498 /// @param selectors One or more CSS selectors (eg, `.item > a[href]`).
499 ///
500 /// @return Returns the closest match (empty if nothing matches, the selector is malformed, or
501 /// the page is gone).
502 ///
503 Element closest(std::string_view selectors) const {
504 detail::CString s(selectors);
505 return Element(ulDOMElementClosest(raw(), s.c_str(), nullptr));
506 }
507
508 ///
509 /// Same as closest(), but returns a Result with the reason for a failure (eg, a SyntaxError for
510 /// a malformed selector).
511 ///
512 /// @return Returns the closest match (empty if nothing matches). Fails with a SyntaxError if
513 /// the selector is malformed.
514 ///
515 [[nodiscard]] Result<Element> closest(std::string_view selectors, Checked_t) const {
516 if (IsEmpty())
517 return detail::EmptyHandleError();
518 detail::CString s(selectors);
519 detail::ErrorScope error;
520 ULDOMElement result = ulDOMElementClosest(raw(), s.c_str(), error.out());
521 if (result)
522 return Element(result);
523 if (error.has_exception())
524 return Unexpected<Error>(error.TakeError());
525 if (IsAlive())
526 return Element();
527 return Unexpected<Error>(Error::PageGone());
528 }
529
530 // --- Attributes -------------------------------------------------------------------------
531
532 ///
533 /// Get an attribute's value (getAttribute).
534 ///
535 /// @param name The attribute name (eg, `href`). HTML elements ignore its case.
536 ///
537 /// @return Returns the value (nullopt if the element doesn't have the attribute). An attribute
538 /// with no value (eg, `<input disabled>`) returns an empty string.
539 ///
540 std::optional<std::string> getAttribute(std::string_view name) const {
541 detail::CString n(name);
542 ULString s = ulDOMElementGetAttribute(raw(), n.c_str());
543 if (!s)
544 return std::nullopt;
545 return detail::TakeString(s);
546 }
547
548 ///
549 /// Whether or not the element has an attribute (hasAttribute).
550 ///
551 /// @param name The attribute name (eg, `href`). HTML elements ignore its case.
552 ///
553 bool hasAttribute(std::string_view name) const {
554 detail::CString n(name);
555 return ulDOMElementHasAttribute(raw(), n.c_str());
556 }
557
558 ///
559 /// Set an attribute's value (setAttribute).
560 ///
561 /// @param name The attribute name (eg, `href`). HTML elements store it in lowercase.
562 ///
563 /// @param new_value The value to set.
564 ///
565 /// @note An invalid name (eg, one containing a space) makes this do nothing. The web throws an
566 /// InvalidCharacterError instead.
567 ///
568 void setAttribute(std::string_view name, std::string_view new_value) const {
569 detail::CString n(name);
570 detail::CString v(new_value);
571 ulDOMElementSetAttribute(raw(), n.c_str(), v.c_str(), nullptr);
572 }
573
574 ///
575 /// Remove an attribute (removeAttribute). Does nothing if the element doesn't have it.
576 ///
577 /// @param name The attribute name (eg, `href`).
578 ///
579 void removeAttribute(std::string_view name) const {
580 detail::CString n(name);
581 ulDOMElementRemoveAttribute(raw(), n.c_str());
582 }
583
584 ///
585 /// Toggle an attribute (toggleAttribute). This removes the attribute if the element has it, or
586 /// adds it with an empty value if not.
587 ///
588 /// @param name The attribute name (eg, `hidden`).
589 ///
590 /// @return Returns whether the element has the attribute afterward (false for an invalid name).
591 ///
592 bool toggleAttribute(std::string_view name) const {
593 detail::CString n(name);
594 bool present = false;
595 ulDOMElementToggleAttribute(raw(), n.c_str(), nullptr, &present, nullptr);
596 return present;
597 }
598
599 ///
600 /// Add or remove an attribute (toggleAttribute with `force`).
601 ///
602 /// @param name The attribute name (eg, `hidden`).
603 ///
604 /// @param force True to add the attribute (with an empty value if it's missing), false to
605 /// remove it.
606 ///
607 /// @return Returns whether the element has the attribute afterward (false for an invalid name).
608 ///
609 bool toggleAttribute(std::string_view name, bool force) const {
610 detail::CString n(name);
611 bool present = false;
612 ulDOMElementToggleAttribute(raw(), n.c_str(), &force, &present, nullptr);
613 return present;
614 }
615
616 ///
617 /// Get the names of the element's attributes in order (getAttributeNames).
618 ///
619 /// @return Returns the names (empty if there are none).
620 ///
621 std::vector<std::string> getAttributeNames() const {
622 std::vector<std::string> names;
623 ULDOMStringList list = ulDOMElementGetAttributeNames(raw());
624 if (!list)
625 return names;
626 size_t count = ulDOMStringListGetLength(list);
627 names.reserve(count);
628 for (size_t i = 0; i < count; i++)
629 names.push_back(detail::TakeString(ulDOMStringListGetString(list, i)));
630 ulDestroyDOMStringList(list);
631 return names;
632 }
633
634 ///
635 /// Whether or not another element is this element or one of its descendants (contains).
636 ///
637 /// This takes an element only (the web's `contains()` takes any node).
638 ///
639 /// @param other The element to look for.
640 ///
641 /// @return Returns true if `other` is this element or inside it.
642 ///
643 bool contains(const Element& other) const {
644 return ulDOMElementContains(raw(), other.raw());
645 }
646
647 ///
648 /// Get the element's child elements in order (children).
649 ///
650 /// This is defined in `<Ultralight/dom/ElementList.h>`. Include that header (or
651 /// `<Ultralight/DOM.h>`) to call it.
652 ///
653 /// @return Returns the child elements (an empty list if there are none).
654 ///
655 /// @note Unlike the web's live collection, the list doesn't change when the document does. Call
656 /// this again to see later changes.
657 ///
658 ElementList children() const;
659
660 // --- Tree -------------------------------------------------------------------------------
661
662 ///
663 /// Add an element as the last child of this element (appendChild).
664 ///
665 /// If `child` is already in a document, it moves from its old position. An element from another
666 /// document (eg, an iframe's) moves into this one.
667 ///
668 /// @param child The element to add.
669 ///
670 /// @return Returns `child` (empty if nothing was added, eg, because `child` is this element or
671 /// one of its ancestors).
672 ///
673 /// @note A handle belongs to the page it came from. If you move an element here from another
674 /// document, its handle still stops working when that document's page goes away.
675 ///
676 Element appendChild(const Element& child) const {
677 return ulDOMElementAppendChild(raw(), child.raw(), nullptr) ? child : Element();
678 }
679
680 ///
681 /// Same as appendChild(), but returns a Result with the reason for a failure (eg, a
682 /// HierarchyRequestError for a cycle).
683 ///
684 /// @return Returns `child`. Fails with a HierarchyRequestError if `child` is this element or one
685 /// of its ancestors.
686 ///
687 [[nodiscard]] Result<Element> appendChild(const Element& child, Checked_t) const {
688 if (IsEmpty() || child.IsEmpty())
689 return detail::EmptyHandleError();
690 detail::ErrorScope error;
691 if (ulDOMElementAppendChild(raw(), child.raw(), error.out()))
692 return child;
693 return Unexpected<Error>(error.TakeError());
694 }
695
696 ///
697 /// Add a node of any kind as the last child of this element (appendChild).
698 ///
699 /// Use this for text and comment nodes (eg, from Document::createTextNode()). An Element
700 /// argument goes to the overload that returns an Element, and a DocumentFragment argument to
701 /// the overload that returns nothing.
702 ///
703 /// @param child The node to add. If it's already in a document, it moves.
704 ///
705 /// @return Returns `child` (empty if nothing was added, eg, because `child` is this element or
706 /// one of its ancestors).
707 ///
708 Node appendChild(const Node& child) const {
709 return ulDOMNodeAppendChild(Node::raw(), child.raw(), nullptr) ? child : Node();
710 }
711
712 ///
713 /// Same as appendChild() with a Node, but returns a Result with the reason for a failure (eg,
714 /// a HierarchyRequestError for a cycle).
715 ///
716 /// @return Returns `child`. Fails with a HierarchyRequestError if `child` can't go there (eg,
717 /// it's this element or one of its ancestors).
718 ///
719 [[nodiscard]] Result<Node> appendChild(const Node& child, Checked_t) const {
720 if (IsEmpty() || child.IsEmpty())
721 return detail::EmptyHandleError();
722 detail::ErrorScope error;
723 if (ulDOMNodeAppendChild(Node::raw(), child.raw(), error.out()))
724 return child;
725 return Unexpected<Error>(error.TakeError());
726 }
727
728 ///
729 /// Move all of a fragment's children to the end of this element (appendChild).
730 ///
731 /// The fragment is empty afterward. This returns nothing, where JavaScript returns the empty
732 /// fragment.
733 ///
734 /// This is defined in `<Ultralight/dom/DocumentFragment.h>`. Include that header (or
735 /// `<Ultralight/DOM.h>`) to call it.
736 ///
737 /// @param fragment The fragment whose children to move.
738 ///
739 void appendChild(const DocumentFragment& fragment) const;
740
741 ///
742 /// Same as appendChild() with a fragment, but returns a Result with the reason for a failure.
743 ///
744 /// @return Returns success (it fails only when a handle is empty or its page is gone).
745 ///
746 [[nodiscard]] Result<void> appendChild(const DocumentFragment& fragment, Checked_t) const;
747
748 ///
749 /// Add nodes and strings to the end of this element, after its last child (append).
750 ///
751 /// Pass any mix of nodes (including elements and fragments) and strings. Each string is added
752 /// as a new text node (it's never parsed as markup), and a fragment adds its children and is
753 /// left empty:
754 ///
755 /// ```
756 /// dom::Element item = doc.createElement("li");
757 /// item.append(icon, " Score: ", std::to_string(score));
758 /// ```
759 ///
760 /// @param nodes The nodes and strings to add, in order. A node that's already in a document
761 /// moves.
762 ///
763 /// @note With more than one argument, the nodes leave their old positions before the insertion
764 /// is checked (like the web), so a call that fails can leave them out of the page.
765 ///
766 template <typename... T>
767 requires((detail::NodeOrString<T> && ...))
768 void append(T&&... nodes) const {
769 auto items = detail::NodeOrStringItems(std::forward<T>(nodes)...);
770 ulDOMElementAppend(raw(), items.data(), items.size(), nullptr);
771 }
772
773 ///
774 /// Same as append(), but returns a Result with the reason for a failure (eg, a
775 /// HierarchyRequestError for a cycle).
776 ///
777 /// dom::Checked comes first here, before the nodes (`item.append(dom::Checked, icon, "x")`).
778 ///
779 /// @return Returns success. Fails with a HierarchyRequestError if one of the nodes can't go
780 /// there (eg, it's this element or one of its ancestors).
781 ///
782 template <typename... T>
783 requires((detail::NodeOrString<T> && ...))
784 [[nodiscard]] Result<void> append(Checked_t, T&&... nodes) const {
785 auto items = detail::NodeOrStringItems(std::forward<T>(nodes)...);
786 if (IsEmpty() || detail::HasEmptyItem(items))
787 return detail::EmptyHandleError();
788 detail::ErrorScope error;
789 if (ulDOMElementAppend(raw(), items.data(), items.size(), error.out()))
790 return {};
791 return Unexpected<Error>(error.TakeError());
792 }
793
794 ///
795 /// Add nodes and strings to the start of this element, before its first child (prepend).
796 ///
797 /// Works like append().
798 ///
799 /// @param nodes The nodes and strings to add, in order. A node that's already in a document
800 /// moves.
801 ///
802 /// @note With more than one argument, the nodes leave their old positions before the insertion
803 /// is checked (like the web), so a call that fails can leave them out of the page.
804 ///
805 template <typename... T>
806 requires((detail::NodeOrString<T> && ...))
807 void prepend(T&&... nodes) const {
808 auto items = detail::NodeOrStringItems(std::forward<T>(nodes)...);
809 ulDOMElementPrepend(raw(), items.data(), items.size(), nullptr);
810 }
811
812 ///
813 /// Same as prepend(), but returns a Result with the reason for a failure (eg, a
814 /// HierarchyRequestError for a cycle).
815 ///
816 /// dom::Checked comes first here, before the nodes (`item.prepend(dom::Checked, icon, "x")`).
817 ///
818 /// @return Returns success. Fails with a HierarchyRequestError if one of the nodes can't go
819 /// there (eg, it's this element or one of its ancestors).
820 ///
821 template <typename... T>
822 requires((detail::NodeOrString<T> && ...))
823 [[nodiscard]] Result<void> prepend(Checked_t, T&&... nodes) const {
824 auto items = detail::NodeOrStringItems(std::forward<T>(nodes)...);
825 if (IsEmpty() || detail::HasEmptyItem(items))
826 return detail::EmptyHandleError();
827 detail::ErrorScope error;
828 if (ulDOMElementPrepend(raw(), items.data(), items.size(), error.out()))
829 return {};
830 return Unexpected<Error>(error.TakeError());
831 }
832
833 ///
834 /// Replace all of this element's children with nodes and strings (replaceChildren).
835 ///
836 /// Pass any mix of nodes (including elements and fragments) and strings, like append(). With
837 /// no arguments, this removes all the children.
838 ///
839 /// @param new_children The nodes and strings to add, in order. A node that's already in a
840 /// document moves.
841 ///
842 /// @note With more than one argument, the nodes leave their old positions before the insertion
843 /// is checked (like the web), so a call that fails can leave them out of the page.
844 ///
845 template <typename... T>
846 requires((detail::NodeOrString<T> && ...))
847 void replaceChildren(T&&... new_children) const {
848 auto items = detail::NodeOrStringItems(std::forward<T>(new_children)...);
849 ulDOMElementReplaceChildren(raw(), items.data(), items.size(), nullptr);
850 }
851
852 ///
853 /// Same as replaceChildren(), but returns a Result with the reason for a failure (eg, a
854 /// HierarchyRequestError for a cycle).
855 ///
856 /// dom::Checked comes first here, before the nodes:
857 ///
858 /// ```
859 /// dom::Result<void> result = list.replaceChildren(dom::Checked, header, row);
860 /// ```
861 ///
862 /// @return Returns success. Fails with a HierarchyRequestError if one of the nodes can't go
863 /// there (eg, it's this element or one of its ancestors).
864 ///
865 template <typename... T>
866 requires((detail::NodeOrString<T> && ...))
867 [[nodiscard]] Result<void> replaceChildren(Checked_t, T&&... new_children) const {
868 auto items = detail::NodeOrStringItems(std::forward<T>(new_children)...);
869 if (IsEmpty() || detail::HasEmptyItem(items))
870 return detail::EmptyHandleError();
871 detail::ErrorScope error;
872 if (ulDOMElementReplaceChildren(raw(), items.data(), items.size(), error.out()))
873 return {};
874 return Unexpected<Error>(error.TakeError());
875 }
876
877 ///
878 /// Insert an element before one of this element's children (insertBefore).
879 ///
880 /// @param child The element to insert. If it's already in a document, it moves.
881 ///
882 /// @param ref_child The child to insert before. Pass an empty Element to add `child` at the
883 /// end.
884 ///
885 /// @return Returns `child` (empty if nothing was inserted, eg, because `ref_child` isn't a
886 /// child of this element).
887 ///
888 Element insertBefore(const Element& child, const Element& ref_child) const {
889 return ulDOMElementInsertBefore(raw(), child.raw(), ref_child.raw(), nullptr) ? child
890 : Element();
891 }
892
893 ///
894 /// Same as insertBefore(), but returns a Result with the reason for a failure (eg, a
895 /// NotFoundError when `ref_child` isn't a child of this element).
896 ///
897 /// @return Returns `child`. Fails with a NotFoundError if `ref_child` isn't a child of this
898 /// element, or a HierarchyRequestError if `child` is this element or one of its
899 /// ancestors.
900 ///
901 [[nodiscard]] Result<Element> insertBefore(const Element& child, const Element& ref_child,
902 Checked_t) const {
903 if (IsEmpty() || child.IsEmpty())
904 return detail::EmptyHandleError();
905 detail::ErrorScope error;
906 if (ulDOMElementInsertBefore(raw(), child.raw(),
907 ref_child.raw(), error.out()))
908 return child;
909 return Unexpected<Error>(error.TakeError());
910 }
911
912 ///
913 /// Insert a node of any kind before one of this element's children (insertBefore).
914 ///
915 /// Use this for text and comment nodes, or a reference child that isn't an element. With two
916 /// Element arguments, the overload that returns an Element is used.
917 ///
918 /// @param child The node to insert. If it's already in a document, it moves.
919 ///
920 /// @param ref_child The child to insert before. Pass an empty Node to add `child` at the end.
921 ///
922 /// @return Returns `child` (empty if nothing was inserted, eg, because `ref_child` isn't a
923 /// child of this element).
924 ///
925 Node insertBefore(const Node& child, const Node& ref_child) const {
926 return ulDOMNodeInsertBefore(Node::raw(), child.raw(), ref_child.raw(), nullptr) ? child
927 : Node();
928 }
929
930 ///
931 /// Same as insertBefore() with Nodes, but returns a Result with the reason for a failure (eg,
932 /// a NotFoundError when `ref_child` isn't a child of this element).
933 ///
934 /// @return Returns `child`. Fails with a NotFoundError if `ref_child` isn't a child of this
935 /// element, or a HierarchyRequestError if `child` can't go there (eg, it's this
936 /// element or one of its ancestors).
937 ///
938 [[nodiscard]] Result<Node> insertBefore(const Node& child, const Node& ref_child,
939 Checked_t) const {
940 if (IsEmpty() || child.IsEmpty())
941 return detail::EmptyHandleError();
942 detail::ErrorScope error;
943 if (ulDOMNodeInsertBefore(Node::raw(), child.raw(), ref_child.raw(), error.out()))
944 return child;
945 return Unexpected<Error>(error.TakeError());
946 }
947
948 ///
949 /// Replace one of this element's children with another element (replaceChild).
950 ///
951 /// @param new_child The element to put in its place (it comes first, as on the web). If it's
952 /// already in a document, it moves.
953 ///
954 /// @param old_child The child to replace.
955 ///
956 /// @return Returns `old_child`, now removed from the document (empty if nothing was replaced,
957 /// eg, because `old_child` isn't a child of this element).
958 ///
959 Element replaceChild(const Element& new_child, const Element& old_child) const {
960 return ulDOMElementReplaceChild(raw(), new_child.raw(), old_child.raw(), nullptr)
961 ? old_child
962 : Element();
963 }
964
965 ///
966 /// Same as replaceChild(), but returns a Result with the reason for a failure (eg, a
967 /// NotFoundError when `old_child` isn't a child of this element).
968 ///
969 /// @return Returns `old_child`, now removed from the document. Fails with a NotFoundError if
970 /// `old_child` isn't a child of this element, or a HierarchyRequestError if `new_child`
971 /// is this element or one of its ancestors.
972 ///
973 [[nodiscard]] Result<Element> replaceChild(const Element& new_child, const Element& old_child,
974 Checked_t) const {
975 if (IsEmpty() || new_child.IsEmpty() || old_child.IsEmpty())
976 return detail::EmptyHandleError();
977 detail::ErrorScope error;
978 if (ulDOMElementReplaceChild(raw(), new_child.raw(),
979 old_child.raw(), error.out()))
980 return old_child;
981 return Unexpected<Error>(error.TakeError());
982 }
983
984 ///
985 /// Replace one of this element's children, of any kind, with a node of any kind
986 /// (replaceChild).
987 ///
988 /// With two Element arguments, the overload that returns an Element is used.
989 ///
990 /// @param new_child The node to put in its place (it comes first, as on the web). If it's
991 /// already in a document, it moves.
992 ///
993 /// @param old_child The child to replace.
994 ///
995 /// @return Returns `old_child`, now removed from the document (empty if nothing was replaced,
996 /// eg, because `old_child` isn't a child of this element).
997 ///
998 Node replaceChild(const Node& new_child, const Node& old_child) const {
999 return ulDOMNodeReplaceChild(Node::raw(), new_child.raw(), old_child.raw(), nullptr)
1000 ? old_child
1001 : Node();
1002 }
1003
1004 ///
1005 /// Same as replaceChild() with Nodes, but returns a Result with the reason for a failure (eg,
1006 /// a NotFoundError when `old_child` isn't a child of this element).
1007 ///
1008 /// @return Returns `old_child`, now removed from the document. Fails with a NotFoundError if
1009 /// `old_child` isn't a child of this element, or a HierarchyRequestError if
1010 /// `new_child` can't go there (eg, it's this element or one of its ancestors).
1011 ///
1012 [[nodiscard]] Result<Node> replaceChild(const Node& new_child, const Node& old_child,
1013 Checked_t) const {
1014 if (IsEmpty() || new_child.IsEmpty() || old_child.IsEmpty())
1015 return detail::EmptyHandleError();
1016 detail::ErrorScope error;
1017 if (ulDOMNodeReplaceChild(Node::raw(), new_child.raw(), old_child.raw(), error.out()))
1018 return old_child;
1019 return Unexpected<Error>(error.TakeError());
1020 }
1021
1022 ///
1023 /// Remove one of this element's children (removeChild).
1024 ///
1025 /// @param child The child to remove.
1026 ///
1027 /// @return Returns `child`, now removed from the document (empty if `child` isn't a child of
1028 /// this element).
1029 ///
1030 Element removeChild(const Element& child) const {
1031 return ulDOMElementRemoveChild(raw(), child.raw(), nullptr) ? child : Element();
1032 }
1033
1034 ///
1035 /// Same as removeChild(), but returns a Result with the reason for a failure (eg, a
1036 /// NotFoundError when `child` isn't a child of this element).
1037 ///
1038 /// @return Returns `child`, now removed from the document. Fails with a NotFoundError if `child`
1039 /// isn't a child of this element.
1040 ///
1041 [[nodiscard]] Result<Element> removeChild(const Element& child, Checked_t) const {
1042 if (IsEmpty() || child.IsEmpty())
1043 return detail::EmptyHandleError();
1044 detail::ErrorScope error;
1045 if (ulDOMElementRemoveChild(raw(), child.raw(), error.out()))
1046 return child;
1047 return Unexpected<Error>(error.TakeError());
1048 }
1049
1050 ///
1051 /// Remove one of this element's children, of any kind (removeChild).
1052 ///
1053 /// With an Element argument, the overload that returns an Element is used.
1054 ///
1055 /// @param child The child to remove.
1056 ///
1057 /// @return Returns `child`, now removed from the document (empty if `child` isn't a child of
1058 /// this element).
1059 ///
1060 Node removeChild(const Node& child) const {
1061 return ulDOMNodeRemoveChild(Node::raw(), child.raw(), nullptr) ? child : Node();
1062 }
1063
1064 ///
1065 /// Same as removeChild() with a Node, but returns a Result with the reason for a failure (eg,
1066 /// a NotFoundError when `child` isn't a child of this element).
1067 ///
1068 /// @return Returns `child`, now removed from the document. Fails with a NotFoundError if `child`
1069 /// isn't a child of this element.
1070 ///
1071 [[nodiscard]] Result<Node> removeChild(const Node& child, Checked_t) const {
1072 if (IsEmpty() || child.IsEmpty())
1073 return detail::EmptyHandleError();
1074 detail::ErrorScope error;
1075 if (ulDOMNodeRemoveChild(Node::raw(), child.raw(), error.out()))
1076 return child;
1077 return Unexpected<Error>(error.TakeError());
1078 }
1079
1080 ///
1081 /// Copy this element (cloneNode). The copy has the same attributes and isn't in the document.
1082 ///
1083 /// @param deep True to also copy the element's descendants.
1084 ///
1085 /// @return Returns the copy.
1086 ///
1087 Element cloneNode(bool deep = false) const {
1088 return Element(ulDOMElementCloneNode(raw(), deep));
1089 }
1090
1091 ///
1092 /// Parse markup and insert it relative to this element (insertAdjacentHTML).
1093 ///
1094 /// @param position Where to insert. `beforebegin` and `afterend` insert before and after this
1095 /// element, and `afterbegin` and `beforeend` insert before its first child
1096 /// and after its last child. Case doesn't matter.
1097 ///
1098 /// @param html The markup to insert. Scripts in it never run.
1099 ///
1100 /// @note Nothing is inserted for an unknown `position`, or for `beforebegin` or `afterend` when
1101 /// the element has no parent or is the `<html>` root.
1102 ///
1103 void insertAdjacentHTML(std::string_view position, std::string_view html) const {
1104 detail::CString p(position);
1105 ulDOMElementInsertAdjacentHTML(raw(), p.c_str(), html.data() ? html.data() : "", html.size(),
1106 nullptr);
1107 }
1108
1109 ///
1110 /// Same as insertAdjacentHTML(), but returns a Result with the reason for a failure (eg, a
1111 /// SyntaxError for an unknown `position`).
1112 ///
1113 /// @return Returns success. Fails with a SyntaxError for an unknown `position`, or a
1114 /// NoModificationAllowedError for `beforebegin` or `afterend` when the element has no
1115 /// parent or is the `<html>` root.
1116 ///
1117 [[nodiscard]] Result<void> insertAdjacentHTML(std::string_view position, std::string_view html,
1118 Checked_t) const {
1119 if (IsEmpty())
1120 return detail::EmptyHandleError();
1121 detail::CString p(position);
1122 detail::ErrorScope error;
1123 if (ulDOMElementInsertAdjacentHTML(raw(), p.c_str(),
1124 html.data() ? html.data() : "", html.size(),
1125 error.out()))
1126 return {};
1127 return Unexpected<Error>(error.TakeError());
1128 }
1129
1130 ///
1131 /// Insert an element relative to this element (insertAdjacentElement).
1132 ///
1133 /// @param position Where to insert. `beforebegin` and `afterend` insert before and after this
1134 /// element, and `afterbegin` and `beforeend` insert before its first child
1135 /// and after its last child. Case doesn't matter.
1136 ///
1137 /// @param other The element to insert. If it's already in a document, it moves.
1138 ///
1139 /// @return Returns `other` (empty if nothing was inserted, eg, for an unknown `position`, or for
1140 /// `beforebegin` or `afterend` when this element has no parent).
1141 ///
1142 Element insertAdjacentElement(std::string_view position, const Element& other) const {
1143 detail::CString p(position);
1144 return Element(ulDOMElementInsertAdjacentElement(raw(), p.c_str(), other.raw(), nullptr));
1145 }
1146
1147 ///
1148 /// Same as insertAdjacentElement(), but returns a Result with the reason for a failure (eg, a
1149 /// SyntaxError for an unknown `position`).
1150 ///
1151 /// @return Returns `other`, or an empty Element if `position` is `beforebegin` or `afterend`
1152 /// and this element has no parent (nothing is inserted, like the web). Fails with a
1153 /// SyntaxError for an unknown `position`, or a HierarchyRequestError if the insertion
1154 /// isn't allowed (eg, `other` is an ancestor of this element).
1155 ///
1156 [[nodiscard]] Result<Element> insertAdjacentElement(std::string_view position,
1157 const Element& other, Checked_t) const {
1158 if (IsEmpty() || other.IsEmpty())
1159 return detail::EmptyHandleError();
1160 detail::CString p(position);
1161 detail::ErrorScope error;
1162 ULDOMElement result = ulDOMElementInsertAdjacentElement(
1163 raw(), p.c_str(), other.raw(), error.out());
1164 if (result)
1165 return Element(result);
1166 if (error.has_exception())
1167 return Unexpected<Error>(error.TakeError());
1168 if (IsAlive() && other.IsAlive())
1169 return Element(); // The spec's parentless silent no-op.
1170 return Unexpected<Error>(Error::PageGone());
1171 }
1172
1173 ///
1174 /// Insert text relative to this element (insertAdjacentText).
1175 ///
1176 /// The text is never parsed as markup, so this is safe for untrusted text.
1177 ///
1178 /// @param position Where to insert. `beforebegin` and `afterend` insert before and after this
1179 /// element, and `afterbegin` and `beforeend` insert before its first child
1180 /// and after its last child. Case doesn't matter.
1181 ///
1182 /// @param text The text to insert.
1183 ///
1184 /// @note Nothing is inserted for an unknown `position`, or for `beforebegin` or `afterend` when
1185 /// this element has no parent or is the `<html>` root.
1186 ///
1187 void insertAdjacentText(std::string_view position, std::string_view text) const {
1188 detail::CString p(position);
1189 ulDOMElementInsertAdjacentText(raw(), p.c_str(), text.data() ? text.data() : "", text.size(),
1190 nullptr);
1191 }
1192
1193 ///
1194 /// Same as insertAdjacentText(), but returns a Result with the reason for a failure (eg, a
1195 /// SyntaxError for an unknown `position`).
1196 ///
1197 /// @return Returns success (nothing is inserted for `beforebegin` or `afterend` when this
1198 /// element has no parent, like the web). Fails with a SyntaxError for an unknown
1199 /// `position`, or a HierarchyRequestError for `beforebegin` or `afterend` on the
1200 /// `<html>` root.
1201 ///
1202 [[nodiscard]] Result<void> insertAdjacentText(std::string_view position, std::string_view text,
1203 Checked_t) const {
1204 if (IsEmpty())
1205 return detail::EmptyHandleError();
1206 detail::CString p(position);
1207 detail::ErrorScope error;
1208 if (ulDOMElementInsertAdjacentText(raw(), p.c_str(),
1209 text.data() ? text.data() : "", text.size(),
1210 error.out()))
1211 return {};
1212 return Unexpected<Error>(error.TakeError());
1213 }
1214
1215 // --- Traversal (read-only properties are calls; see the class doc) -----------------------
1216
1217 ///
1218 /// Get the element's tag name (tagName).
1219 ///
1220 /// @return Returns the tag name (uppercase for HTML elements, eg, `DIV`).
1221 ///
1222 std::string tagName() const {
1223 return detail::TakeString(ulDOMElementGetTagName(raw()));
1224 }
1225
1226 ///
1227 /// Get the element's parent element (parentElement).
1228 ///
1229 /// @return Returns the parent element (empty if the parent isn't an element).
1230 ///
1231 Element parentElement() const { return Element(ulDOMElementGetParentElement(raw())); }
1232
1233 ///
1234 /// Get the element's first child element (firstElementChild).
1235 ///
1236 /// @return Returns the first child element (empty if there's none).
1237 ///
1239 return Element(ulDOMElementGetFirstElementChild(raw()));
1240 }
1241
1242 ///
1243 /// Get the element's last child element (lastElementChild).
1244 ///
1245 /// @return Returns the last child element (empty if there's none).
1246 ///
1248 return Element(ulDOMElementGetLastElementChild(raw()));
1249 }
1250
1251 ///
1252 /// Get the element's next sibling element (nextElementSibling).
1253 ///
1254 /// @return Returns the next sibling element (empty if there's none).
1255 ///
1257 return Element(ulDOMElementGetNextElementSibling(raw()));
1258 }
1259
1260 ///
1261 /// Get the element's previous sibling element (previousElementSibling).
1262 ///
1263 /// @return Returns the previous sibling element (empty if there's none).
1264 ///
1266 return Element(ulDOMElementGetPreviousElementSibling(raw()));
1267 }
1268
1269 ///
1270 /// Get the number of the element's child elements (childElementCount).
1271 ///
1272 /// @return Returns the number of child elements.
1273 ///
1274 size_t childElementCount() const { return ulDOMElementGetChildElementCount(raw()); }
1275
1276 ///
1277 /// Get the document this element belongs to (ownerDocument).
1278 ///
1279 /// This is defined in `<Ultralight/dom/Document.h>`. Include that header (or
1280 /// `<Ultralight/DOM.h>`) to call it.
1281 ///
1282 /// @return Returns the document of the page this handle came from.
1283 ///
1284 Document ownerDocument() const;
1285
1286 // --- Geometry and scrolling ---------------------------------------------------------------
1287
1288 ///
1289 /// Get the element's bounding rectangle relative to the viewport (getBoundingClientRect).
1290 ///
1291 /// @return Returns the rectangle in CSS pixels (all zeros if the element isn't displayed). For
1292 /// an element in an iframe, it's relative to the iframe's viewport.
1293 ///
1294 /// @note This updates the layout first if the page has pending changes (so do the offset,
1295 /// client, and scroll size reads). Make all your changes first, then read.
1296 ///
1298 ULDOMRect r = ulDOMElementGetBoundingClientRect(raw());
1299 return DOMRect { r.x, r.y, r.width, r.height };
1300 }
1301
1302 ///
1303 /// Get the element's width in CSS pixels, including its padding and borders (offsetWidth).
1304 ///
1305 /// @return Returns the width in whole pixels (0 if the element isn't displayed).
1306 ///
1307 int offsetWidth() const { return ulDOMElementGetOffsetWidth(raw()); }
1308
1309 ///
1310 /// Get the element's height in CSS pixels, including its padding and borders (offsetHeight).
1311 ///
1312 /// @return Returns the height in whole pixels (0 if the element isn't displayed).
1313 ///
1314 int offsetHeight() const { return ulDOMElementGetOffsetHeight(raw()); }
1315
1316 ///
1317 /// Get the distance from the element's top border edge to its offsetParent()'s top padding
1318 /// edge, in CSS pixels (offsetTop).
1319 ///
1320 /// @return Returns the distance in whole pixels (0 if the element isn't displayed).
1321 ///
1322 int offsetTop() const { return ulDOMElementGetOffsetTop(raw()); }
1323
1324 ///
1325 /// Get the distance from the element's left border edge to its offsetParent()'s left padding
1326 /// edge, in CSS pixels (offsetLeft).
1327 ///
1328 /// @return Returns the distance in whole pixels (0 if the element isn't displayed).
1329 ///
1330 int offsetLeft() const { return ulDOMElementGetOffsetLeft(raw()); }
1331
1332 ///
1333 /// Get the element that offsetTop() and offsetLeft() measure from (offsetParent).
1334 ///
1335 /// This is the nearest positioned ancestor (or a table cell or table around the element), or
1336 /// the `<body>` if there's none.
1337 ///
1338 /// @return Returns the offset parent (empty if the element isn't displayed, has fixed
1339 /// positioning, or is the `<html>` or `<body>` element).
1340 ///
1341 Element offsetParent() const { return Element(ulDOMElementGetOffsetParent(raw())); }
1342
1343 ///
1344 /// Get the element's inner width in CSS pixels, including its padding but not its borders or
1345 /// scrollbar (clientWidth).
1346 ///
1347 /// @return Returns the width in whole pixels (0 if the element isn't displayed or is inline).
1348 ///
1349 int clientWidth() const { return ulDOMElementGetClientWidth(raw()); }
1350
1351 ///
1352 /// Get the element's inner height in CSS pixels, including its padding but not its borders or
1353 /// scrollbar (clientHeight).
1354 ///
1355 /// @return Returns the height in whole pixels (0 if the element isn't displayed or is inline).
1356 ///
1357 int clientHeight() const { return ulDOMElementGetClientHeight(raw()); }
1358
1359 ///
1360 /// Get the width of the element's top border, in CSS pixels (clientTop).
1361 ///
1362 /// @return Returns the width in whole pixels (0 if the element isn't displayed or is inline).
1363 ///
1364 int clientTop() const { return ulDOMElementGetClientTop(raw()); }
1365
1366 ///
1367 /// Get the width of the element's left border, in CSS pixels (clientLeft).
1368 ///
1369 /// @return Returns the width in whole pixels (0 if the element isn't displayed or is inline).
1370 ///
1371 int clientLeft() const { return ulDOMElementGetClientLeft(raw()); }
1372
1373 ///
1374 /// Get the width of the element's content in CSS pixels, including any part scrolled out of
1375 /// view (scrollWidth).
1376 ///
1377 /// @return Returns the width in whole pixels.
1378 ///
1379 int scrollWidth() const { return ulDOMElementGetScrollWidth(raw()); }
1380
1381 ///
1382 /// Get the height of the element's content in CSS pixels, including any part scrolled out of
1383 /// view (scrollHeight).
1384 ///
1385 /// @return Returns the height in whole pixels.
1386 ///
1387 int scrollHeight() const { return ulDOMElementGetScrollHeight(raw()); }
1388
1389 ///
1390 /// Scroll the element's ancestors so the element is visible (scrollIntoView).
1391 ///
1392 /// Scrolling is instant (CSS `scroll-behavior` is ignored). This does nothing if the element
1393 /// isn't displayed.
1394 ///
1395 /// @param align_to_top True to line up the element's top with the top of the visible area,
1396 /// false to line up the bottoms.
1397 ///
1398 void scrollIntoView(bool align_to_top = true) const {
1399 ulDOMElementScrollIntoView(raw(), align_to_top);
1400 }
1401
1402 ///
1403 /// Scroll the element's content to a position (scrollTo).
1404 ///
1405 /// The content scrolls at once (CSS `scroll-behavior` is ignored), and the position stays
1406 /// within the scrollable area. This does nothing if the element isn't displayed or has nothing
1407 /// to scroll.
1408 ///
1409 /// @param x The horizontal position in CSS pixels (any fraction is dropped). A value that
1410 /// isn't finite (eg, NaN) counts as 0.
1411 ///
1412 /// @param y The vertical position in CSS pixels (any fraction is dropped). A value that isn't
1413 /// finite counts as 0.
1414 ///
1415 void scrollTo(double x, double y) const { ulDOMElementScrollTo(raw(), x, y); }
1416
1417 ///
1418 /// Scroll the element's content by an offset (scrollBy).
1419 ///
1420 /// The content scrolls the same way as with scrollTo().
1421 ///
1422 /// @param x The horizontal offset in CSS pixels. A value that isn't finite counts as 0.
1423 ///
1424 /// @param y The vertical offset in CSS pixels. A value that isn't finite counts as 0.
1425 ///
1426 void scrollBy(double x, double y) const { ulDOMElementScrollBy(raw(), x, y); }
1427
1428 // --- Focus and activation -----------------------------------------------------------------
1429
1430 ///
1431 /// Give the element keyboard focus (focus).
1432 ///
1433 /// This scrolls the element into view, like the web. It does nothing if the element can't take
1434 /// focus or isn't in the document.
1435 ///
1436 void focus() const { ulDOMElementFocus(raw()); }
1437
1438 ///
1439 /// Remove keyboard focus from the element (blur). Does nothing if it doesn't have focus.
1440 ///
1441 void blur() const { ulDOMElementBlur(raw()); }
1442
1443 ///
1444 /// Click the element (click).
1445 ///
1446 /// This fires one `click` event and runs the element's click behavior. For example, a checkbox
1447 /// toggles and fires `input` and `change`, a button activates, and a link navigates. There's no
1448 /// mouse movement and no `mousedown` or `mouseup`, like the web. The event's `isTrusted` is
1449 /// false.
1450 ///
1451 /// @note This does nothing on a disabled form control or a non-HTML element (eg, SVG). Use
1452 /// View::FireMouseEvent() to click like a real user.
1453 ///
1454 void click() const { ulDOMElementClick(raw()); }
1455
1456 // --- Events -------------------------------------------------------------------------------
1457
1458 ///
1459 /// Dispatch a synthetic event to this element (dispatchEvent).
1460 ///
1461 /// Every listener (native and page JavaScript) runs before this returns. The event's `isTrusted`
1462 /// is false.
1463 ///
1464 /// A default action that depends only on the event type still runs, so a synthetic `click`
1465 /// follows a link. Other default actions don't run. A synthetic `click` doesn't toggle a
1466 /// checkbox, and a synthetic `submit` doesn't submit its form (use click() or
1467 /// HTMLFormElement::requestSubmit() for those).
1468 ///
1469 /// @param type The event type (eg, `click`). An empty type dispatches nothing.
1470 ///
1471 /// @param init The event's bubbles, cancelable, and composed flags (see EventInit). They're all
1472 /// false by default.
1473 ///
1474 /// @return Returns false if a listener canceled the event with Event::preventDefault() (which
1475 /// needs `init.cancelable`), or true otherwise.
1476 ///
1477 bool dispatchEvent(std::string_view type, const EventInit& init = {}) const {
1478 detail::CString t(type);
1479 return ulDOMElementDispatchEvent(raw(), t.c_str(),
1480 detail::EventInitFlags(init), nullptr);
1481 }
1482
1483 ///
1484 /// Dispatch a synthetic CustomEvent with a string payload to this element.
1485 ///
1486 /// This works like dispatchEvent(). Listeners read the payload as the event's `detail`. Page
1487 /// JavaScript gets it as a string, and a native listener gets it from Event::AsCustom() (this
1488 /// works with JavaScript disabled too). To send structured data, pass JSON and parse it in the
1489 /// listener.
1490 ///
1491 /// @param type The event type (eg, `game-saved`). An empty type dispatches nothing.
1492 ///
1493 /// @param event_detail The payload as UTF-8 text. It ends at the first null character, and
1494 /// text that isn't valid UTF-8 arrives as no payload.
1495 ///
1496 /// @param init The event's bubbles, cancelable, and composed flags (see EventInit).
1497 /// They're all false by default.
1498 ///
1499 /// @return Returns false if a listener canceled the event with Event::preventDefault() (which
1500 /// needs `init.cancelable`), or true otherwise.
1501 ///
1502 /// @see js::API::Emit()
1503 ///
1504 bool dispatchCustomEvent(std::string_view type, std::string_view event_detail,
1505 const EventInit& init = {}) const {
1506 detail::CString t(type);
1507 detail::CString d(event_detail);
1508 return ulDOMElementDispatchCustomEvent(raw(), t.c_str(),
1509 detail::EventInitFlags(init), d.c_str(),
1510 nullptr);
1511 }
1512
1513 ///
1514 /// Listen for an event on this element (addEventListener).
1515 ///
1516 /// The callback can take the event or nothing:
1517 ///
1518 /// ```
1519 /// button.addEventListener("click", [](dom::Event event) { Log(event.type()); });
1520 /// button.addEventListener("mouseenter", [] { Highlight(); });
1521 /// button.addEventListener("keydown", OnKey, { .capture = true, .signal = controller.signal() });
1522 /// ```
1523 ///
1524 /// The callback runs on the Renderer's thread while the event is dispatched (eg, inside
1525 /// View::FireMouseEvent() or dispatchEvent()). It can change the DOM and add or remove
1526 /// listeners, its own included.
1527 ///
1528 /// @param type The event type to listen for (eg, `click`).
1529 ///
1530 /// @param callback The callable to run on each event, taking (dom::Event) or ().
1531 ///
1532 /// @param options The listener's options (see AddEventListenerOptions).
1533 ///
1534 /// @return Returns a handle for removing the listener, which you can ignore. It's empty when
1535 /// nothing was added (the page is gone or `options.signal` is already aborted).
1536 ///
1537 /// @see EventListener, AbortController
1538 ///
1539 template <typename F>
1540 EventListener addEventListener(std::string_view type, F&& callback,
1541 const AddEventListenerOptions& options = {}) const {
1542 using Fn = std::decay_t<F>;
1543 static_assert(detail::InvocableWithEvent<Fn> || std::is_invocable_v<Fn&>,
1544 "addEventListener takes a callable invocable with (dom::Event) or ()");
1545 detail::CString t(type);
1546 ULDOMEventCallback thunk = &detail::EventThunk<Fn>;
1547 ULUserDataDestroyCallback destroy = &detail::DeleteCallable<Fn>;
1548 return detail::AddListener<Fn>(std::forward<F>(callback), options,
1549 [&](unsigned flags, void* fn) {
1550 return ulDOMElementAddEventListener(raw(), t.c_str(), flags,
1551 thunk, fn, destroy);
1552 });
1553 }
1554
1555 ///
1556 /// Listen for an event on this element, with options as flags (eg, `dom::Once | dom::Capture`).
1557 ///
1558 /// @param type The event type to listen for.
1559 ///
1560 /// @param callback The callable to run on each event, taking (dom::Event) or ().
1561 ///
1562 /// @param flags The listener's options (see EventListenerFlags).
1563 ///
1564 /// @return Returns a handle for removing the listener, which you can ignore (empty when the
1565 /// page is gone).
1566 ///
1567 template <typename F>
1568 EventListener addEventListener(std::string_view type, F&& callback,
1569 EventListenerFlags flags) const {
1570 return addEventListener(type, std::forward<F>(callback), detail::ToOptions(flags));
1571 }
1572
1573 ///
1574 /// Listen by calling a member function on an object you keep alive.
1575 ///
1576 /// ```
1577 /// button.addEventListener("click", *this, &Hud::OnSave, { .signal = listeners_.signal() });
1578 /// ```
1579 ///
1580 /// @param type The event type to listen for.
1581 ///
1582 /// @param receiver The object to call `method` on.
1583 ///
1584 /// @param method The member function to call, taking (dom::Event) or ().
1585 ///
1586 /// @param options The listener's options (see AddEventListenerOptions).
1587 ///
1588 /// @return Returns a handle for removing the listener, which you can ignore. It's empty when
1589 /// nothing was added (the page is gone or `options.signal` is already aborted).
1590 ///
1591 /// @warning The listener calls `receiver` for as long as it lives, so `receiver` must outlive
1592 /// the page, or the listener must be added with the signal of an AbortController
1593 /// that `receiver` owns (as above). The smart-pointer overload with a std::weak_ptr
1594 /// skips calls once the object is gone instead.
1595 ///
1596 template <typename C, typename M>
1597 requires(std::is_class_v<C> && std::is_member_function_pointer_v<M>
1599 EventListener addEventListener(std::string_view type, C& receiver, M method,
1600 const AddEventListenerOptions& options = {}) const {
1601 return addEventListener(type, detail::WrapBorrowedMember(receiver, method), options);
1602 }
1603
1604 ///
1605 /// Listen by calling a member function on an object you keep alive, with options as flags.
1606 ///
1607 /// @param type The event type to listen for.
1608 ///
1609 /// @param receiver The object to call `method` on.
1610 ///
1611 /// @param method The member function to call, taking (dom::Event) or ().
1612 ///
1613 /// @param flags The listener's options (see EventListenerFlags).
1614 ///
1615 /// @return Returns a handle for removing the listener, which you can ignore (empty when the
1616 /// page is gone).
1617 ///
1618 /// @warning The listener calls `receiver` for as long as it lives, so `receiver` must outlive
1619 /// the page. To tie the listener to `receiver` instead, use the options overload
1620 /// with the signal of an AbortController that `receiver` owns.
1621 ///
1622 template <typename C, typename M>
1623 requires(std::is_class_v<C> && std::is_member_function_pointer_v<M>
1624 && !LockableHolder<C>)
1625 EventListener addEventListener(std::string_view type, C& receiver, M method,
1626 EventListenerFlags flags) const {
1627 return addEventListener(type, detail::WrapBorrowedMember(receiver, method), flags);
1628 }
1629
1630 ///
1631 /// Listen by calling a member function through a smart pointer that's checked before each call.
1632 ///
1633 /// A std::shared_ptr keeps the object alive for as long as the listener exists. A std::weak_ptr
1634 /// doesn't, and events are skipped once the object is gone.
1635 ///
1636 /// @param type The event type to listen for.
1637 ///
1638 /// @param holder A std::shared_ptr, a std::weak_ptr, or another smart pointer with a
1639 /// HolderTraits specialization (see LockableHolder).
1640 ///
1641 /// @param method The member function to call, taking (dom::Event) or ().
1642 ///
1643 /// @param options The listener's options (see AddEventListenerOptions).
1644 ///
1645 /// @return Returns a handle for removing the listener, which you can ignore. It's empty when
1646 /// nothing was added (the page is gone or `options.signal` is already aborted).
1647 ///
1648 template <typename H, typename M>
1649 requires(LockableHolder<H> && std::is_member_function_pointer_v<M>)
1650 EventListener addEventListener(std::string_view type, H holder, M method,
1651 const AddEventListenerOptions& options = {}) const {
1652 return addEventListener(type, detail::WrapHolderMember(std::move(holder), method), options);
1653 }
1654
1655 ///
1656 /// Listen by calling a member function through a smart pointer, with options as flags.
1657 ///
1658 /// @param type The event type to listen for.
1659 ///
1660 /// @param holder A std::shared_ptr, a std::weak_ptr, or another smart pointer with a
1661 /// HolderTraits specialization (see LockableHolder).
1662 ///
1663 /// @param method The member function to call, taking (dom::Event) or ().
1664 ///
1665 /// @param flags The listener's options (see EventListenerFlags).
1666 ///
1667 /// @return Returns a handle for removing the listener, which you can ignore (empty when the
1668 /// page is gone).
1669 ///
1670 template <typename H, typename M>
1671 requires(LockableHolder<H> && std::is_member_function_pointer_v<M>)
1672 EventListener addEventListener(std::string_view type, H holder, M method,
1673 EventListenerFlags flags) const {
1674 return addEventListener(type, detail::WrapHolderMember(std::move(holder), method), flags);
1675 }
1676
1677 ///
1678 /// Listen for an event on the elements inside this one that match a CSS selector, including
1679 /// elements added later (event delegation).
1680 ///
1681 /// One listener on this element covers the whole subtree. When an event reaches this element,
1682 /// the library checks the event's target and its ancestors (up to and including this element)
1683 /// against `selector`. The callback runs with the closest match, and doesn't run if nothing
1684 /// matches:
1685 ///
1686 /// ```
1687 /// table.On("tr.item", "click", [](dom::Event event, dom::Element row) { Select(row); });
1688 /// ```
1689 ///
1690 /// Events that don't bubble (eg, `focus`) reach this element only in the capture phase, so
1691 /// pass `{ .capture = true }` (or dom::Capture) for them.
1692 ///
1693 /// @param selector A CSS selector (eg, `tr.item`). A malformed selector adds nothing and
1694 /// logs a warning.
1695 ///
1696 /// @param type The event type to listen for.
1697 ///
1698 /// @param callback The callable to run for each match, taking (dom::Event, dom::Element),
1699 /// (dom::Element), or ().
1700 ///
1701 /// @param options The listener's options (see AddEventListenerOptions).
1702 ///
1703 /// @return Returns a handle for removing the listener, which you can ignore. It's empty when
1704 /// nothing was added (the page is gone or `options.signal` is already aborted).
1705 ///
1706 /// @note With `once`, the listener is removed after the first event that reaches this
1707 /// element, even if nothing matched.
1708 ///
1709 template <typename F>
1710 EventListener On(std::string_view selector, std::string_view type, F&& callback,
1711 const AddEventListenerOptions& options = {}) const {
1712 using Fn = std::decay_t<F>;
1713 static_assert(detail::InvocableWithEvent<Fn, Element> || std::is_invocable_v<Fn&, Element>
1714 || std::is_invocable_v<Fn&>,
1715 "On takes a callable invocable with (dom::Event, dom::Element), "
1716 "(dom::Element), or ()");
1717 detail::CString sel(selector);
1718 detail::CString t(type);
1719 ULDOMDelegatedEventCallback thunk = &detail::DelegatedThunk<Fn>;
1720 ULUserDataDestroyCallback destroy = &detail::DeleteCallable<Fn>;
1721 return detail::AddListener<Fn>(std::forward<F>(callback), options,
1722 [&](unsigned flags, void* fn) {
1723 return ulDOMElementAddDelegatedEventListener(
1724 raw(), sel.c_str(), t.c_str(), flags, thunk, fn,
1725 destroy);
1726 });
1727 }
1728
1729 ///
1730 /// Listen for an event on matching elements, with options as flags (see the options overload).
1731 ///
1732 /// @param selector A CSS selector (eg, `tr.item`).
1733 ///
1734 /// @param type The event type to listen for.
1735 ///
1736 /// @param callback The callable to run for each match, taking (dom::Event, dom::Element),
1737 /// (dom::Element), or ().
1738 ///
1739 /// @param flags The listener's options (see EventListenerFlags).
1740 ///
1741 /// @return Returns a handle for removing the listener, which you can ignore (empty when the
1742 /// page is gone).
1743 ///
1744 template <typename F>
1745 EventListener On(std::string_view selector, std::string_view type, F&& callback,
1746 EventListenerFlags flags) const {
1747 return On(selector, type, std::forward<F>(callback), detail::ToOptions(flags));
1748 }
1749
1750 ///
1751 /// Same as On(), but returns a Result with the reason for a failure (eg, a SyntaxError for a
1752 /// malformed selector).
1753 ///
1754 /// @param selector A CSS selector (eg, `tr.item`).
1755 ///
1756 /// @param type The event type to listen for.
1757 ///
1758 /// @param callback The callable to run for each match, taking (dom::Event, dom::Element),
1759 /// (dom::Element), or ().
1760 ///
1761 /// @param options The listener's options (see AddEventListenerOptions).
1762 ///
1763 /// @return Returns a handle for removing the listener (empty when `options.signal` is
1764 /// already aborted). Fails with a SyntaxError if `selector` is malformed.
1765 ///
1766 template <typename F>
1767 [[nodiscard]] Result<EventListener> On(std::string_view selector, std::string_view type,
1768 F&& callback, const AddEventListenerOptions& options,
1769 Checked_t) const {
1770 if (IsEmpty())
1771 return detail::EmptyHandleError();
1772 return detail::CheckedDelegation(On(selector, type, std::forward<F>(callback), options),
1773 IsAlive(), options, selector);
1774 }
1775
1776 ///
1777 /// Same as On() with default options, but returns a Result with the reason for a failure.
1778 ///
1779 /// @param selector A CSS selector (eg, `tr.item`).
1780 ///
1781 /// @param type The event type to listen for.
1782 ///
1783 /// @param callback The callable to run for each match, taking (dom::Event, dom::Element),
1784 /// (dom::Element), or ().
1785 ///
1786 /// @return Returns a handle for removing the listener. Fails with a SyntaxError if
1787 /// `selector` is malformed.
1788 ///
1789 template <typename F>
1790 [[nodiscard]] Result<EventListener> On(std::string_view selector, std::string_view type,
1791 F&& callback, Checked_t) const {
1792 return On(selector, type, std::forward<F>(callback), AddEventListenerOptions {}, Checked);
1793 }
1794
1795 ///
1796 /// Listen for an event on matching elements by calling a member function on an object you keep
1797 /// alive (see the callable overload).
1798 ///
1799 /// @param selector A CSS selector (eg, `tr.item`).
1800 ///
1801 /// @param type The event type to listen for.
1802 ///
1803 /// @param receiver The object to call `method` on.
1804 ///
1805 /// @param method The member function to call, taking (dom::Event, dom::Element),
1806 /// (dom::Element), or ().
1807 ///
1808 /// @param options The listener's options (see AddEventListenerOptions).
1809 ///
1810 /// @return Returns a handle for removing the listener, which you can ignore. It's empty when
1811 /// nothing was added (the page is gone or `options.signal` is already aborted).
1812 ///
1813 /// @warning The listener calls `receiver` for as long as it lives, so `receiver` must outlive
1814 /// the page, or the listener must be added with the signal of an AbortController
1815 /// that `receiver` owns. The smart-pointer overload with a std::weak_ptr skips calls
1816 /// once the object is gone instead.
1817 ///
1818 template <typename C, typename M>
1819 requires(std::is_class_v<C> && std::is_member_function_pointer_v<M>
1821 EventListener On(std::string_view selector, std::string_view type, C& receiver, M method,
1822 const AddEventListenerOptions& options = {}) const {
1823 return On(selector, type, detail::WrapBorrowedMember(receiver, method), options);
1824 }
1825
1826 ///
1827 /// Listen for an event on matching elements by calling a member function on an object you keep
1828 /// alive, with options as flags.
1829 ///
1830 /// @param selector A CSS selector (eg, `tr.item`).
1831 ///
1832 /// @param type The event type to listen for.
1833 ///
1834 /// @param receiver The object to call `method` on.
1835 ///
1836 /// @param method The member function to call, taking (dom::Event, dom::Element),
1837 /// (dom::Element), or ().
1838 ///
1839 /// @param flags The listener's options (see EventListenerFlags).
1840 ///
1841 /// @return Returns a handle for removing the listener, which you can ignore (empty when the
1842 /// page is gone).
1843 ///
1844 /// @warning The listener calls `receiver` for as long as it lives, so `receiver` must outlive
1845 /// the page. To tie the listener to `receiver` instead, use the options overload
1846 /// with the signal of an AbortController that `receiver` owns.
1847 ///
1848 template <typename C, typename M>
1849 requires(std::is_class_v<C> && std::is_member_function_pointer_v<M>
1850 && !LockableHolder<C>)
1851 EventListener On(std::string_view selector, std::string_view type, C& receiver, M method,
1852 EventListenerFlags flags) const {
1853 return On(selector, type, detail::WrapBorrowedMember(receiver, method), flags);
1854 }
1855
1856 ///
1857 /// Listen for an event on matching elements by calling a member function through a smart
1858 /// pointer that's checked before each call (see the callable overload).
1859 ///
1860 /// A std::shared_ptr keeps the object alive for as long as the listener exists. A std::weak_ptr
1861 /// doesn't, and events are skipped once the object is gone.
1862 ///
1863 /// @param selector A CSS selector (eg, `tr.item`).
1864 ///
1865 /// @param type The event type to listen for.
1866 ///
1867 /// @param holder A std::shared_ptr, a std::weak_ptr, or another smart pointer with a
1868 /// HolderTraits specialization (see LockableHolder).
1869 ///
1870 /// @param method The member function to call, taking (dom::Event, dom::Element),
1871 /// (dom::Element), or ().
1872 ///
1873 /// @param options The listener's options (see AddEventListenerOptions).
1874 ///
1875 /// @return Returns a handle for removing the listener, which you can ignore. It's empty when
1876 /// nothing was added (the page is gone or `options.signal` is already aborted).
1877 ///
1878 template <typename H, typename M>
1879 requires(LockableHolder<H> && std::is_member_function_pointer_v<M>)
1880 EventListener On(std::string_view selector, std::string_view type, H holder, M method,
1881 const AddEventListenerOptions& options = {}) const {
1882 return On(selector, type, detail::WrapHolderMember(std::move(holder), method), options);
1883 }
1884
1885 ///
1886 /// Listen for an event on matching elements by calling a member function through a smart
1887 /// pointer, with options as flags.
1888 ///
1889 /// @param selector A CSS selector (eg, `tr.item`).
1890 ///
1891 /// @param type The event type to listen for.
1892 ///
1893 /// @param holder A std::shared_ptr, a std::weak_ptr, or another smart pointer with a
1894 /// HolderTraits specialization (see LockableHolder).
1895 ///
1896 /// @param method The member function to call, taking (dom::Event, dom::Element),
1897 /// (dom::Element), or ().
1898 ///
1899 /// @param flags The listener's options (see EventListenerFlags).
1900 ///
1901 /// @return Returns a handle for removing the listener, which you can ignore (empty when the
1902 /// page is gone).
1903 ///
1904 template <typename H, typename M>
1905 requires(LockableHolder<H> && std::is_member_function_pointer_v<M>)
1906 EventListener On(std::string_view selector, std::string_view type, H holder, M method,
1907 EventListenerFlags flags) const {
1908 return On(selector, type, detail::WrapHolderMember(std::move(holder), method), flags);
1909 }
1910
1911 // --- Typed element views --------------------------------------------------------------------
1912
1913 ///
1914 /// Get this element as an `<input>` (HTMLInputElement).
1915 ///
1916 /// @return Returns the element as an HTMLInputElement (empty if it isn't an `<input>`), so
1917 /// `if (auto input = el.AsInput())` checks and converts in one step.
1918 ///
1919 /// @note In an XHTML document, this and the other As*() functions always return an empty view.
1920 ///
1921 HTMLInputElement AsInput() const;
1922
1923 ///
1924 /// Get this element as a `<textarea>` (HTMLTextAreaElement).
1925 ///
1926 /// @return Returns the element as an HTMLTextAreaElement (empty if it isn't a `<textarea>`).
1927 ///
1929
1930 ///
1931 /// Get this element as a `<select>` (HTMLSelectElement).
1932 ///
1933 /// @return Returns the element as an HTMLSelectElement (empty if it isn't a `<select>`).
1934 ///
1936
1937 ///
1938 /// Get this element as an `<option>` (HTMLOptionElement).
1939 ///
1940 /// @return Returns the element as an HTMLOptionElement (empty if it isn't an `<option>`).
1941 ///
1943
1944 ///
1945 /// Get this element as a `<form>` (HTMLFormElement).
1946 ///
1947 /// @return Returns the element as an HTMLFormElement (empty if it isn't a `<form>`).
1948 ///
1949 HTMLFormElement AsForm() const;
1950
1951 ///
1952 /// Get this element as an `<iframe>` (HTMLIFrameElement).
1953 ///
1954 /// @return Returns the element as an HTMLIFrameElement (empty if it isn't an `<iframe>` or a
1955 /// legacy `<frame>`).
1956 ///
1958
1959 ///
1960 /// Get this element as an `<img>` (HTMLImageElement).
1961 ///
1962 /// @return Returns the element as an HTMLImageElement (empty if it isn't an `<img>`).
1963 ///
1964 HTMLImageElement AsImage() const;
1965
1966 ///
1967 /// Get this element as an `<a>` (HTMLAnchorElement).
1968 ///
1969 /// @return Returns the element as an HTMLAnchorElement (empty if it isn't an `<a>`).
1970 ///
1972
1973 // --- Interop with the C API (most embedders never touch raw handles) -----------------------
1974 //
1975 // These deliberately hide Node's same-named members with element-typed versions: the two
1976 // handle families name one object underneath, so the narrower type is the only
1977 // difference (the ULDOMNode/ULDOMElement casts below are exact by construction).
1978
1979 ///
1980 /// Wrap a C handle you own, taking ownership of it.
1981 ///
1982 /// @param handle A handle from the C API that you would otherwise destroy with
1983 /// ulDestroyDOMElement() (NULL gives an empty Element).
1984 ///
1985 /// @return Returns an Element that destroys `handle` when it's done.
1986 ///
1987 static Element Adopt(ULDOMElement handle) { return Element(handle); }
1988
1989 ///
1990 /// Wrap a C handle the library owns (eg, a callback argument), adding a reference.
1991 ///
1992 /// @param handle The borrowed handle (NULL gives an empty Element).
1993 ///
1994 /// @return Returns an Element with its own reference, so you can keep it after the callback.
1995 ///
1996 static Element FromBorrowed(ULDOMElement handle) {
1997 return Element(handle ? ulCreateDOMElementRef(handle) : nullptr);
1998 }
1999
2000 ///
2001 /// Get the C handle, for passing to the `<Ultralight/CAPI/CAPI_DOMElement.h>` functions.
2002 ///
2003 /// @return Returns the handle (NULL for an empty Element). This Element still owns it, so don't
2004 /// destroy it.
2005 ///
2006 ULDOMElement raw() const { return reinterpret_cast<ULDOMElement>(detail_.handle); }
2007
2008 ///
2009 /// Give up ownership of the C handle and return it. This Element becomes empty.
2010 ///
2011 /// @return Returns the handle. You must call ulDestroyDOMElement() when finished.
2012 ///
2013 ULDOMElement LeakRef() {
2014 return reinterpret_cast<ULDOMElement>(Node::LeakRef());
2015 }
2016
2017 protected:
2018 explicit Element(ULDOMElement handle)
2019 : Node(reinterpret_cast<ULDOMNode>(handle)) {}
2020
2021 ///
2022 /// Whether or not this element's tag name is `upper_tag`.
2023 ///
2024 /// @param upper_tag The tag name to compare with, in uppercase (eg, `INPUT`).
2025 ///
2026 bool TagIs(const char* upper_tag) const {
2027 return raw() && tagName() == upper_tag;
2028 }
2029};
2030
2031// The property proxies rely on this layout: the storage lives in the Node base, the style
2032// pack is the first member Element declares, and the types are non-aggregate (heap
2033// aggregate-initialization of the collapsed pack crashes some older compilers). Element is
2034// not standard-layout (its data spans Node and Element), so offsetof on it is
2035// conditionally-supported; clang defines it for this single-inheritance shape, exactly as
2036// the typed element views below already rely on for their own members. Node itself stays
2037// standard-layout (asserted in Node.h).
2038static_assert(std::is_standard_layout_v<detail::StyleProxy>,
2039 "StyleProxy must stay standard-layout");
2040static_assert(!std::is_aggregate_v<Element>, "Element must not be an aggregate");
2041static_assert(!std::is_aggregate_v<detail::StyleProxy>,
2042 "StyleProxy must not be an aggregate");
2043#if defined(__GNUC__)
2044#pragma GCC diagnostic push
2045#pragma GCC diagnostic ignored "-Winvalid-offsetof"
2046#endif
2047static_assert(offsetof(Element, style) == sizeof(Node),
2048 "the style proxy must stay the first member Element declares (directly "
2049 "after the Node base subobject)");
2050#if defined(__GNUC__)
2051#pragma GCC diagnostic pop
2052#endif
2053
2054///
2055/// The direction of a text control's selection (selectionDirection).
2056///
2057enum class SelectionDirection : uint8_t {
2058 None, ///< No direction.
2059 Forward, ///< The selection was made toward the end of the text.
2060 Backward, ///< The selection was made toward the start of the text.
2061};
2062
2063///
2064/// Where the selection goes after HTMLInputElement::setRangeText() replaces text (selectMode).
2065///
2066enum class SelectionMode : uint8_t {
2067 Preserve, ///< Keep the selection, adjusted for the edit.
2068 Select, ///< Select the inserted text.
2069 Start, ///< Put the caret before the inserted text.
2070 End, ///< Put the caret after the inserted text.
2071};
2072
2073///
2074/// The ways a form control fails its constraints (ValidityState).
2075///
2076/// Get one from HTMLInputElement::validity() (or the textarea and select versions). It's a
2077/// snapshot, so unlike the web's live ValidityState it doesn't change when the control does.
2078///
2079/// ```
2080/// dom::ValidityState v = input.validity();
2081/// if (v.valueMissing)
2082/// input.setCustomValidity("Enter a name");
2083/// ```
2084///
2086 bool valueMissing = false; ///< A `required` control has no value.
2087 bool typeMismatch = false; ///< The value doesn't fit the input type (eg, a bad email).
2088 bool patternMismatch = false; ///< The value doesn't match the `pattern` attribute.
2089 bool tooLong = false; ///< The user made the value longer than `maxlength`.
2090 bool tooShort = false; ///< The user made the value shorter than `minlength`.
2091 bool rangeUnderflow = false; ///< The value is less than `min`.
2092 bool rangeOverflow = false; ///< The value is greater than `max`.
2093 bool stepMismatch = false; ///< The value doesn't fit the `step` attribute.
2094 bool badInput = false; ///< The user entered something the control can't convert.
2095 bool customError = false; ///< A custom error message is set.
2096 bool valid = false; ///< None of the above are true.
2097};
2098
2099/// \cond INTERNAL
2100namespace detail {
2101
2102inline ValidityState ReadValidity(ULDOMElement element) {
2103 ULDOMValidityState state;
2104 ulDOMElementGetValidity(element, &state);
2105 return ValidityState { state.value_missing, state.type_mismatch, state.pattern_mismatch,
2106 state.too_long, state.too_short, state.range_underflow,
2107 state.range_overflow, state.step_mismatch, state.bad_input,
2108 state.custom_error, state.valid };
2109}
2110
2111inline const char* SelectionDirectionName(SelectionDirection direction) {
2112 switch (direction) {
2113 case SelectionDirection::Forward:
2114 return "forward";
2115 case SelectionDirection::Backward:
2116 return "backward";
2117 default:
2118 return "none";
2119 }
2120}
2121
2122inline SelectionDirection SelectionDirectionTag::Get(ULDOMElement h) {
2123 std::string name = TakeString(ulDOMElementGetSelectionDirection(h));
2124 if (name == "forward")
2125 return SelectionDirection::Forward;
2126 if (name == "backward")
2127 return SelectionDirection::Backward;
2128 return SelectionDirection::None;
2129}
2130
2131inline void SelectionDirectionTag::Set(ULDOMElement h, SelectionDirection v) {
2132 ulDOMElementSetSelectionDirection(h, SelectionDirectionName(v), nullptr);
2133}
2134
2135inline ULDOMSelectionMode SelectionModeValue(SelectionMode mode) {
2136 switch (mode) {
2137 case SelectionMode::Select:
2138 return kULDOMSelectionMode_Select;
2139 case SelectionMode::Start:
2140 return kULDOMSelectionMode_Start;
2141 case SelectionMode::End:
2142 return kULDOMSelectionMode_End;
2143 default:
2144 return kULDOMSelectionMode_Preserve;
2145 }
2146}
2147
2148} // namespace detail
2149/// \endcond
2150
2151///
2152/// An `<input>` element (HTMLInputElement).
2153///
2154/// Get one with Element::AsInput(). It adds the input's own attributes (eg, `type` and
2155/// `placeholder`), form validation, the text selection API, and SetValue(), which fires events
2156/// like a user edit.
2157///
2158/// ## Validation
2159///
2160/// Mark the constraints in the page (eg, `%required` or `pattern`) or set them from native code,
2161/// then check them:
2162///
2163/// ```
2164/// dom::HTMLInputElement name_input = document.getElementById("name").AsInput();
2165/// name_input.required = true;
2166/// if (!name_input.checkValidity())
2167/// Log(name_input.validationMessage());
2168/// ```
2169///
2170/// ## Text Selection
2171///
2172/// The selection members and methods work on the input types that support selection (`text`,
2173/// `search`, `tel`, `url`, and `password`), like the web. Offsets count UTF-16 code units. On
2174/// any other type (eg, `email` or `number`):
2175///
2176/// - **The offsets read std::nullopt and the direction reads SelectionDirection::None** (the
2177/// web reads null).
2178/// - **Assigning to the selection members does nothing.**
2179/// - **The methods fail** with an InvalidStateError.
2180///
2181/// select() is the exception. It works on every text field, `email` and `number` included.
2182///
2183/// A change to the selection fires a `%select` event during a later Renderer::Update(), not
2184/// during the call.
2185///
2186/// @note Changing the selection of a displayed input that doesn't have focus also gives it
2187/// keyboard focus (when the View has focus).
2188///
2190 public:
2191 // The members shared with HTMLTextAreaElement and HTMLSelectElement come first, in the same
2192 // order in each view (one offset per property; see PropertiesImpl.h).
2193
2194 ///
2195 /// Whether or not the input has a `required` attribute (required). Assign to add or remove it.
2196 ///
2197 /// A required input with an empty value fails checkValidity().
2198 ///
2199 detail::BoolProp<detail::RequiredTag> required;
2200
2201 ///
2202 /// The input's `placeholder` attribute (placeholder), the hint it shows while it's empty.
2203 /// Reads as an empty string if there's none.
2204 ///
2205 detail::StringProp<detail::PlaceholderTag> placeholder;
2206
2207 ///
2208 /// Whether or not the input has a `readonly` attribute (readOnly). Assign to add or remove it.
2209 ///
2210 /// The user can't edit a read-only input, and checkValidity() skips it.
2211 ///
2212 detail::BoolProp<detail::ReadOnlyTag> readOnly;
2213
2214 ///
2215 /// The input's default value (defaultValue), which is its `value` attribute.
2216 ///
2217 /// The input goes back to this value when its form is reset. Assigning also changes the
2218 /// current value, unless the user has edited it or you've assigned `value`.
2219 ///
2220 detail::StringProp<detail::DefaultValueTag> defaultValue;
2221
2222 ///
2223 /// The offset where the selection starts (selectionStart), as a `std::optional<size_t>`.
2224 /// Assign to move it.
2225 ///
2226 /// It reads std::nullopt on an input type without selection (see "Text Selection" above).
2227 /// Values past the end of the text are clamped to it.
2228 ///
2229 /// ```
2230 /// std::optional<size_t> start = input.selectionStart;
2231 /// input.selectionStart = 0;
2232 /// ```
2233 ///
2234 detail::ValueProp<detail::SelectionStartTag> selectionStart;
2235
2236 ///
2237 /// The offset just past the end of the selection (selectionEnd). Works like selectionStart.
2238 ///
2239 detail::ValueProp<detail::SelectionEndTag> selectionEnd;
2240
2241 ///
2242 /// The direction of the selection (selectionDirection), as a SelectionDirection. Assign to
2243 /// change it.
2244 ///
2245 /// @note By default a selection always has a direction, so SelectionDirection::None reads back
2246 /// as SelectionDirection::Forward (see ViewConfig::match_native_editing_behavior).
2247 ///
2248 detail::ValueProp<detail::SelectionDirectionTag> selectionDirection;
2249
2250 ///
2251 /// The input's type (type), such as `text`, `email`, or `checkbox`.
2252 ///
2253 /// Reads are lowercase, and an input whose `type` attribute is missing or unknown reads `text`,
2254 /// like the web. Assign to set the `type` attribute.
2255 ///
2256 detail::StringProp<detail::InputTypeTag> type;
2257
2258 ///
2259 /// Create an empty HTMLInputElement.
2260 ///
2262
2263 ///
2264 /// Copy constructor (both handles refer to the same element).
2265 ///
2266 /// @param other The HTMLInputElement to copy.
2267 ///
2269
2270 ///
2271 /// Move constructor (`other` becomes empty).
2272 ///
2273 /// @param other The HTMLInputElement to move from.
2274 ///
2275 HTMLInputElement(HTMLInputElement&& other) noexcept : Element(std::move(other)) {}
2276
2277 ///
2278 /// Assignment (copies or moves).
2279 ///
2280 /// @param other The HTMLInputElement to assign from.
2281 ///
2282 /// @return Returns this HTMLInputElement.
2283 ///
2285 Element::operator=(std::move(other));
2286 return *this;
2287 }
2288
2289 ///
2290 /// Set the input's value and fire `input` and `change` events, like a user edit.
2291 ///
2292 /// Both events fire before this returns, and only if the value changed. Checkbox, radio,
2293 /// hidden, and button-type inputs just take the new value (no events fire).
2294 ///
2295 /// Assigning the inherited `value` member sets the value without events.
2296 ///
2297 /// @param new_value The value to set.
2298 ///
2299 /// @note A non-empty value on a file input is ignored.
2300 ///
2301 void SetValue(std::string_view new_value) const {
2302 ulDOMElementSetValue(raw(), new_value.data() ? new_value.data() : "", new_value.size(), true,
2303 nullptr);
2304 }
2305
2306 ///
2307 /// Same as SetValue(), but returns a Result with the reason for a failure (eg, an
2308 /// InvalidStateError for a non-empty value on a file input).
2309 ///
2310 /// @return Returns success. Fails with an InvalidStateError for a non-empty value on a file
2311 /// input.
2312 ///
2313 [[nodiscard]] Result<void> SetValue(std::string_view new_value, Checked_t) const {
2314 if (IsEmpty())
2315 return detail::EmptyHandleError();
2316 detail::ErrorScope error;
2317 if (ulDOMElementSetValue(raw(), new_value.data() ? new_value.data() : "",
2318 new_value.size(), true, error.out()))
2319 return {};
2320 return Unexpected<Error>(error.TakeError());
2321 }
2322
2323 ///
2324 /// Get the form the input belongs to (form).
2325 ///
2326 /// @return Returns the input's form (empty if it isn't in one).
2327 ///
2328 HTMLFormElement form() const;
2329
2330 ///
2331 /// Check whether the input meets its constraints (checkValidity).
2332 ///
2333 /// Constraints come from attributes like `required`, `pattern`, and `maxlength`, from the
2334 /// input's type (eg, `email`), and from setCustomValidity(). An invalid input gets a
2335 /// cancelable `invalid` event, which doesn't bubble.
2336 ///
2337 /// @return Returns true if the input is valid. An input excluded from validation (eg, a
2338 /// disabled input or a read-only text field) is always valid.
2339 ///
2340 bool checkValidity() const { return ulDOMElementCheckValidity(raw()); }
2341
2342 ///
2343 /// Check whether the input meets its constraints, and report a failure to the user
2344 /// (reportValidity).
2345 ///
2346 /// This works like checkValidity(). If the input is invalid and no listener canceled its
2347 /// `invalid` event, the input also gets keyboard focus (and scrolls into view).
2348 ///
2349 /// @return Returns true if the input is valid.
2350 ///
2351 /// @note The library doesn't display a validation message. To show one, read
2352 /// validationMessage() in an `invalid` listener and display it yourself.
2353 ///
2354 bool reportValidity() const { return ulDOMElementReportValidity(raw()); }
2355
2356 ///
2357 /// Set a custom validation error (setCustomValidity).
2358 ///
2359 /// While the message isn't empty, the input is invalid and validationMessage() returns it.
2360 ///
2361 /// @param error The error message. Pass an empty string to clear the error.
2362 ///
2363 void setCustomValidity(std::string_view error) const {
2364 ulDOMElementSetCustomValidity(raw(), error.data() ? error.data() : "", error.size());
2365 }
2366
2367 ///
2368 /// Get the message that describes why the input is invalid (validationMessage).
2369 ///
2370 /// @return Returns the message (eg, the one passed to setCustomValidity(), or the library's
2371 /// message for a missing required value), or an empty string if the input is valid or
2372 /// excluded from validation.
2373 ///
2374 std::string validationMessage() const {
2375 return detail::TakeString(ulDOMElementGetValidationMessage(raw()));
2376 }
2377
2378 ///
2379 /// Get how the input fails its constraints (validity).
2380 ///
2381 /// @return Returns a snapshot of the validity flags (all false if this HTMLInputElement is
2382 /// empty or its page is gone). Call this again after the value or the constraints
2383 /// change.
2384 ///
2385 ValidityState validity() const { return detail::ReadValidity(raw()); }
2386
2387 ///
2388 /// Select all of the input's text (select).
2389 ///
2390 /// This works on every text field (`email` and `number` too) and does nothing on other input
2391 /// types.
2392 ///
2393 void select() const { ulDOMElementSelect(raw()); }
2394
2395 ///
2396 /// Set the selection (setSelectionRange).
2397 ///
2398 /// Offsets past the end of the text are clamped to it.
2399 ///
2400 /// @param start The offset where the selection starts.
2401 ///
2402 /// @param end The offset just past the end of the selection.
2403 ///
2404 /// @param direction The direction of the selection.
2405 ///
2406 /// @note This does nothing if the input type doesn't support selection.
2407 ///
2408 void setSelectionRange(size_t start, size_t end,
2409 SelectionDirection direction = SelectionDirection::None) const {
2410 ulDOMElementSetSelectionRange(raw(), detail::IndexArgument(start), detail::IndexArgument(end),
2411 detail::SelectionDirectionName(direction), nullptr);
2412 }
2413
2414 ///
2415 /// Same as setSelectionRange(), but returns a Result with the reason for a failure (eg, an
2416 /// InvalidStateError if the input type doesn't support selection).
2417 ///
2418 /// @return Returns success. Fails with an InvalidStateError if the input type doesn't support
2419 /// selection.
2420 ///
2421 [[nodiscard]] Result<void> setSelectionRange(size_t start, size_t end, Checked_t) const {
2423 }
2424
2425 ///
2426 /// Same as setSelectionRange() with a `direction`, but returns a Result with the reason for a
2427 /// failure.
2428 ///
2429 /// @return Returns success. Fails with an InvalidStateError if the input type doesn't support
2430 /// selection.
2431 ///
2432 [[nodiscard]] Result<void> setSelectionRange(size_t start, size_t end,
2433 SelectionDirection direction, Checked_t) const {
2434 if (IsEmpty())
2435 return detail::EmptyHandleError();
2436 detail::ErrorScope error;
2437 if (ulDOMElementSetSelectionRange(raw(), detail::IndexArgument(start),
2438 detail::IndexArgument(end),
2439 detail::SelectionDirectionName(direction), error.out()))
2440 return {};
2441 return Unexpected<Error>(error.TakeError());
2442 }
2443
2444 ///
2445 /// Replace the selected text (setRangeText).
2446 ///
2447 /// This fires no `input` or `change` events, like the web.
2448 ///
2449 /// @param replacement The text to put in place of the selection.
2450 ///
2451 /// @note This does nothing if the input type doesn't support selection.
2452 ///
2453 void setRangeText(std::string_view replacement) const {
2454 ulDOMElementSetRangeText(raw(), replacement.data() ? replacement.data() : "",
2455 replacement.size(), nullptr);
2456 }
2457
2458 ///
2459 /// Same as setRangeText(), but returns a Result with the reason for a failure (eg, an
2460 /// InvalidStateError if the input type doesn't support selection).
2461 ///
2462 /// @return Returns success. Fails with an InvalidStateError if the input type doesn't support
2463 /// selection.
2464 ///
2465 [[nodiscard]] Result<void> setRangeText(std::string_view replacement, Checked_t) const {
2466 if (IsEmpty())
2467 return detail::EmptyHandleError();
2468 detail::ErrorScope error;
2469 if (ulDOMElementSetRangeText(raw(), replacement.data() ? replacement.data() : "",
2470 replacement.size(), error.out()))
2471 return {};
2472 return Unexpected<Error>(error.TakeError());
2473 }
2474
2475 ///
2476 /// Replace a range of the text (setRangeText with a range).
2477 ///
2478 /// Offsets past the end of the text are clamped to it. This fires no `input` or `change`
2479 /// events, like the web.
2480 ///
2481 /// @param replacement The text to put in place of the range.
2482 ///
2483 /// @param start The offset where the range starts.
2484 ///
2485 /// @param end The offset just past the end of the range.
2486 ///
2487 /// @param mode Where the selection goes afterward.
2488 ///
2489 /// @note This does nothing if `start` is greater than `end` or the input type doesn't support
2490 /// selection.
2491 ///
2492 void setRangeText(std::string_view replacement, size_t start, size_t end,
2494 ulDOMElementSetRangeTextInRange(raw(), replacement.data() ? replacement.data() : "",
2495 replacement.size(), detail::IndexArgument(start),
2496 detail::IndexArgument(end), detail::SelectionModeValue(mode),
2497 nullptr);
2498 }
2499
2500 ///
2501 /// Same as setRangeText() with a range, but returns a Result with the reason for a failure (eg,
2502 /// an IndexSizeError if `start` is greater than `end`).
2503 ///
2504 /// @return Returns success. Fails with an IndexSizeError if `start` is greater than `end`, or an
2505 /// InvalidStateError if the input type doesn't support selection.
2506 ///
2507 [[nodiscard]] Result<void> setRangeText(std::string_view replacement, size_t start,
2508 size_t end, Checked_t) const {
2509 return setRangeText(replacement, start, end, SelectionMode::Preserve, Checked);
2510 }
2511
2512 ///
2513 /// Same as setRangeText() with a range and a `mode`, but returns a Result with the reason for a
2514 /// failure.
2515 ///
2516 /// @return Returns success. Fails with an IndexSizeError if `start` is greater than `end`, or an
2517 /// InvalidStateError if the input type doesn't support selection.
2518 ///
2519 [[nodiscard]] Result<void> setRangeText(std::string_view replacement, size_t start,
2520 size_t end, SelectionMode mode, Checked_t) const {
2521 if (IsEmpty())
2522 return detail::EmptyHandleError();
2523 detail::ErrorScope error;
2524 if (ulDOMElementSetRangeTextInRange(raw(), replacement.data() ? replacement.data() : "",
2525 replacement.size(), detail::IndexArgument(start),
2526 detail::IndexArgument(end),
2527 detail::SelectionModeValue(mode), error.out()))
2528 return {};
2529 return Unexpected<Error>(error.TakeError());
2530 }
2531
2532 private:
2533 friend class Element;
2534 explicit HTMLInputElement(Element base) : Element(std::move(base)) {}
2535};
2536
2537///
2538/// A `<textarea>` element (HTMLTextAreaElement).
2539///
2540/// Get one with Element::AsTextArea(). It adds the textarea's attributes, form validation, and
2541/// the text selection API. These work like HTMLInputElement's, except that a textarea always
2542/// supports selection.
2543///
2545 public:
2546 // Same order as the shared members of HTMLInputElement (one offset per property).
2547
2548 ///
2549 /// Whether or not the textarea has a `required` attribute (required). Works like
2550 /// HTMLInputElement::required.
2551 ///
2552 detail::BoolProp<detail::RequiredTag> required;
2553
2554 ///
2555 /// The textarea's `placeholder` attribute (placeholder). Works like
2556 /// HTMLInputElement::placeholder.
2557 ///
2558 detail::StringProp<detail::PlaceholderTag> placeholder;
2559
2560 ///
2561 /// Whether or not the textarea has a `readonly` attribute (readOnly). Works like
2562 /// HTMLInputElement::readOnly.
2563 ///
2564 detail::BoolProp<detail::ReadOnlyTag> readOnly;
2565
2566 ///
2567 /// The textarea's default value (defaultValue), which is the text between its tags.
2568 ///
2569 /// The textarea goes back to this value when its form is reset. Assigning replaces the text
2570 /// between the tags, and also changes the current value unless the user has edited it or
2571 /// you've assigned `value`.
2572 ///
2573 detail::StringProp<detail::DefaultValueTag> defaultValue;
2574
2575 ///
2576 /// The offset where the selection starts (selectionStart). Works like
2577 /// HTMLInputElement::selectionStart.
2578 ///
2579 detail::ValueProp<detail::SelectionStartTag> selectionStart;
2580
2581 ///
2582 /// The offset just past the end of the selection (selectionEnd). Works like selectionStart.
2583 ///
2584 detail::ValueProp<detail::SelectionEndTag> selectionEnd;
2585
2586 ///
2587 /// The direction of the selection (selectionDirection). Works like
2588 /// HTMLInputElement::selectionDirection.
2589 ///
2590 detail::ValueProp<detail::SelectionDirectionTag> selectionDirection;
2591
2592 ///
2593 /// Create an empty HTMLTextAreaElement.
2594 ///
2596
2597 ///
2598 /// Copy constructor (both handles refer to the same element).
2599 ///
2600 /// @param other The HTMLTextAreaElement to copy.
2601 ///
2603
2604 ///
2605 /// Move constructor (`other` becomes empty).
2606 ///
2607 /// @param other The HTMLTextAreaElement to move from.
2608 ///
2609 HTMLTextAreaElement(HTMLTextAreaElement&& other) noexcept : Element(std::move(other)) {}
2610
2611 ///
2612 /// Assignment (copies or moves).
2613 ///
2614 /// @param other The HTMLTextAreaElement to assign from.
2615 ///
2616 /// @return Returns this HTMLTextAreaElement.
2617 ///
2619 Element::operator=(std::move(other));
2620 return *this;
2621 }
2622
2623 ///
2624 /// Set the textarea's value and fire `input` and `change` events, like a user edit.
2625 ///
2626 /// Both events fire before this returns, and only if the value changed. Assigning the inherited
2627 /// `value` member sets the value without events.
2628 ///
2629 /// @param new_value The value to set.
2630 ///
2631 void SetValue(std::string_view new_value) const {
2632 ulDOMElementSetValue(raw(), new_value.data() ? new_value.data() : "", new_value.size(), true,
2633 nullptr);
2634 }
2635
2636 ///
2637 /// Same as SetValue(), but returns a Result with the reason for a failure.
2638 ///
2639 /// @return Returns success (it fails only when this HTMLTextAreaElement is empty or its page
2640 /// is gone).
2641 ///
2642 [[nodiscard]] Result<void> SetValue(std::string_view new_value, Checked_t) const {
2643 if (IsEmpty())
2644 return detail::EmptyHandleError();
2645 detail::ErrorScope error;
2646 if (ulDOMElementSetValue(raw(), new_value.data() ? new_value.data() : "",
2647 new_value.size(), true, error.out()))
2648 return {};
2649 return Unexpected<Error>(error.TakeError());
2650 }
2651
2652 ///
2653 /// Get the form the textarea belongs to (form).
2654 ///
2655 /// @return Returns the textarea's form (empty if it isn't in one).
2656 ///
2657 HTMLFormElement form() const;
2658
2659 ///
2660 /// Check whether the textarea meets its constraints (checkValidity). Works like
2661 /// HTMLInputElement::checkValidity().
2662 ///
2663 /// @return Returns true if the textarea is valid.
2664 ///
2665 bool checkValidity() const { return ulDOMElementCheckValidity(raw()); }
2666
2667 ///
2668 /// Check whether the textarea meets its constraints, and report a failure to the user
2669 /// (reportValidity). Works like HTMLInputElement::reportValidity().
2670 ///
2671 /// @return Returns true if the textarea is valid.
2672 ///
2673 bool reportValidity() const { return ulDOMElementReportValidity(raw()); }
2674
2675 ///
2676 /// Set a custom validation error (setCustomValidity). Works like
2677 /// HTMLInputElement::setCustomValidity().
2678 ///
2679 /// @param error The error message. Pass an empty string to clear the error.
2680 ///
2681 void setCustomValidity(std::string_view error) const {
2682 ulDOMElementSetCustomValidity(raw(), error.data() ? error.data() : "", error.size());
2683 }
2684
2685 ///
2686 /// Get the message that describes why the textarea is invalid (validationMessage).
2687 ///
2688 /// @return Returns the message, or an empty string if the textarea is valid or excluded from
2689 /// validation.
2690 ///
2691 std::string validationMessage() const {
2692 return detail::TakeString(ulDOMElementGetValidationMessage(raw()));
2693 }
2694
2695 ///
2696 /// Get how the textarea fails its constraints (validity). Works like
2697 /// HTMLInputElement::validity().
2698 ///
2699 /// @return Returns a snapshot of the validity flags.
2700 ///
2701 ValidityState validity() const { return detail::ReadValidity(raw()); }
2702
2703 ///
2704 /// Select all of the text (select).
2705 ///
2706 void select() const { ulDOMElementSelect(raw()); }
2707
2708 ///
2709 /// Set the selection (setSelectionRange).
2710 ///
2711 /// Offsets past the end of the text are clamped to it.
2712 ///
2713 /// @param start The offset where the selection starts.
2714 ///
2715 /// @param end The offset just past the end of the selection.
2716 ///
2717 /// @param direction The direction of the selection.
2718 ///
2719 void setSelectionRange(size_t start, size_t end,
2720 SelectionDirection direction = SelectionDirection::None) const {
2721 ulDOMElementSetSelectionRange(raw(), detail::IndexArgument(start), detail::IndexArgument(end),
2722 detail::SelectionDirectionName(direction), nullptr);
2723 }
2724
2725 ///
2726 /// Same as setSelectionRange(), but returns a Result with the reason for a failure.
2727 ///
2728 /// @return Returns success (it fails only when this element is empty or its page is gone).
2729 ///
2730 [[nodiscard]] Result<void> setSelectionRange(size_t start, size_t end, Checked_t) const {
2732 }
2733
2734 ///
2735 /// Same as setSelectionRange() with a `direction`, but returns a Result with the reason for a
2736 /// failure.
2737 ///
2738 /// @return Returns success (it fails only when this element is empty or its page is gone).
2739 ///
2740 [[nodiscard]] Result<void> setSelectionRange(size_t start, size_t end,
2741 SelectionDirection direction, Checked_t) const {
2742 if (IsEmpty())
2743 return detail::EmptyHandleError();
2744 detail::ErrorScope error;
2745 if (ulDOMElementSetSelectionRange(raw(), detail::IndexArgument(start),
2746 detail::IndexArgument(end),
2747 detail::SelectionDirectionName(direction), error.out()))
2748 return {};
2749 return Unexpected<Error>(error.TakeError());
2750 }
2751
2752 ///
2753 /// Replace the selected text (setRangeText).
2754 ///
2755 /// This fires no `input` or `change` events, like the web.
2756 ///
2757 /// @param replacement The text to put in place of the selection.
2758 ///
2759 void setRangeText(std::string_view replacement) const {
2760 ulDOMElementSetRangeText(raw(), replacement.data() ? replacement.data() : "",
2761 replacement.size(), nullptr);
2762 }
2763
2764 ///
2765 /// Same as setRangeText(), but returns a Result with the reason for a failure.
2766 ///
2767 /// @return Returns success (it fails only when this element is empty or its page is gone).
2768 ///
2769 [[nodiscard]] Result<void> setRangeText(std::string_view replacement, Checked_t) const {
2770 if (IsEmpty())
2771 return detail::EmptyHandleError();
2772 detail::ErrorScope error;
2773 if (ulDOMElementSetRangeText(raw(), replacement.data() ? replacement.data() : "",
2774 replacement.size(), error.out()))
2775 return {};
2776 return Unexpected<Error>(error.TakeError());
2777 }
2778
2779 ///
2780 /// Replace a range of the text (setRangeText with a range).
2781 ///
2782 /// Offsets past the end of the text are clamped to it. This fires no `input` or `change`
2783 /// events, like the web.
2784 ///
2785 /// @param replacement The text to put in place of the range.
2786 ///
2787 /// @param start The offset where the range starts.
2788 ///
2789 /// @param end The offset just past the end of the range.
2790 ///
2791 /// @param mode Where the selection goes afterward.
2792 ///
2793 /// @note This does nothing if `start` is greater than `end`.
2794 ///
2795 void setRangeText(std::string_view replacement, size_t start, size_t end,
2797 ulDOMElementSetRangeTextInRange(raw(), replacement.data() ? replacement.data() : "",
2798 replacement.size(), detail::IndexArgument(start),
2799 detail::IndexArgument(end), detail::SelectionModeValue(mode),
2800 nullptr);
2801 }
2802
2803 ///
2804 /// Same as setRangeText() with a range, but returns a Result with the reason for a failure (eg,
2805 /// an IndexSizeError if `start` is greater than `end`).
2806 ///
2807 /// @return Returns success. Fails with an IndexSizeError if `start` is greater than `end`.
2808 ///
2809 [[nodiscard]] Result<void> setRangeText(std::string_view replacement, size_t start,
2810 size_t end, Checked_t) const {
2811 return setRangeText(replacement, start, end, SelectionMode::Preserve, Checked);
2812 }
2813
2814 ///
2815 /// Same as setRangeText() with a range and a `mode`, but returns a Result with the reason for a
2816 /// failure.
2817 ///
2818 /// @return Returns success. Fails with an IndexSizeError if `start` is greater than `end`.
2819 ///
2820 [[nodiscard]] Result<void> setRangeText(std::string_view replacement, size_t start,
2821 size_t end, SelectionMode mode, Checked_t) const {
2822 if (IsEmpty())
2823 return detail::EmptyHandleError();
2824 detail::ErrorScope error;
2825 if (ulDOMElementSetRangeTextInRange(raw(), replacement.data() ? replacement.data() : "",
2826 replacement.size(), detail::IndexArgument(start),
2827 detail::IndexArgument(end),
2828 detail::SelectionModeValue(mode), error.out()))
2829 return {};
2830 return Unexpected<Error>(error.TakeError());
2831 }
2832
2833 private:
2834 friend class Element;
2835 explicit HTMLTextAreaElement(Element base) : Element(std::move(base)) {}
2836};
2837
2838///
2839/// A `<select>` element (HTMLSelectElement).
2840///
2841/// Get one with Element::AsSelect(). It adds ways to list the options, the select's attributes,
2842/// and form validation (which works like HTMLInputElement's).
2843///
2844/// Use the inherited `value` and `selectedIndex` members to read or change the selection.
2845/// Changing it that way fires no `change` event, like the web. SetValue() changes it like a user
2846/// would.
2847///
2848/// length() is read-only here (the web can also shorten or lengthen the list through it). Add and
2849/// remove `<option>` elements to change the list.
2850///
2852 public:
2853 // Same order as the shared members of HTMLInputElement (one offset per property).
2854
2855 ///
2856 /// Whether or not the select has a `required` attribute (required). Assign to add or remove
2857 /// it.
2858 ///
2859 /// A required select fails checkValidity() while no option is selected, or while the selected
2860 /// option is a placeholder (the first option, with an empty value, in a select that shows one
2861 /// option at a time).
2862 ///
2863 detail::BoolProp<detail::RequiredTag> required;
2864
2865 ///
2866 /// Create an empty HTMLSelectElement.
2867 ///
2869
2870 ///
2871 /// Copy constructor (both handles refer to the same element).
2872 ///
2873 /// @param other The HTMLSelectElement to copy.
2874 ///
2876
2877 ///
2878 /// Move constructor (`other` becomes empty).
2879 ///
2880 /// @param other The HTMLSelectElement to move from.
2881 ///
2882 HTMLSelectElement(HTMLSelectElement&& other) noexcept : Element(std::move(other)) {}
2883
2884 ///
2885 /// Assignment (copies or moves).
2886 ///
2887 /// @param other The HTMLSelectElement to assign from.
2888 ///
2889 /// @return Returns this HTMLSelectElement.
2890 ///
2892 Element::operator=(std::move(other));
2893 return *this;
2894 }
2895
2896 ///
2897 /// Select the option with a value and fire `input` and `change` events, like a user choice.
2898 ///
2899 /// Both events fire before this returns, and only if the selection changed. If no option has
2900 /// the value, no option is selected (like assigning the inherited `value` member, which fires no
2901 /// events).
2902 ///
2903 /// @param new_value The value of the option to select.
2904 ///
2905 void SetValue(std::string_view new_value) const {
2906 ulDOMElementSetValue(raw(), new_value.data() ? new_value.data() : "", new_value.size(), true,
2907 nullptr);
2908 }
2909
2910 ///
2911 /// Same as SetValue(), but returns a Result with the reason for a failure.
2912 ///
2913 /// @return Returns success (it fails only when this HTMLSelectElement is empty or its page is
2914 /// gone).
2915 ///
2916 [[nodiscard]] Result<void> SetValue(std::string_view new_value, Checked_t) const {
2917 if (IsEmpty())
2918 return detail::EmptyHandleError();
2919 detail::ErrorScope error;
2920 if (ulDOMElementSetValue(raw(), new_value.data() ? new_value.data() : "",
2921 new_value.size(), true, error.out()))
2922 return {};
2923 return Unexpected<Error>(error.TakeError());
2924 }
2925
2926 ///
2927 /// Get the form the select belongs to (form).
2928 ///
2929 /// @return Returns the select's form (empty if it isn't in one).
2930 ///
2931 HTMLFormElement form() const;
2932
2933 ///
2934 /// Check whether the select meets its constraints (checkValidity). Works like
2935 /// HTMLInputElement::checkValidity().
2936 ///
2937 /// @return Returns true if the select is valid.
2938 ///
2939 bool checkValidity() const { return ulDOMElementCheckValidity(raw()); }
2940
2941 ///
2942 /// Check whether the select meets its constraints, and report a failure to the user
2943 /// (reportValidity). Works like HTMLInputElement::reportValidity().
2944 ///
2945 /// @return Returns true if the select is valid.
2946 ///
2947 bool reportValidity() const { return ulDOMElementReportValidity(raw()); }
2948
2949 ///
2950 /// Set a custom validation error (setCustomValidity). Works like
2951 /// HTMLInputElement::setCustomValidity().
2952 ///
2953 /// @param error The error message. Pass an empty string to clear the error.
2954 ///
2955 void setCustomValidity(std::string_view error) const {
2956 ulDOMElementSetCustomValidity(raw(), error.data() ? error.data() : "", error.size());
2957 }
2958
2959 ///
2960 /// Get the message that describes why the select is invalid (validationMessage).
2961 ///
2962 /// @return Returns the message, or an empty string if the select is valid or excluded from
2963 /// validation.
2964 ///
2965 std::string validationMessage() const {
2966 return detail::TakeString(ulDOMElementGetValidationMessage(raw()));
2967 }
2968
2969 ///
2970 /// Get how the select fails its constraints (validity). Works like
2971 /// HTMLInputElement::validity().
2972 ///
2973 /// @return Returns a snapshot of the validity flags.
2974 ///
2975 ValidityState validity() const { return detail::ReadValidity(raw()); }
2976
2977 ///
2978 /// Get the number of options (length), including those inside an `<optgroup>`.
2979 ///
2980 /// @return Returns the number of options.
2981 ///
2982 size_t length() const { return ulDOMElementGetOptionCount(raw()); }
2983
2984 ///
2985 /// Get an option by its position (item).
2986 ///
2987 /// @param index The option's position, counting the options inside an `<optgroup>` too.
2988 ///
2989 /// @return Returns the option (empty if `index` is out of range).
2990 ///
2991 HTMLOptionElement item(size_t index) const;
2992
2993 ///
2994 /// Get the selected options in order (selectedOptions).
2995 ///
2996 /// This is defined in `<Ultralight/dom/ElementList.h>`. Include that header (or
2997 /// `<Ultralight/DOM.h>`) to call it.
2998 ///
2999 /// @return Returns the selected options (an empty list if none is selected).
3000 ///
3001 /// @note Unlike the web's live collection, the list doesn't change when the selection does.
3002 /// Call this again to see later changes.
3003 ///
3005
3006 private:
3007 friend class Element;
3008 explicit HTMLSelectElement(Element base) : Element(std::move(base)) {}
3009};
3010
3011///
3012/// An `<option>` element (HTMLOptionElement).
3013///
3014/// Get one with Element::AsOption() or HTMLSelectElement::item(). It adds the `selected` and
3015/// `text` members and the option's position.
3016///
3017/// The inherited `value` member reads the option's text when it has no `%value` attribute, like
3018/// the web.
3019///
3021 public:
3022 ///
3023 /// Whether or not the option is selected (selected). Assign to select or deselect it.
3024 ///
3025 /// This is the current state, not the `selected` attribute. Assigning fires no `change` event,
3026 /// like the web.
3027 ///
3028 detail::BoolProp<detail::OptionSelectedTag> selected;
3029
3030 ///
3031 /// The option's text with whitespace collapsed (text).
3032 ///
3033 /// Assign to replace the option's children with the text, like assigning textContent.
3034 ///
3035 detail::StringProp<detail::OptionTextTag> text;
3036
3037 ///
3038 /// Create an empty HTMLOptionElement.
3039 ///
3041
3042 ///
3043 /// Copy constructor (both handles refer to the same element).
3044 ///
3045 /// @param other The HTMLOptionElement to copy.
3046 ///
3048
3049 ///
3050 /// Move constructor (`other` becomes empty).
3051 ///
3052 /// @param other The HTMLOptionElement to move from.
3053 ///
3054 HTMLOptionElement(HTMLOptionElement&& other) noexcept : Element(std::move(other)) {}
3055
3056 ///
3057 /// Assignment (copies or moves).
3058 ///
3059 /// @param other The HTMLOptionElement to assign from.
3060 ///
3061 /// @return Returns this HTMLOptionElement.
3062 ///
3064 Element::operator=(std::move(other));
3065 return *this;
3066 }
3067
3068 ///
3069 /// Get the option's position in its `<select>` (index).
3070 ///
3071 /// @return Returns the position (0 if the option isn't in a `<select>`, like the web, and also
3072 /// for an empty HTMLOptionElement or one whose page is gone).
3073 ///
3074 size_t index() const { return ulDOMElementGetIndex(raw()); }
3075
3076 private:
3077 friend class Element;
3078 friend class HTMLSelectElement;
3079 explicit HTMLOptionElement(Element base) : Element(std::move(base)) {}
3080};
3081
3082///
3083/// A `<form>` element (HTMLFormElement).
3084///
3085/// Get one with Element::AsForm() or the form() method of a form control. It adds the form's
3086/// controls and ways to validate, submit, and reset the form:
3087///
3088/// ```
3089/// for (dom::Element control : form.elements())
3090/// Log(control.name);
3091/// ```
3092///
3093class HTMLFormElement : public Element {
3094 public:
3095 ///
3096 /// Create an empty HTMLFormElement.
3097 ///
3099
3100 ///
3101 /// Get the form's controls in document order (elements).
3102 ///
3103 /// The list includes controls outside the form whose `form` attribute holds the form's id.
3104 /// Image buttons (`<input type="image">`) aren't included, like the web.
3105 ///
3106 /// This is defined in `<Ultralight/dom/ElementList.h>`. Include that header (or
3107 /// `<Ultralight/DOM.h>`) to call it.
3108 ///
3109 /// @return Returns the controls (an empty list if there are none).
3110 ///
3111 /// @note Unlike the web's live collection, the list doesn't change when the form does. Call
3112 /// this again to see later changes.
3113 ///
3114 ElementList elements() const;
3115
3116 ///
3117 /// Check whether every control in the form meets its constraints (checkValidity).
3118 ///
3119 /// Each invalid control gets a cancelable `invalid` event (see
3120 /// HTMLInputElement::checkValidity()).
3121 ///
3122 /// @return Returns true if every control is valid.
3123 ///
3124 bool checkValidity() const { return ulDOMElementCheckValidity(raw()); }
3125
3126 ///
3127 /// Check whether every control in the form meets its constraints, and report a failure to the
3128 /// user (reportValidity).
3129 ///
3130 /// This works like checkValidity(). The first invalid control whose `invalid` event no
3131 /// listener canceled also gets keyboard focus (and scrolls into view).
3132 ///
3133 /// @return Returns true if every control is valid.
3134 ///
3135 /// @note The library doesn't display a validation message. To show one, read the control's
3136 /// message (eg, HTMLInputElement::validationMessage()) in an `invalid` listener and
3137 /// display it yourself.
3138 ///
3139 bool reportValidity() const { return ulDOMElementReportValidity(raw()); }
3140
3141 ///
3142 /// Submit the form (submit).
3143 ///
3144 /// Like the web's form.submit(), this skips validation and fires no `submit` event.
3145 ///
3146 void submit() const { ulDOMElementSubmit(raw()); }
3147
3148 ///
3149 /// Submit the form like a user would (requestSubmit).
3150 ///
3151 /// This checks the form's fields first. If they're valid, it fires a cancelable `submit` event
3152 /// and then submits. Call Event::preventDefault() in a `submit` listener to handle the
3153 /// submission natively instead.
3154 ///
3155 void requestSubmit() const { ulDOMElementRequestSubmit(raw(), nullptr, nullptr); }
3156
3157 ///
3158 /// Same as requestSubmit(), but returns a Result with the reason for a failure.
3159 ///
3160 /// @return Returns success, even when invalid fields stop the submission (it fails only when
3161 /// this form is empty or its page is gone).
3162 ///
3163 [[nodiscard]] Result<void> requestSubmit(Checked_t) const {
3164 if (IsEmpty())
3165 return detail::EmptyHandleError();
3166 detail::ErrorScope error;
3167 if (ulDOMElementRequestSubmit(raw(), nullptr, error.out()))
3168 return {};
3169 return Unexpected<Error>(error.TakeError());
3170 }
3171
3172 ///
3173 /// Submit the form like a user would, as if `submitter` was clicked (requestSubmit with a
3174 /// submitter).
3175 ///
3176 /// The submitter's attributes (eg, `formaction`) apply to the submission.
3177 ///
3178 /// @param submitter A submit button in this form. An empty Element works like requestSubmit()
3179 /// with no submitter.
3180 ///
3181 /// @note Nothing is submitted if `submitter` isn't a submit button in this form.
3182 ///
3183 void requestSubmit(const Element& submitter) const {
3184 ulDOMElementRequestSubmit(raw(), submitter.raw(), nullptr);
3185 }
3186
3187 ///
3188 /// Same as requestSubmit() with a submitter, but returns a Result with the reason for a failure
3189 /// (eg, a TypeError if `submitter` isn't a submit button).
3190 ///
3191 /// @return Returns success. Fails with a TypeError if `submitter` isn't a submit button, or a
3192 /// NotFoundError if it isn't in this form.
3193 ///
3194 [[nodiscard]] Result<void> requestSubmit(const Element& submitter, Checked_t) const {
3195 if (IsEmpty())
3196 return detail::EmptyHandleError();
3197 detail::ErrorScope error;
3198 if (ulDOMElementRequestSubmit(raw(), submitter.raw(), error.out()))
3199 return {};
3200 return Unexpected<Error>(error.TakeError());
3201 }
3202
3203 ///
3204 /// Reset the form's controls to their default values (reset).
3205 ///
3206 /// This fires a cancelable `reset` event first.
3207 ///
3208 void reset() const { ulDOMElementReset(raw()); }
3209
3210 private:
3211 friend class Element;
3212 friend class HTMLInputElement;
3214 friend class HTMLSelectElement;
3215 explicit HTMLFormElement(Element base) : Element(std::move(base)) {}
3216};
3217
3218///
3219/// An `<iframe>` element (HTMLIFrameElement).
3220///
3221/// Get one with Element::AsIFrame(), which also accepts a legacy `<frame>`. It adds the frame's
3222/// `src` and contentDocument() for reaching the page inside the frame.
3223///
3225 public:
3226 // At the same offset as HTMLImageElement::src (one offset per property).
3227
3228 ///
3229 /// The URL of the page in the frame (src).
3230 ///
3231 /// Reads return the absolute URL, resolved against the document's base URL (an empty string if
3232 /// there's no `src` attribute), like the web. Assign to set the `src` attribute, which loads
3233 /// the new page in the frame.
3234 ///
3235 detail::StringProp<detail::SrcTag> src;
3236
3237 ///
3238 /// Create an empty HTMLIFrameElement.
3239 ///
3241
3242 ///
3243 /// Copy constructor (both handles refer to the same element).
3244 ///
3245 /// @param other The HTMLIFrameElement to copy.
3246 ///
3248
3249 ///
3250 /// Move constructor (`other` becomes empty).
3251 ///
3252 /// @param other The HTMLIFrameElement to move from.
3253 ///
3254 HTMLIFrameElement(HTMLIFrameElement&& other) noexcept : Element(std::move(other)) {}
3255
3256 ///
3257 /// Assignment (copies or moves).
3258 ///
3259 /// @param other The HTMLIFrameElement to assign from.
3260 ///
3261 /// @return Returns this HTMLIFrameElement.
3262 ///
3264 Element::operator=(std::move(other));
3265 return *this;
3266 }
3267
3268 ///
3269 /// Get the document inside the frame (contentDocument).
3270 ///
3271 /// This is defined in `<Ultralight/dom/Document.h>`. Include that header (or
3272 /// `<Ultralight/DOM.h>`) to call it.
3273 ///
3274 /// @return Returns the frame's document (empty if the frame has no document). This works for a
3275 /// frame from any origin.
3276 ///
3277 /// @note The document's handles stop working when the frame navigates or is removed, even while
3278 /// this page lives on.
3279 ///
3280 Document contentDocument() const;
3281
3282 private:
3283 friend class Element;
3284 explicit HTMLIFrameElement(Element base) : Element(std::move(base)) {}
3285};
3286
3287///
3288/// An `<img>` element (HTMLImageElement).
3289///
3290/// Get one with Element::AsImage(). It adds the image's `src` and `alt` members.
3291///
3293 public:
3294 // At the same offset as HTMLIFrameElement::src (one offset per property).
3295
3296 ///
3297 /// The URL of the image (src).
3298 ///
3299 /// Reads return the absolute URL, resolved against the document's base URL (an empty string if
3300 /// there's no `src` attribute), like the web. Assign to set the `src` attribute, which loads the
3301 /// new image.
3302 ///
3303 detail::StringProp<detail::SrcTag> src;
3304
3305 ///
3306 /// The image's text alternative (alt), from its `alt` attribute. Reads as an empty string if
3307 /// there's none.
3308 ///
3309 detail::StringProp<detail::AltTag> alt;
3310
3311 ///
3312 /// Create an empty HTMLImageElement.
3313 ///
3315
3316 ///
3317 /// Copy constructor (both handles refer to the same element).
3318 ///
3319 /// @param other The HTMLImageElement to copy.
3320 ///
3322
3323 ///
3324 /// Move constructor (`other` becomes empty).
3325 ///
3326 /// @param other The HTMLImageElement to move from.
3327 ///
3328 HTMLImageElement(HTMLImageElement&& other) noexcept : Element(std::move(other)) {}
3329
3330 ///
3331 /// Assignment (copies or moves).
3332 ///
3333 /// @param other The HTMLImageElement to assign from.
3334 ///
3335 /// @return Returns this HTMLImageElement.
3336 ///
3338 Element::operator=(std::move(other));
3339 return *this;
3340 }
3341
3342 private:
3343 friend class Element;
3344 explicit HTMLImageElement(Element base) : Element(std::move(base)) {}
3345};
3346
3347///
3348/// An `<a>` element (HTMLAnchorElement).
3349///
3350/// Get one with Element::AsAnchor(). It adds the link's `href` member.
3351///
3353 public:
3354 ///
3355 /// The URL the link points to (href).
3356 ///
3357 /// Reads return the absolute URL, resolved against the document's base URL (an empty string if
3358 /// there's no `href` attribute), like the web. Assign to set the `href` attribute.
3359 ///
3360 detail::StringProp<detail::HrefTag> href;
3361
3362 ///
3363 /// Create an empty HTMLAnchorElement.
3364 ///
3366
3367 ///
3368 /// Copy constructor (both handles refer to the same element).
3369 ///
3370 /// @param other The HTMLAnchorElement to copy.
3371 ///
3373
3374 ///
3375 /// Move constructor (`other` becomes empty).
3376 ///
3377 /// @param other The HTMLAnchorElement to move from.
3378 ///
3379 HTMLAnchorElement(HTMLAnchorElement&& other) noexcept : Element(std::move(other)) {}
3380
3381 ///
3382 /// Assignment (copies or moves).
3383 ///
3384 /// @param other The HTMLAnchorElement to assign from.
3385 ///
3386 /// @return Returns this HTMLAnchorElement.
3387 ///
3389 Element::operator=(std::move(other));
3390 return *this;
3391 }
3392
3393 private:
3394 friend class Element;
3395 explicit HTMLAnchorElement(Element base) : Element(std::move(base)) {}
3396};
3397
3399 return HTMLInputElement(TagIs("INPUT") ? *this : Element());
3400}
3401
3403 return HTMLTextAreaElement(TagIs("TEXTAREA") ? *this : Element());
3404}
3405
3407 return HTMLSelectElement(TagIs("SELECT") ? *this : Element());
3408}
3409
3411 return HTMLOptionElement(TagIs("OPTION") ? *this : Element());
3412}
3413
3414inline HTMLOptionElement HTMLSelectElement::item(size_t index) const {
3415 // The waist guarantees the returned handle is an option, so no tag re-check is needed.
3416 return HTMLOptionElement(Element::Adopt(ulDOMElementGetOption(raw(), index)));
3417}
3418
3420 return HTMLFormElement(TagIs("FORM") ? *this : Element());
3421}
3422
3424 return HTMLIFrameElement(TagIs("IFRAME") || TagIs("FRAME") ? *this : Element());
3425}
3426
3428 return HTMLImageElement(TagIs("IMG") ? *this : Element());
3429}
3430
3432 return HTMLAnchorElement(TagIs("A") ? *this : Element());
3433}
3434
3435// The waist returns only a form (or NULL), so no tag re-check is needed.
3437 return HTMLFormElement(Element::Adopt(ulDOMElementGetForm(raw())));
3438}
3439
3441 return HTMLFormElement(Element::Adopt(ulDOMElementGetForm(raw())));
3442}
3443
3445 return HTMLFormElement(Element::Adopt(ulDOMElementGetForm(raw())));
3446}
3447
3449 return Element::Adopt(ulDOMNodeGetParentElement(detail_.handle));
3450}
3451
3452inline Element Node::AsElement() const {
3453 return Element::Adopt(ulDOMNodeAsElement(detail_.handle));
3454}
3455
3456inline Element Event::target() const {
3457 return Element::Adopt(ulDOMEventGetTarget(borrowed_));
3458}
3459
3460inline Node Event::targetNode() const {
3461 return Node::Adopt(ulDOMEventGetTargetNode(borrowed_));
3462}
3463
3465 return Element::Adopt(ulDOMEventGetCurrentTarget(borrowed_));
3466}
3467
3469 return Element::Adopt(ulDOMEventGetRelatedTarget(raw()));
3470}
3471
3473 return Element::Adopt(ulDOMEventGetRelatedTarget(raw()));
3474}
3475
3477 return Element::Adopt(ulDOMEventGetSubmitter(raw()));
3478}
3479
3480} // namespace dom
3481} // namespace ultralight
3482
3483// The property-proxy offset tables and operator definitions require the complete
3484// Element type above.
3485#include <Ultralight/dom/detail/PropertiesImpl.h>
3486
3487#pragma pop_macro("None")
A container for assembling DOM nodes off the page.
Definition DocumentFragment.h:43
The root of a page's DOM tree in a View or frame.
Definition Document.h:106
A handle to an element on a page.
Definition Element.h:142
Element insertBefore(const Element &child, const Element &ref_child) const
Insert an element before one of this element's children (insertBefore).
Definition Element.h:888
Result< Node > replaceChild(const Node &new_child, const Node &old_child, Checked_t) const
Same as replaceChild() with Nodes, but returns a Result with the reason for a failure (eg,...
Definition Element.h:1012
EventListener addEventListener(std::string_view type, F &&callback, EventListenerFlags flags) const
Listen for an event on this element, with options as flags (eg, dom::Once | dom::Capture).
Definition Element.h:1568
int offsetWidth() const
Get the element's width in CSS pixels, including its padding and borders (offsetWidth).
Definition Element.h:1307
int scrollWidth() const
Get the width of the element's content in CSS pixels, including any part scrolled out of view (scroll...
Definition Element.h:1379
void prepend(T &&... nodes) const
Add nodes and strings to the start of this element, before its first child (prepend).
Definition Element.h:807
detail::StringProp< detail::TextContentTag > textContent
The text of this element and all its descendants (textContent).
Definition Element.h:216
Result< Element > insertAdjacentElement(std::string_view position, const Element &other, Checked_t) const
Same as insertAdjacentElement(), but returns a Result with the reason for a failure (eg,...
Definition Element.h:1156
Element appendChild(const Element &child) const
Add an element as the last child of this element (appendChild).
Definition Element.h:676
detail::StringProp< detail::ValueTag > value
The current value of a form control (value).
Definition Element.h:306
int clientHeight() const
Get the element's inner height in CSS pixels, including its padding but not its borders or scrollbar ...
Definition Element.h:1357
bool contains(const Element &other) const
Whether or not another element is this element or one of its descendants (contains).
Definition Element.h:643
EventListener addEventListener(std::string_view type, C &receiver, M method, EventListenerFlags flags) const
Listen by calling a member function on an object you keep alive, with options as flags.
Definition Element.h:1625
EventListener addEventListener(std::string_view type, H holder, M method, const AddEventListenerOptions &options={}) const
Listen by calling a member function through a smart pointer that's checked before each call.
Definition Element.h:1650
int offsetLeft() const
Get the distance from the element's left border edge to its offsetParent()'s left padding edge,...
Definition Element.h:1330
void scrollBy(double x, double y) const
Scroll the element's content by an offset (scrollBy).
Definition Element.h:1426
Element firstElementChild() const
Get the element's first child element (firstElementChild).
Definition Element.h:1238
Result< bool > matches(std::string_view selectors, Checked_t) const
Same as matches(), but returns a Result with the reason for a failure (eg, a SyntaxError for a malfor...
Definition Element.h:481
Result< EventListener > On(std::string_view selector, std::string_view type, F &&callback, Checked_t) const
Same as On() with default options, but returns a Result with the reason for a failure.
Definition Element.h:1790
detail::BoolProp< detail::DisabledTag > disabled
Whether or not the element has a disabled attribute (disabled).
Definition Element.h:329
void replaceChildren(T &&... new_children) const
Replace all of this element's children with nodes and strings (replaceChildren).
Definition Element.h:847
EventListener On(std::string_view selector, std::string_view type, C &receiver, M method, EventListenerFlags flags) const
Listen for an event on matching elements by calling a member function on an object you keep alive,...
Definition Element.h:1851
detail::BoolProp< detail::CheckedTag > checked
Whether or not a checkbox or radio button is checked (checked).
Definition Element.h:321
void scrollIntoView(bool align_to_top=true) const
Scroll the element's ancestors so the element is visible (scrollIntoView).
Definition Element.h:1398
detail::StringProp< detail::ClassNameTag > className
The element's class attribute as one string (className).
Definition Element.h:273
Element closest(std::string_view selectors) const
Find the closest ancestor that matches a CSS selector, starting with this element itself (closest).
Definition Element.h:503
bool toggleAttribute(std::string_view name) const
Toggle an attribute (toggleAttribute).
Definition Element.h:592
std::string tagName() const
Get the element's tag name (tagName).
Definition Element.h:1222
Result< void > insertAdjacentText(std::string_view position, std::string_view text, Checked_t) const
Same as insertAdjacentText(), but returns a Result with the reason for a failure (eg,...
Definition Element.h:1202
void insertAdjacentText(std::string_view position, std::string_view text) const
Insert text relative to this element (insertAdjacentText).
Definition Element.h:1187
void removeAttribute(std::string_view name) const
Remove an attribute (removeAttribute).
Definition Element.h:579
Node insertBefore(const Node &child, const Node &ref_child) const
Insert a node of any kind before one of this element's children (insertBefore).
Definition Element.h:925
EventListener addEventListener(std::string_view type, F &&callback, const AddEventListenerOptions &options={}) const
Listen for an event on this element (addEventListener).
Definition Element.h:1540
int clientWidth() const
Get the element's inner width in CSS pixels, including its padding but not its borders or scrollbar (...
Definition Element.h:1349
detail::IntProp< detail::TabIndexTag > tabIndex
The element's position in the keyboard focus order (tabIndex).
Definition Element.h:294
static Element FromBorrowed(ULDOMElement handle)
Wrap a C handle the library owns (eg, a callback argument), adding a reference.
Definition Element.h:1996
Element insertAdjacentElement(std::string_view position, const Element &other) const
Insert an element relative to this element (insertAdjacentElement).
Definition Element.h:1142
detail::StringProp< detail::NameTag > name
The element's name attribute (name).
Definition Element.h:313
detail::ValueProp< detail::ScrollTopTag > scrollTop
How far the element's content is scrolled down, in CSS pixels (scrollTop).
Definition Element.h:356
Element querySelector(std::string_view selectors) const
Find the first element below this one that matches a CSS selector (querySelector).
Definition Element.h:410
bool hasAttribute(std::string_view name) const
Whether or not the element has an attribute (hasAttribute).
Definition Element.h:553
Result< void > replaceChildren(Checked_t, T &&... new_children) const
Same as replaceChildren(), but returns a Result with the reason for a failure (eg,...
Definition Element.h:867
std::vector< std::string > getAttributeNames() const
Get the names of the element's attributes in order (getAttributeNames).
Definition Element.h:621
int offsetTop() const
Get the distance from the element's top border edge to its offsetParent()'s top padding edge,...
Definition Element.h:1322
EventListener On(std::string_view selector, std::string_view type, H holder, M method, EventListenerFlags flags) const
Listen for an event on matching elements by calling a member function through a smart pointer,...
Definition Element.h:1906
Node replaceChild(const Node &new_child, const Node &old_child) const
Replace one of this element's children, of any kind, with a node of any kind (replaceChild).
Definition Element.h:998
Element cloneNode(bool deep=false) const
Copy this element (cloneNode).
Definition Element.h:1087
Element previousElementSibling() const
Get the element's previous sibling element (previousElementSibling).
Definition Element.h:1265
DOMRect getBoundingClientRect() const
Get the element's bounding rectangle relative to the viewport (getBoundingClientRect).
Definition Element.h:1297
EventListener On(std::string_view selector, std::string_view type, H holder, M method, const AddEventListenerOptions &options={}) const
Listen for an event on matching elements by calling a member function through a smart pointer that's ...
Definition Element.h:1880
Result< void > insertAdjacentHTML(std::string_view position, std::string_view html, Checked_t) const
Same as insertAdjacentHTML(), but returns a Result with the reason for a failure (eg,...
Definition Element.h:1117
Document ownerDocument() const
Get the document this element belongs to (ownerDocument).
Definition Document.h:1026
int scrollHeight() const
Get the height of the element's content in CSS pixels, including any part scrolled out of view (scrol...
Definition Element.h:1387
HTMLIFrameElement AsIFrame() const
Get this element as an <iframe> (HTMLIFrameElement).
Definition Element.h:3423
static Element Adopt(ULDOMElement handle)
Wrap a C handle you own, taking ownership of it.
Definition Element.h:1987
Result< void > append(Checked_t, T &&... nodes) const
Same as append(), but returns a Result with the reason for a failure (eg, a HierarchyRequestError for...
Definition Element.h:784
Element(const Element &other)
Copy constructor (both handles refer to the same element).
Definition Element.h:375
HTMLImageElement AsImage() const
Get this element as an <img> (HTMLImageElement).
Definition Element.h:3427
void focus() const
Give the element keyboard focus (focus).
Definition Element.h:1436
Result< Node > appendChild(const Node &child, Checked_t) const
Same as appendChild() with a Node, but returns a Result with the reason for a failure (eg,...
Definition Element.h:719
int offsetHeight() const
Get the element's height in CSS pixels, including its padding and borders (offsetHeight).
Definition Element.h:1314
ULDOMElement LeakRef()
Give up ownership of the C handle and return it.
Definition Element.h:2013
Result< EventListener > On(std::string_view selector, std::string_view type, F &&callback, const AddEventListenerOptions &options, Checked_t) const
Same as On(), but returns a Result with the reason for a failure (eg, a SyntaxError for a malformed s...
Definition Element.h:1767
Result< Element > insertBefore(const Element &child, const Element &ref_child, Checked_t) const
Same as insertBefore(), but returns a Result with the reason for a failure (eg, a NotFoundError when ...
Definition Element.h:901
Element(ULDOMElement handle)
Definition Element.h:2018
EventListener On(std::string_view selector, std::string_view type, F &&callback, EventListenerFlags flags) const
Listen for an event on matching elements, with options as flags (see the options overload).
Definition Element.h:1745
void blur() const
Remove keyboard focus from the element (blur).
Definition Element.h:1441
Result< Element > replaceChild(const Element &new_child, const Element &old_child, Checked_t) const
Same as replaceChild(), but returns a Result with the reason for a failure (eg, a NotFoundError when ...
Definition Element.h:973
HTMLFormElement AsForm() const
Get this element as a <form> (HTMLFormElement).
Definition Element.h:3419
size_t childElementCount() const
Get the number of the element's child elements (childElementCount).
Definition Element.h:1274
detail::BoolProp< detail::HiddenTag > hidden
Whether or not the element has a hidden attribute (hidden).
Definition Element.h:285
bool matches(std::string_view selectors) const
Whether or not this element matches a CSS selector (matches).
Definition Element.h:469
Result< Element > appendChild(const Element &child, Checked_t) const
Same as appendChild(), but returns a Result with the reason for a failure (eg, a HierarchyRequestErro...
Definition Element.h:687
HTMLOptionElement AsOption() const
Get this element as an <option> (HTMLOptionElement).
Definition Element.h:3410
Element nextElementSibling() const
Get the element's next sibling element (nextElementSibling).
Definition Element.h:1256
void click() const
Click the element (click).
Definition Element.h:1454
Result< Element > removeChild(const Element &child, Checked_t) const
Same as removeChild(), but returns a Result with the reason for a failure (eg, a NotFoundError when c...
Definition Element.h:1041
Node removeChild(const Node &child) const
Remove one of this element's children, of any kind (removeChild).
Definition Element.h:1060
bool dispatchEvent(std::string_view type, const EventInit &init={}) const
Dispatch a synthetic event to this element (dispatchEvent).
Definition Element.h:1477
ElementList querySelectorAll(std::string_view selectors) const
Find every element below this one that matches a CSS selector (querySelectorAll).
Definition ElementList.h:196
Result< void > prepend(Checked_t, T &&... nodes) const
Same as prepend(), but returns a Result with the reason for a failure (eg, a HierarchyRequestError fo...
Definition Element.h:823
HTMLSelectElement AsSelect() const
Get this element as a <select> (HTMLSelectElement).
Definition Element.h:3406
Element lastElementChild() const
Get the element's last child element (lastElementChild).
Definition Element.h:1247
Result< Node > removeChild(const Node &child, Checked_t) const
Same as removeChild() with a Node, but returns a Result with the reason for a failure (eg,...
Definition Element.h:1071
Node appendChild(const Node &child) const
Add a node of any kind as the last child of this element (appendChild).
Definition Element.h:708
detail::ValueProp< detail::SelectedIndexTag > selectedIndex
The index of the selected option in a <select> (selectedIndex), as a std::optional<size_t>.
Definition Element.h:344
HTMLTextAreaElement AsTextArea() const
Get this element as a <textarea> (HTMLTextAreaElement).
Definition Element.h:3402
EventListener addEventListener(std::string_view type, H holder, M method, EventListenerFlags flags) const
Listen by calling a member function through a smart pointer, with options as flags.
Definition Element.h:1672
Element replaceChild(const Element &new_child, const Element &old_child) const
Replace one of this element's children with another element (replaceChild).
Definition Element.h:959
detail::StringProp< detail::TitleTag > title
The element's title attribute (title).
Definition Element.h:278
EventListener On(std::string_view selector, std::string_view type, C &receiver, M method, const AddEventListenerOptions &options={}) const
Listen for an event on matching elements by calling a member function on an object you keep alive (se...
Definition Element.h:1821
Element & operator=(Element other) noexcept
Assignment (copies or moves).
Definition Element.h:391
Element()
Create an empty Element.
Definition Element.h:368
Result< Node > insertBefore(const Node &child, const Node &ref_child, Checked_t) const
Same as insertBefore() with Nodes, but returns a Result with the reason for a failure (eg,...
Definition Element.h:938
bool toggleAttribute(std::string_view name, bool force) const
Add or remove an attribute (toggleAttribute with force).
Definition Element.h:609
bool dispatchCustomEvent(std::string_view type, std::string_view event_detail, const EventInit &init={}) const
Dispatch a synthetic CustomEvent with a string payload to this element.
Definition Element.h:1504
Element(Element &&other) noexcept
Move constructor (other becomes empty).
Definition Element.h:382
Element removeChild(const Element &child) const
Remove one of this element's children (removeChild).
Definition Element.h:1030
int clientLeft() const
Get the width of the element's left border, in CSS pixels (clientLeft).
Definition Element.h:1371
void scrollTo(double x, double y) const
Scroll the element's content to a position (scrollTo).
Definition Element.h:1415
Element parentElement() const
Get the element's parent element (parentElement).
Definition Element.h:1231
ElementList children() const
Get the element's child elements in order (children).
Definition ElementList.h:224
detail::StringProp< detail::InnerTextTag > innerText
The element's text as it's rendered (innerText).
Definition Element.h:234
detail::StringProp< detail::InnerHTMLTag > innerHTML
The markup of the element's children (innerHTML).
Definition Element.h:250
HTMLAnchorElement AsAnchor() const
Get this element as an <a> (HTMLAnchorElement).
Definition Element.h:3431
EventListener addEventListener(std::string_view type, C &receiver, M method, const AddEventListenerOptions &options={}) const
Listen by calling a member function on an object you keep alive.
Definition Element.h:1599
Result< Element > closest(std::string_view selectors, Checked_t) const
Same as closest(), but returns a Result with the reason for a failure (eg, a SyntaxError for a malfor...
Definition Element.h:515
void insertAdjacentHTML(std::string_view position, std::string_view html) const
Parse markup and insert it relative to this element (insertAdjacentHTML).
Definition Element.h:1103
detail::StringProp< detail::IdTag > id
The element's id attribute (id).
Definition Element.h:267
std::optional< std::string > getAttribute(std::string_view name) const
Get an attribute's value (getAttribute).
Definition Element.h:540
ULDOMElement raw() const
Get the C handle, for passing to the <Ultralight/CAPI/CAPI_DOMElement.h> functions.
Definition Element.h:2006
detail::ClassListProxy classList
The element's classes (classList).
Definition Element.h:195
HTMLInputElement AsInput() const
Get this element as an <input> (HTMLInputElement).
Definition Element.h:3398
void append(T &&... nodes) const
Add nodes and strings to the end of this element, after its last child (append).
Definition Element.h:768
detail::StringProp< detail::OuterHTMLTag > outerHTML
The markup of the element and its children (outerHTML).
Definition Element.h:262
detail::DatasetProxy dataset
The element's data-* attributes by camelCase name (dataset).
Definition Element.h:209
EventListener On(std::string_view selector, std::string_view type, F &&callback, const AddEventListenerOptions &options={}) const
Listen for an event on the elements inside this one that match a CSS selector, including elements add...
Definition Element.h:1710
void setAttribute(std::string_view name, std::string_view new_value) const
Set an attribute's value (setAttribute).
Definition Element.h:568
Result< Element > querySelector(std::string_view selectors, Checked_t) const
Same as querySelector(), but returns a Result with the reason for a failure (eg, a SyntaxError for a ...
Definition Element.h:422
detail::ValueProp< detail::ScrollLeftTag > scrollLeft
How far the element's content is scrolled right, in CSS pixels (scrollLeft).
Definition Element.h:363
Element offsetParent() const
Get the element that offsetTop() and offsetLeft() measure from (offsetParent).
Definition Element.h:1341
detail::StyleProxy style
The element's inline style (style).
Definition Element.h:179
bool TagIs(const char *upper_tag) const
Whether or not this element's tag name is upper_tag.
Definition Element.h:2026
int clientTop() const
Get the width of the element's top border, in CSS pixels (clientTop).
Definition Element.h:1364
A fixed list of DOM elements returned by a query or an Element::children() call.
Definition ElementList.h:55
static Error PageGone()
Create a page-gone error (is_page_gone() returns true).
Definition Error.h:123
Node targetNode() const
Get the node the event was dispatched to (target).
Definition Element.h:3460
Element target() const
Get the element the event was dispatched to (target).
Definition Element.h:3456
Element currentTarget() const
Get the element whose listener is running (currentTarget).
Definition Element.h:3464
ULDOMEvent raw() const
Get the C handle, for passing to the <Ultralight/CAPI/CAPI_DOMEvent.h> functions.
Definition Event.h:268
A handle to a registered DOM event listener.
Definition EventListener.h:151
Element relatedTarget() const
Get the other element of the focus change (relatedTarget).
Definition Element.h:3472
An <a> element (HTMLAnchorElement).
Definition Element.h:3352
friend class Element
Definition Element.h:3394
HTMLAnchorElement(const HTMLAnchorElement &other)
Copy constructor (both handles refer to the same element).
Definition Element.h:3372
detail::StringProp< detail::HrefTag > href
The URL the link points to (href).
Definition Element.h:3360
HTMLAnchorElement & operator=(HTMLAnchorElement other) noexcept
Assignment (copies or moves).
Definition Element.h:3388
HTMLAnchorElement(HTMLAnchorElement &&other) noexcept
Move constructor (other becomes empty).
Definition Element.h:3379
HTMLAnchorElement()
Create an empty HTMLAnchorElement.
Definition Element.h:3365
A <form> element (HTMLFormElement).
Definition Element.h:3093
friend class Element
Definition Element.h:3211
HTMLFormElement()
Create an empty HTMLFormElement.
Definition Element.h:3098
friend class HTMLSelectElement
Definition Element.h:3214
friend class HTMLInputElement
Definition Element.h:3212
bool checkValidity() const
Check whether every control in the form meets its constraints (checkValidity).
Definition Element.h:3124
Result< void > requestSubmit(const Element &submitter, Checked_t) const
Same as requestSubmit() with a submitter, but returns a Result with the reason for a failure (eg,...
Definition Element.h:3194
Result< void > requestSubmit(Checked_t) const
Same as requestSubmit(), but returns a Result with the reason for a failure.
Definition Element.h:3163
void reset() const
Reset the form's controls to their default values (reset).
Definition Element.h:3208
void requestSubmit() const
Submit the form like a user would (requestSubmit).
Definition Element.h:3155
ElementList elements() const
Get the form's controls in document order (elements).
Definition ElementList.h:219
void requestSubmit(const Element &submitter) const
Submit the form like a user would, as if submitter was clicked (requestSubmit with a submitter).
Definition Element.h:3183
bool reportValidity() const
Check whether every control in the form meets its constraints, and report a failure to the user (repo...
Definition Element.h:3139
void submit() const
Submit the form (submit).
Definition Element.h:3146
friend class HTMLTextAreaElement
Definition Element.h:3213
An <iframe> element (HTMLIFrameElement).
Definition Element.h:3224
friend class Element
Definition Element.h:3283
HTMLIFrameElement()
Create an empty HTMLIFrameElement.
Definition Element.h:3240
Document contentDocument() const
Get the document inside the frame (contentDocument).
Definition Document.h:1034
HTMLIFrameElement(HTMLIFrameElement &&other) noexcept
Move constructor (other becomes empty).
Definition Element.h:3254
HTMLIFrameElement(const HTMLIFrameElement &other)
Copy constructor (both handles refer to the same element).
Definition Element.h:3247
detail::StringProp< detail::SrcTag > src
The URL of the page in the frame (src).
Definition Element.h:3235
HTMLIFrameElement & operator=(HTMLIFrameElement other) noexcept
Assignment (copies or moves).
Definition Element.h:3263
An <img> element (HTMLImageElement).
Definition Element.h:3292
friend class Element
Definition Element.h:3343
HTMLImageElement()
Create an empty HTMLImageElement.
Definition Element.h:3314
HTMLImageElement & operator=(HTMLImageElement other) noexcept
Assignment (copies or moves).
Definition Element.h:3337
HTMLImageElement(const HTMLImageElement &other)
Copy constructor (both handles refer to the same element).
Definition Element.h:3321
HTMLImageElement(HTMLImageElement &&other) noexcept
Move constructor (other becomes empty).
Definition Element.h:3328
detail::StringProp< detail::SrcTag > src
The URL of the image (src).
Definition Element.h:3303
detail::StringProp< detail::AltTag > alt
The image's text alternative (alt), from its alt attribute.
Definition Element.h:3309
An <input> element (HTMLInputElement).
Definition Element.h:2189
friend class Element
Definition Element.h:2533
void SetValue(std::string_view new_value) const
Set the input's value and fire input and change events, like a user edit.
Definition Element.h:2301
detail::ValueProp< detail::SelectionStartTag > selectionStart
The offset where the selection starts (selectionStart), as a std::optional<size_t>.
Definition Element.h:2234
std::string validationMessage() const
Get the message that describes why the input is invalid (validationMessage).
Definition Element.h:2374
Result< void > setRangeText(std::string_view replacement, size_t start, size_t end, Checked_t) const
Same as setRangeText() with a range, but returns a Result with the reason for a failure (eg,...
Definition Element.h:2507
detail::StringProp< detail::PlaceholderTag > placeholder
The input's placeholder attribute (placeholder), the hint it shows while it's empty.
Definition Element.h:2205
bool checkValidity() const
Check whether the input meets its constraints (checkValidity).
Definition Element.h:2340
HTMLInputElement()
Create an empty HTMLInputElement.
Definition Element.h:2261
void setRangeText(std::string_view replacement) const
Replace the selected text (setRangeText).
Definition Element.h:2453
void setRangeText(std::string_view replacement, size_t start, size_t end, SelectionMode mode=SelectionMode::Preserve) const
Replace a range of the text (setRangeText with a range).
Definition Element.h:2492
ValidityState validity() const
Get how the input fails its constraints (validity).
Definition Element.h:2385
void setCustomValidity(std::string_view error) const
Set a custom validation error (setCustomValidity).
Definition Element.h:2363
HTMLInputElement(HTMLInputElement &&other) noexcept
Move constructor (other becomes empty).
Definition Element.h:2275
Result< void > SetValue(std::string_view new_value, Checked_t) const
Same as SetValue(), but returns a Result with the reason for a failure (eg, an InvalidStateError for ...
Definition Element.h:2313
HTMLFormElement form() const
Get the form the input belongs to (form).
Definition Element.h:3436
HTMLInputElement(const HTMLInputElement &other)
Copy constructor (both handles refer to the same element).
Definition Element.h:2268
Result< void > setSelectionRange(size_t start, size_t end, Checked_t) const
Same as setSelectionRange(), but returns a Result with the reason for a failure (eg,...
Definition Element.h:2421
detail::StringProp< detail::DefaultValueTag > defaultValue
The input's default value (defaultValue), which is its value attribute.
Definition Element.h:2220
Result< void > setRangeText(std::string_view replacement, Checked_t) const
Same as setRangeText(), but returns a Result with the reason for a failure (eg, an InvalidStateError ...
Definition Element.h:2465
detail::BoolProp< detail::ReadOnlyTag > readOnly
Whether or not the input has a readonly attribute (readOnly).
Definition Element.h:2212
detail::ValueProp< detail::SelectionEndTag > selectionEnd
The offset just past the end of the selection (selectionEnd).
Definition Element.h:2239
Result< void > setRangeText(std::string_view replacement, size_t start, size_t end, SelectionMode mode, Checked_t) const
Same as setRangeText() with a range and a mode, but returns a Result with the reason for a failure.
Definition Element.h:2519
detail::BoolProp< detail::RequiredTag > required
Whether or not the input has a required attribute (required).
Definition Element.h:2199
Result< void > setSelectionRange(size_t start, size_t end, SelectionDirection direction, Checked_t) const
Same as setSelectionRange() with a direction, but returns a Result with the reason for a failure.
Definition Element.h:2432
bool reportValidity() const
Check whether the input meets its constraints, and report a failure to the user (reportValidity).
Definition Element.h:2354
void select() const
Select all of the input's text (select).
Definition Element.h:2393
detail::ValueProp< detail::SelectionDirectionTag > selectionDirection
The direction of the selection (selectionDirection), as a SelectionDirection.
Definition Element.h:2248
detail::StringProp< detail::InputTypeTag > type
The input's type (type), such as text, email, or checkbox.
Definition Element.h:2256
void setSelectionRange(size_t start, size_t end, SelectionDirection direction=SelectionDirection::None) const
Set the selection (setSelectionRange).
Definition Element.h:2408
HTMLInputElement & operator=(HTMLInputElement other) noexcept
Assignment (copies or moves).
Definition Element.h:2284
An <option> element (HTMLOptionElement).
Definition Element.h:3020
friend class Element
Definition Element.h:3077
friend class HTMLSelectElement
Definition Element.h:3078
detail::StringProp< detail::OptionTextTag > text
The option's text with whitespace collapsed (text).
Definition Element.h:3035
HTMLOptionElement & operator=(HTMLOptionElement other) noexcept
Assignment (copies or moves).
Definition Element.h:3063
HTMLOptionElement()
Create an empty HTMLOptionElement.
Definition Element.h:3040
size_t index() const
Get the option's position in its <select> (index).
Definition Element.h:3074
HTMLOptionElement(const HTMLOptionElement &other)
Copy constructor (both handles refer to the same element).
Definition Element.h:3047
HTMLOptionElement(HTMLOptionElement &&other) noexcept
Move constructor (other becomes empty).
Definition Element.h:3054
detail::BoolProp< detail::OptionSelectedTag > selected
Whether or not the option is selected (selected).
Definition Element.h:3028
A <select> element (HTMLSelectElement).
Definition Element.h:2851
friend class Element
Definition Element.h:3007
HTMLSelectElement(HTMLSelectElement &&other) noexcept
Move constructor (other becomes empty).
Definition Element.h:2882
void SetValue(std::string_view new_value) const
Select the option with a value and fire input and change events, like a user choice.
Definition Element.h:2905
HTMLSelectElement()
Create an empty HTMLSelectElement.
Definition Element.h:2868
std::string validationMessage() const
Get the message that describes why the select is invalid (validationMessage).
Definition Element.h:2965
bool checkValidity() const
Check whether the select meets its constraints (checkValidity).
Definition Element.h:2939
HTMLOptionElement item(size_t index) const
Get an option by its position (item).
Definition Element.h:3414
HTMLSelectElement(const HTMLSelectElement &other)
Copy constructor (both handles refer to the same element).
Definition Element.h:2875
ElementList selectedOptions() const
Get the selected options in order (selectedOptions).
Definition ElementList.h:214
ValidityState validity() const
Get how the select fails its constraints (validity).
Definition Element.h:2975
void setCustomValidity(std::string_view error) const
Set a custom validation error (setCustomValidity).
Definition Element.h:2955
size_t length() const
Get the number of options (length), including those inside an <optgroup>.
Definition Element.h:2982
Result< void > SetValue(std::string_view new_value, Checked_t) const
Same as SetValue(), but returns a Result with the reason for a failure.
Definition Element.h:2916
HTMLFormElement form() const
Get the form the select belongs to (form).
Definition Element.h:3444
detail::BoolProp< detail::RequiredTag > required
Whether or not the select has a required attribute (required).
Definition Element.h:2863
bool reportValidity() const
Check whether the select meets its constraints, and report a failure to the user (reportValidity).
Definition Element.h:2947
HTMLSelectElement & operator=(HTMLSelectElement other) noexcept
Assignment (copies or moves).
Definition Element.h:2891
A <textarea> element (HTMLTextAreaElement).
Definition Element.h:2544
friend class Element
Definition Element.h:2834
void SetValue(std::string_view new_value) const
Set the textarea's value and fire input and change events, like a user edit.
Definition Element.h:2631
detail::ValueProp< detail::SelectionStartTag > selectionStart
The offset where the selection starts (selectionStart).
Definition Element.h:2579
std::string validationMessage() const
Get the message that describes why the textarea is invalid (validationMessage).
Definition Element.h:2691
Result< void > setRangeText(std::string_view replacement, size_t start, size_t end, Checked_t) const
Same as setRangeText() with a range, but returns a Result with the reason for a failure (eg,...
Definition Element.h:2809
detail::StringProp< detail::PlaceholderTag > placeholder
The textarea's placeholder attribute (placeholder).
Definition Element.h:2558
bool checkValidity() const
Check whether the textarea meets its constraints (checkValidity).
Definition Element.h:2665
void setRangeText(std::string_view replacement) const
Replace the selected text (setRangeText).
Definition Element.h:2759
void setRangeText(std::string_view replacement, size_t start, size_t end, SelectionMode mode=SelectionMode::Preserve) const
Replace a range of the text (setRangeText with a range).
Definition Element.h:2795
ValidityState validity() const
Get how the textarea fails its constraints (validity).
Definition Element.h:2701
void setCustomValidity(std::string_view error) const
Set a custom validation error (setCustomValidity).
Definition Element.h:2681
Result< void > SetValue(std::string_view new_value, Checked_t) const
Same as SetValue(), but returns a Result with the reason for a failure.
Definition Element.h:2642
HTMLFormElement form() const
Get the form the textarea belongs to (form).
Definition Element.h:3440
Result< void > setSelectionRange(size_t start, size_t end, Checked_t) const
Same as setSelectionRange(), but returns a Result with the reason for a failure.
Definition Element.h:2730
detail::StringProp< detail::DefaultValueTag > defaultValue
The textarea's default value (defaultValue), which is the text between its tags.
Definition Element.h:2573
Result< void > setRangeText(std::string_view replacement, Checked_t) const
Same as setRangeText(), but returns a Result with the reason for a failure.
Definition Element.h:2769
detail::BoolProp< detail::ReadOnlyTag > readOnly
Whether or not the textarea has a readonly attribute (readOnly).
Definition Element.h:2564
detail::ValueProp< detail::SelectionEndTag > selectionEnd
The offset just past the end of the selection (selectionEnd).
Definition Element.h:2584
Result< void > setRangeText(std::string_view replacement, size_t start, size_t end, SelectionMode mode, Checked_t) const
Same as setRangeText() with a range and a mode, but returns a Result with the reason for a failure.
Definition Element.h:2820
detail::BoolProp< detail::RequiredTag > required
Whether or not the textarea has a required attribute (required).
Definition Element.h:2552
HTMLTextAreaElement(const HTMLTextAreaElement &other)
Copy constructor (both handles refer to the same element).
Definition Element.h:2602
Result< void > setSelectionRange(size_t start, size_t end, SelectionDirection direction, Checked_t) const
Same as setSelectionRange() with a direction, but returns a Result with the reason for a failure.
Definition Element.h:2740
bool reportValidity() const
Check whether the textarea meets its constraints, and report a failure to the user (reportValidity).
Definition Element.h:2673
HTMLTextAreaElement()
Create an empty HTMLTextAreaElement.
Definition Element.h:2595
void select() const
Select all of the text (select).
Definition Element.h:2706
detail::ValueProp< detail::SelectionDirectionTag > selectionDirection
The direction of the selection (selectionDirection).
Definition Element.h:2590
void setSelectionRange(size_t start, size_t end, SelectionDirection direction=SelectionDirection::None) const
Set the selection (setSelectionRange).
Definition Element.h:2719
HTMLTextAreaElement & operator=(HTMLTextAreaElement other) noexcept
Assignment (copies or moves).
Definition Element.h:2618
HTMLTextAreaElement(HTMLTextAreaElement &&other) noexcept
Move constructor (other becomes empty).
Definition Element.h:2609
Element relatedTarget() const
Get the other element of a mouse transition (relatedTarget).
Definition Element.h:3468
A reference to an item in the DOM tree.
Definition Node.h:147
detail::NodeStorage detail_
Internal storage (not part of the API).
Definition Node.h:166
Node()
Create an empty Node.
Definition Node.h:171
ULDOMNode LeakRef()
Give up ownership of the C handle and return it.
Definition Node.h:546
ULDOMNode raw() const
Get the C handle, for passing to the <Ultralight/CAPI/CAPI_DOMNode.h> functions.
Definition Node.h:539
static Node Adopt(ULDOMNode handle)
Wrap a C handle you own, taking ownership of it.
Definition Node.h:520
bool IsEmpty() const
Whether or not this Node is empty (it holds no handle).
Definition Node.h:216
bool IsAlive() const
Whether or not this Node is valid (it isn't empty and its page is still alive).
Definition Node.h:223
Element parentElement() const
Get the node's parent element (parentElement).
Definition Element.h:3448
Element AsElement() const
Get this node as an Element.
Definition Element.h:3452
Element submitter() const
Get the button that submitted the form (submitter).
Definition Element.h:3476
Whether or not the DOM API can hold an object through H (ignoring const and references),...
Definition Holders.h:41
Definition StringSTL.h:166
Direct C++ access to modify page elements and handle events.
constexpr Checked_t Checked
Pass to a DOM call to get a dom::Result holding the reason for a failure instead of an empty value.
Definition Error.h:299
EventListenerFlags
Options for adding an event listener, as flags (addEventListener() and On()).
Definition EventListener.h:41
SelectionDirection
The direction of a text control's selection (selectionDirection).
Definition Element.h:2057
@ Forward
The selection was made toward the end of the text.
Definition Element.h:2059
@ None
No direction.
Definition Element.h:2058
@ Backward
The selection was made toward the start of the text.
Definition Element.h:2060
SelectionMode
Where the selection goes after HTMLInputElement::setRangeText() replaces text (selectMode).
Definition Element.h:2066
@ Preserve
Keep the selection, adjusted for the edit.
Definition Element.h:2067
@ Select
Select the inserted text.
Definition Element.h:2068
Expected< T, Error > Result
The result of a DOM operation that can fail (either a T or a dom::Error).
Definition Error.h:277
Root namespace for every public Ultralight type, function, and enumeration.
@ End
Definition Anchor.h:29
@ Start
Definition Anchor.h:29
@ None
Definition Anchor.h:36
Options for adding an event listener, like the web's options object for addEventListener().
Definition EventListener.h:526
The type of dom::Checked.
Definition Error.h:282
A rectangle in CSS pixels, relative to the viewport (DOMRect).
Definition DOMRect.h:26
Options for an event you dispatch yourself (the web's EventInit).
Definition Event.h:30
The ways a form control fails its constraints (ValidityState).
Definition Element.h:2085
bool patternMismatch
The value doesn't match the pattern attribute.
Definition Element.h:2088
bool valid
None of the above are true.
Definition Element.h:2096
bool valueMissing
A required control has no value.
Definition Element.h:2086
bool tooShort
The user made the value shorter than minlength.
Definition Element.h:2090
bool rangeOverflow
The value is greater than max.
Definition Element.h:2092
bool rangeUnderflow
The value is less than min.
Definition Element.h:2091
bool tooLong
The user made the value longer than maxlength.
Definition Element.h:2089
bool stepMismatch
The value doesn't fit the step attribute.
Definition Element.h:2093
bool badInput
The user entered something the control can't convert.
Definition Element.h:2094
bool customError
A custom error message is set.
Definition Element.h:2095
bool typeMismatch
The value doesn't fit the input type (eg, a bad email).
Definition Element.h:2087