diff --git a/src/browser/tests/element/html/label.html b/src/browser/tests/element/html/label.html
index cdc3f637..b2ebc2cb 100644
--- a/src/browser/tests/element/html/label.html
+++ b/src/browser/tests/element/html/label.html
@@ -16,3 +16,59 @@
testing.expectEqual('', l2.htmlFor);
}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/browser/webapi/element/html/Label.zig b/src/browser/webapi/element/html/Label.zig
index c08bff06..1069f2c6 100644
--- a/src/browser/webapi/element/html/Label.zig
+++ b/src/browser/webapi/element/html/Label.zig
@@ -3,6 +3,7 @@ const Page = @import("../../../Page.zig");
const Node = @import("../../Node.zig");
const Element = @import("../../Element.zig");
const HtmlElement = @import("../Html.zig");
+const TreeWalker = @import("../../TreeWalker.zig");
const Label = @This();
@@ -23,6 +24,33 @@ pub fn setHtmlFor(self: *Label, value: []const u8, page: *Page) !void {
try self.asElement().setAttributeSafe(comptime .wrap("for"), .wrap(value), page);
}
+pub fn getControl(self: *Label, page: *Page) ?*Element {
+ if (self.asElement().getAttributeSafe(comptime .wrap("for"))) |id| {
+ const el = page.document.getElementById(id, page) orelse return null;
+ if (!isLabelable(el)) {
+ return null;
+ }
+ return el;
+ }
+
+ var tw = TreeWalker.FullExcludeSelf.Elements.init(self.asNode(), .{});
+ while (tw.next()) |el| {
+ if (isLabelable(el)) {
+ return el;
+ }
+ }
+ return null;
+}
+
+fn isLabelable(el: *Element) bool {
+ const html = el.is(HtmlElement) orelse return false;
+ return switch (html._type) {
+ .button, .meter, .output, .progress, .select, .textarea => true,
+ .input => |input| input._input_type != .hidden,
+ else => false,
+ };
+}
+
pub const JsApi = struct {
pub const bridge = js.Bridge(Label);
@@ -33,6 +61,7 @@ pub const JsApi = struct {
};
pub const htmlFor = bridge.accessor(Label.getHtmlFor, Label.setHtmlFor, .{});
+ pub const control = bridge.accessor(Label.getControl, null, .{});
};
const testing = @import("../../../../testing.zig");