@@ -290,6 +290,22 @@ pub const Element = struct {
290290 return true ;
291291 }
292292
293+ pub fn _getAttributeNames (self : * parser.Element , page : * Page ) ! []const []const u8 {
294+ const attributes = try parser .nodeGetAttributes (@ptrCast (self )) orelse return &.{};
295+ const ln = try parser .namedNodeMapGetLength (attributes );
296+
297+ const names = try page .call_arena .alloc ([]const u8 , ln );
298+ var at : usize = 0 ;
299+
300+ for (0.. ln ) | i | {
301+ const attribute = try parser .namedNodeMapItem (attributes , @intCast (i )) orelse break ;
302+ names [at ] = try parser .attributeGetName (attribute );
303+ at += 1 ;
304+ }
305+
306+ return names [0.. at ];
307+ }
308+
293309 pub fn _getAttributeNode (self : * parser.Element , name : []const u8 ) ! ? * parser.Attribute {
294310 return try parser .elementGetAttributeNode (self , name );
295311 }
@@ -597,6 +613,7 @@ test "Browser.DOM.Element" {
597613 .{ "let a = document.getElementById('content')" , "undefined" },
598614 .{ "a.hasAttributes()" , "true" },
599615 .{ "a.attributes.length" , "1" },
616+ .{ "a.getAttributeNames()" , "id" },
600617 .{ "a.getAttribute('id')" , "content" },
601618 .{ "a.attributes['id'].value" , "content" },
602619 .{
@@ -615,6 +632,7 @@ test "Browser.DOM.Element" {
615632 .{ "a.setAttribute('foo', 'bar')" , "undefined" },
616633 .{ "a.hasAttribute('foo')" , "true" },
617634 .{ "a.getAttribute('foo')" , "bar" },
635+ .{ "a.getAttributeNames()" , "id,foo" },
618636
619637 .{ "a.setAttribute('foo', 'baz')" , "undefined" },
620638 .{ "a.hasAttribute('foo')" , "true" },
@@ -807,6 +825,7 @@ test "Browser.DOM.Element" {
807825
808826 try runner .testCases (&.{
809827 .{ "const rm = document.createElement('div')" , null },
828+ .{ "rm.getAttributeNames()" , "" },
810829 .{ "rm.id = 'to-remove'" , null },
811830 .{ "document.getElementsByTagName('body')[0].appendChild(rm)" , null },
812831 .{ "document.getElementById('to-remove') != null" , "true" },
0 commit comments