Element.toggleAttribute and InteresectionObserver init param overload

This commit is contained in:
Karl Seguin
2025-11-21 14:35:52 +08:00
parent b9486e8935
commit 095413c6c5
3 changed files with 66 additions and 2 deletions

View File

@@ -102,3 +102,36 @@
testing.expectEqual(false, el1.hasAttribute('data-test'));
}
</script>
<script id=toggleAttribute>
{
const el1 = $('#attr1');
// Toggle non-existent attribute (should add it and return true)
testing.expectEqual(false, el1.hasAttribute('toggle-test'));
testing.expectEqual(true, el1.toggleAttribute('toggle-test'));
testing.expectEqual(true, el1.hasAttribute('toggle-test'));
testing.expectEqual('', el1.getAttribute('toggle-test'));
// Toggle existing attribute (should remove it and return false)
testing.expectEqual(false, el1.toggleAttribute('toggle-test'));
testing.expectEqual(false, el1.hasAttribute('toggle-test'));
// Toggle with force=true when attribute doesn't exist (should add and return true)
testing.expectEqual(false, el1.hasAttribute('toggle-test'));
testing.expectEqual(true, el1.toggleAttribute('toggle-test', true));
testing.expectEqual(true, el1.hasAttribute('toggle-test'));
// Toggle with force=true when attribute exists (should keep and return true)
testing.expectEqual(true, el1.toggleAttribute('toggle-test', true));
testing.expectEqual(true, el1.hasAttribute('toggle-test'));
// Toggle with force=false when attribute exists (should remove and return false)
testing.expectEqual(false, el1.toggleAttribute('toggle-test', false));
testing.expectEqual(false, el1.hasAttribute('toggle-test'));
// Toggle with force=false when attribute doesn't exist (should keep removed and return false)
testing.expectEqual(false, el1.toggleAttribute('toggle-test', false));
testing.expectEqual(false, el1.hasAttribute('toggle-test'));
}
</script>

View File

@@ -354,6 +354,22 @@ pub fn removeAttribute(self: *Element, name: []const u8, page: *Page) !void {
return attributes.delete(name, self, page);
}
pub fn toggleAttribute(self: *Element, name: []const u8, force: ?bool, page: *Page) !bool {
const has = try self.hasAttribute(name, page);
const should_add = force orelse !has;
if (should_add and !has) {
try self.setAttribute(name, "", page);
return true;
} else if (!should_add and has) {
try self.removeAttribute(name, page);
return false;
}
return should_add;
}
pub fn removeAttributeNode(self: *Element, attr: *Attribute, page: *Page) !*Attribute {
if (attr._element == null or attr._element.? != self) {
return error.NotFound;
@@ -909,6 +925,7 @@ pub const JsApi = struct {
pub const setAttribute = bridge.function(Element.setAttribute, .{});
pub const setAttributeNode = bridge.function(Element.setAttributeNode, .{});
pub const removeAttribute = bridge.function(Element.removeAttribute, .{});
pub const toggleAttribute = bridge.function(Element.toggleAttribute, .{});
pub const getAttributeNames = bridge.function(Element.getAttributeNames, .{});
pub const removeAttributeNode = bridge.function(Element.removeAttributeNode, .{ .dom_exception = true });
pub const shadowRoot = bridge.accessor(Element.getShadowRoot, null, .{});

View File

@@ -53,18 +53,32 @@ var zero_rect: DOMRect = .{
pub const ObserverInit = struct {
root: ?*Element = null,
rootMargin: ?[]const u8 = null,
threshold: []const f64 = &.{0.0},
threshold: Threshold = .{.scalar = 0.0},
const Threshold = union(enum) {
scalar: f64,
array: []const f64,
};
};
pub fn init(callback: js.Function, options: ?ObserverInit, page: *Page) !*IntersectionObserver {
const opts = options orelse ObserverInit{};
const root_margin = if (opts.rootMargin) |rm| try page.arena.dupe(u8, rm) else "0px";
const threshold = switch (opts.threshold) {
.scalar => |s| blk: {
const arr = try page.arena.alloc(f64, 1);
arr[0] = s;
break :blk arr;
},
.array => |arr| try page.arena.dupe(f64, arr),
};
return page._factory.create(IntersectionObserver{
._callback = callback,
._root = opts.root,
._root_margin = root_margin,
._threshold = try page.arena.dupe(f64, opts.threshold),
._threshold = threshold
});
}