dom: implement basic queryselector for element

This commit is contained in:
Pierre Tachoire
2023-12-14 16:41:35 +01:00
parent 76bdd94a3c
commit 459a46a553

View File

@@ -9,6 +9,7 @@ const checkCases = jsruntime.test_utils.checkCases;
const collection = @import("html_collection.zig"); const collection = @import("html_collection.zig");
const Node = @import("node.zig").Node; const Node = @import("node.zig").Node;
const Walker = @import("html_collection.zig").WalkerDepthFirst;
const NodeList = @import("nodelist.zig").NodeList; const NodeList = @import("nodelist.zig").NodeList;
const HTMLElem = @import("../html/elements.zig"); const HTMLElem = @import("../html/elements.zig");
pub const Union = @import("../html/elements.zig").Union; pub const Union = @import("../html/elements.zig").Union;
@@ -192,6 +193,74 @@ pub const Element = struct {
return try HTMLElem.toInterface(HTMLElem.Union, res.?); return try HTMLElem.toInterface(HTMLElem.Union, res.?);
} }
fn getElementById(self: *parser.Element, id: []const u8) !?*parser.Node {
// walk over the node tree fo find the node by id.
const root = parser.elementToNode(self);
const walker = Walker{};
var next: ?*parser.Node = null;
while (true) {
next = try walker.get_next(root, next) orelse return null;
// ignore non-element nodes.
if (try parser.nodeType(next.?) != .element) {
continue;
}
const e = parser.nodeToElement(next.?);
if (std.mem.eql(u8, id, try get_id(e))) return next;
}
}
// TODO netsurf doesn't handle query selectors. We have to implement a
// solution by ourselves.
// We handle only * and single id selector like `#foo`.
pub fn _querySelector(self: *parser.Element, selectors: []const u8) !?Union {
if (selectors.len == 0) return null;
// catch-all, return the firstElementChild
if (selectors[0] == '*') return try get_firstElementChild(self);
// support only simple id selector.
if (selectors[0] != '#' or std.mem.indexOf(u8, selectors, " ") != null) return null;
// walk over the node tree fo find the node by id.
const n = try getElementById(self, selectors[1..]) orelse return null;
return try toInterface(parser.nodeToElement(n));
}
// TODO netsurf doesn't handle query selectors. We have to implement a
// solution by ourselves.
// We handle only * and single id selector like `#foo`.
pub fn _querySelectorAll(self: *parser.Element, alloc: std.mem.Allocator, selectors: []const u8) !*NodeList {
const list = try NodeList.init(alloc);
errdefer list.deinit(alloc);
if (selectors.len == 0) return list;
// catch-all, return all elements
if (selectors[0] == '*') {
// walk over the node tree fo find the node by id.
const root = parser.elementToNode(self);
const walker = Walker{};
var next: ?*parser.Node = null;
while (true) {
next = try walker.get_next(root, next) orelse return list;
// ignore non-element nodes.
if (try parser.nodeType(next.?) != .element) {
continue;
}
try list.append(next.?);
}
}
// support only simple id selector.
if (selectors[0] != '#' or std.mem.indexOf(u8, selectors, " ") != null) return list;
// walk over the node tree fo find the node by id.
const n = try getElementById(self, selectors[1..]) orelse return list;
try list.append(n);
return list;
}
pub fn deinit(_: *parser.Element, _: std.mem.Allocator) void {} pub fn deinit(_: *parser.Element, _: std.mem.Allocator) void {}
}; };
@@ -279,4 +348,22 @@ pub fn testExecFn(
.{ .src = "d.nextElementSibling", .ex = "null" }, .{ .src = "d.nextElementSibling", .ex = "null" },
}; };
try checkCases(js_env, &elementSibling); try checkCases(js_env, &elementSibling);
var querySelector = [_]Case{
.{ .src = "let e = document.getElementById('content')", .ex = "undefined" },
.{ .src = "e.querySelector('foo')", .ex = "null" },
.{ .src = "e.querySelector('#foo')", .ex = "null" },
.{ .src = "e.querySelector('#link').id", .ex = "link" },
.{ .src = "e.querySelector('#para').id", .ex = "para" },
.{ .src = "e.querySelector('*').id", .ex = "link" },
.{ .src = "e.querySelectorAll('foo').length", .ex = "0" },
.{ .src = "e.querySelectorAll('#foo').length", .ex = "0" },
.{ .src = "e.querySelectorAll('#link').length", .ex = "1" },
.{ .src = "e.querySelectorAll('#link').item(0).id", .ex = "link" },
.{ .src = "e.querySelectorAll('#para').length", .ex = "1" },
.{ .src = "e.querySelectorAll('#para').item(0).id", .ex = "para" },
.{ .src = "e.querySelectorAll('*').length", .ex = "4" },
};
try checkCases(js_env, &querySelector);
} }