Element Manipulation

The Element namespace provides pipeable functions for imperative DOM operations. While most of the time you’ll set attributes declaratively when creating elements, these functions are useful for refs, post-mount operations, and dynamic manipulation.

Getting a Reference

Use ref to create a reference you can manipulate later:

import { ref, Element } from "@effex/dom";

const buttonRef = yield* ref<HTMLButtonElement>();

// Pass to an element
yield* $.button({ ref: buttonRef }, $.of("Click me"));

// Manipulate later — waits until the element is mounted
yield* buttonRef.pipe(Element.focus);

// Check connection status
buttonRef.isConnected;  // Readable<boolean>

Attributes

yield* el.pipe(Element.setAttribute("aria-expanded", "true"));
yield* el.pipe(Element.removeAttribute("disabled"));
yield* el.pipe(Element.toggleAttribute("hidden"));
yield* el.pipe(Element.hasAttribute("disabled"));  // Effect<boolean>

// Reactive binding — attribute updates when Readable changes
yield* el.pipe(Element.bindAttribute("aria-label", labelReadable));
yield* el.pipe(Element.bindBooleanAttribute("disabled", isDisabled));

Classes

yield* el.pipe(Element.addClass("active", "highlighted"));
yield* el.pipe(Element.removeClass("loading"));
yield* el.pipe(Element.toggleClass("expanded"));
yield* el.pipe(Element.replaceClass("old-class", "new-class"));
yield* el.pipe(Element.setClass("entirely-new-class"));

// Reactive binding
yield* el.pipe(Element.bindClass(classNameReadable));

Styles

yield* el.pipe(Element.setStyle("backgroundColor", "red"));
yield* el.pipe(Element.setStyles({ opacity: "1", fontSize: "16px" }));
yield* el.pipe(Element.removeStyle("color"));

// Reactive binding
yield* el.pipe(Element.bindStyle("color", colorReadable));

Data Attributes

yield* el.pipe(Element.setData("state", "open"));    // data-state="open"
yield* el.pipe(Element.removeData("state"));
yield* el.pipe(Element.getData("state"));             // Effect<string, DataAttributeNotFound>

// Reactive binding
yield* el.pipe(Element.bindData("state", stateReadable));

Content

yield* el.pipe(Element.setTextContent("Hello"));
yield* el.pipe(Element.setInnerHTML("<em>bold</em>"));
yield* el.pipe(Element.setInputValue("new value"));  // Without cursor reset

// Reactive bindings
yield* el.pipe(Element.bindTextContent(textReadable));
yield* el.pipe(Element.bindInputValue(valueReadable));

Focus & Interaction

yield* el.pipe(Element.focus);
yield* el.pipe(Element.blur);
yield* el.pipe(Element.click);
yield* el.pipe(Element.focusFirst("[data-item]"));  // First matching descendant
yield* el.pipe(Element.focusLast("[data-item]"));

yield* el.pipe(Element.getBoundingClientRect);  // Effect<DOMRect>
yield* el.pipe(Element.getId);                   // Effect<string>
yield* el.pipe(Element.contains(childNode));     // Effect<boolean>

Event Listeners

For adding listeners imperatively (usually you’d use onClick etc. on the element instead):

yield* el.pipe(Element.on("click", (e) => handleClick(e)));
yield* el.pipe(Element.once("transitionend", (e) => afterTransition()));

Debugging

yield* el.pipe(Element.tap((node) => console.log(node)));
yield* el.pipe(Element.tapEffect((node) => Effect.log("mounted")));