1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 18:47:34 +00:00

LibWeb: Add document.querySelector()

This commit is contained in:
Linus Groh 2020-05-25 21:12:00 +01:00 committed by Andreas Kling
parent 6e505b853e
commit 67b742bf32
8 changed files with 46 additions and 5 deletions

View file

@ -9,7 +9,7 @@
function drawSomeCurves() { function drawSomeCurves() {
var canvas = document.querySelectorAll("canvas")[0]; var canvas = document.querySelector("canvas");
var ctx = canvas.getContext("2d"); var ctx = canvas.getContext("2d");
var x = 150; var x = 150;
var y = 150; var y = 150;

View file

@ -9,7 +9,7 @@
function drawHouse() { function drawHouse() {
var ctx = document.querySelectorAll("canvas")[0].getContext("2d"); var ctx = document.querySelector("canvas").getContext("2d");
ctx.fillStyle = 'black'; ctx.fillStyle = 'black';
ctx.fillRect(0, 0, 300, 300); ctx.fillRect(0, 0, 300, 300);

View file

@ -9,10 +9,10 @@
</style> </style>
<script> <script>
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function() {
img = document.querySelectorAll("img")[0]; img = document.querySelector("img");
img.addEventListener("load", function() { img.addEventListener("load", function() {
var ctx = document.querySelectorAll("canvas")[0].getContext("2d"); var ctx = document.querySelector("canvas").getContext("2d");
ctx.fillStyle = '#666'; ctx.fillStyle = '#666';
ctx.fillRect(0, 0, 400, 400); ctx.fillRect(0, 0, 400, 400);
ctx.scale(2, 2); ctx.scale(2, 2);

View file

@ -12,7 +12,7 @@
<div id="set_href">Click me to set location.href!</div> <div id="set_href">Click me to set location.href!</div>
<div id="reload">Click me to call location.reload()!</div> <div id="reload">Click me to call location.reload()!</div>
<script> <script>
var pre = document.querySelectorAll("pre")[0]; var pre = document.querySelector("pre");
pre.innerHTML += "href: " + location.href + '\n'; pre.innerHTML += "href: " + location.href + '\n';
pre.innerHTML += "protocol: " + location.protocol + '\n'; pre.innerHTML += "protocol: " + location.protocol + '\n';
pre.innerHTML += "host: " + location.host + '\n'; pre.innerHTML += "host: " + location.host + '\n';

View file

@ -41,6 +41,7 @@ DocumentWrapper::DocumentWrapper(Document& document)
: NodeWrapper(document) : NodeWrapper(document)
{ {
put_native_function("getElementById", get_element_by_id, 1); put_native_function("getElementById", get_element_by_id, 1);
put_native_function("querySelector", query_selector, 1);
put_native_function("querySelectorAll", query_selector_all, 1); put_native_function("querySelectorAll", query_selector_all, 1);
} }
@ -86,6 +87,23 @@ JS::Value DocumentWrapper::get_element_by_id(JS::Interpreter& interpreter)
return wrap(interpreter.heap(), const_cast<Element&>(*element)); return wrap(interpreter.heap(), const_cast<Element&>(*element));
} }
JS::Value DocumentWrapper::query_selector(JS::Interpreter& interpreter)
{
auto* document = document_from(interpreter);
if (!document)
return {};
if (!interpreter.argument_count())
return interpreter.throw_exception<JS::TypeError>("querySelector() needs one argument");
auto selector = interpreter.argument(0).to_string(interpreter);
if (interpreter.exception())
return {};
// FIXME: Throw if selector is invalid
auto element = document->query_selector(selector);
if (!element)
return JS::js_null();
return wrap(interpreter.heap(), *element);
}
JS::Value DocumentWrapper::query_selector_all(JS::Interpreter& interpreter) JS::Value DocumentWrapper::query_selector_all(JS::Interpreter& interpreter)
{ {
auto* document = document_from(interpreter); auto* document = document_from(interpreter);
@ -96,6 +114,7 @@ JS::Value DocumentWrapper::query_selector_all(JS::Interpreter& interpreter)
auto selector = interpreter.argument(0).to_string(interpreter); auto selector = interpreter.argument(0).to_string(interpreter);
if (interpreter.exception()) if (interpreter.exception())
return {}; return {};
// FIXME: Throw if selector is invalid
auto elements = document->query_selector_all(selector); auto elements = document->query_selector_all(selector);
// FIXME: This should be a static NodeList, not a plain JS::Array. // FIXME: This should be a static NodeList, not a plain JS::Array.
auto* node_list = JS::Array::create(interpreter.global_object()); auto* node_list = JS::Array::create(interpreter.global_object());

View file

@ -43,6 +43,7 @@ private:
virtual const char* class_name() const override { return "DocumentWrapper"; } virtual const char* class_name() const override { return "DocumentWrapper"; }
static JS::Value get_element_by_id(JS::Interpreter&); static JS::Value get_element_by_id(JS::Interpreter&);
static JS::Value query_selector(JS::Interpreter&);
static JS::Value query_selector_all(JS::Interpreter&); static JS::Value query_selector_all(JS::Interpreter&);
}; };

View file

@ -321,6 +321,26 @@ Vector<const Element*> Document::get_elements_by_name(const String& name) const
return elements; return elements;
} }
RefPtr<Element> Document::query_selector(const StringView& selector_text)
{
auto selector = parse_selector(selector_text);
if (!selector.has_value())
return {};
dump_selector(selector.value());
RefPtr<Element> result;
for_each_in_subtree_of_type<Element>([&](auto& element) {
if (SelectorEngine::matches(selector.value(), element)) {
result = element;
return IterationDecision::Break;
}
return IterationDecision::Continue;
});
return result;
}
NonnullRefPtrVector<Element> Document::query_selector_all(const StringView& selector_text) NonnullRefPtrVector<Element> Document::query_selector_all(const StringView& selector_text)
{ {
auto selector = parse_selector(selector_text); auto selector = parse_selector(selector_text);

View file

@ -116,6 +116,7 @@ public:
void schedule_style_update(); void schedule_style_update();
Vector<const Element*> get_elements_by_name(const String&) const; Vector<const Element*> get_elements_by_name(const String&) const;
RefPtr<Element> query_selector(const StringView&);
NonnullRefPtrVector<Element> query_selector_all(const StringView&); NonnullRefPtrVector<Element> query_selector_all(const StringView&);
const String& source() const { return m_source; } const String& source() const { return m_source; }