diff --git a/Tests/LibWeb/Text/expected/document-scrollingElement-quirks-mode.txt b/Tests/LibWeb/Text/expected/document-scrollingElement-quirks-mode.txt
new file mode 100644
index 0000000000..ef65c16053
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/document-scrollingElement-quirks-mode.txt
@@ -0,0 +1,2 @@
+document.compatMode: BackCompat
+document.scrollingElement is body element: true
diff --git a/Tests/LibWeb/Text/expected/document-scrollingElement.txt b/Tests/LibWeb/Text/expected/document-scrollingElement.txt
new file mode 100644
index 0000000000..3b1db7c098
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/document-scrollingElement.txt
@@ -0,0 +1,2 @@
+document.compatMode: CSS1Compat
+document.scrollingElement is document root element: true
diff --git a/Tests/LibWeb/Text/input/document-scrollingElement-quirks-mode.html b/Tests/LibWeb/Text/input/document-scrollingElement-quirks-mode.html
new file mode 100644
index 0000000000..2d187184ca
--- /dev/null
+++ b/Tests/LibWeb/Text/input/document-scrollingElement-quirks-mode.html
@@ -0,0 +1,8 @@
+
+
+
diff --git a/Tests/LibWeb/Text/input/document-scrollingElement.html b/Tests/LibWeb/Text/input/document-scrollingElement.html
new file mode 100644
index 0000000000..4d5252400b
--- /dev/null
+++ b/Tests/LibWeb/Text/input/document-scrollingElement.html
@@ -0,0 +1,8 @@
+
+
+
diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp
index e99ff8728d..09829c76d8 100644
--- a/Userland/Libraries/LibWeb/DOM/Document.cpp
+++ b/Userland/Libraries/LibWeb/DOM/Document.cpp
@@ -3857,4 +3857,26 @@ Vector> Document::elements_from_point(double x, double
return sequence;
}
+// https://drafts.csswg.org/cssom-view/#dom-document-scrollingelement
+JS::GCPtr Document::scrolling_element() const
+{
+ // 1. If the Document is in quirks mode, follow these substeps:
+ if (in_quirks_mode()) {
+ // 1. If the body element exists, and it is not potentially scrollable, return the body element and abort these steps.
+ // For this purpose, a value of overflow:clip on the the body element’s parent element must be treated as overflow:hidden.
+ if (auto const* body_element = body(); body_element && !body_element->is_potentially_scrollable())
+ return body_element;
+
+ // 2. Return null and abort these steps.
+ return nullptr;
+ }
+
+ // 2. If there is a root element, return the root element and abort these steps.
+ if (auto const* root_element = document_element(); root_element)
+ return root_element;
+
+ // 3. Return null.
+ return nullptr;
+}
+
}
diff --git a/Userland/Libraries/LibWeb/DOM/Document.h b/Userland/Libraries/LibWeb/DOM/Document.h
index 67b10b10a7..21af6ab7b1 100644
--- a/Userland/Libraries/LibWeb/DOM/Document.h
+++ b/Userland/Libraries/LibWeb/DOM/Document.h
@@ -568,6 +568,7 @@ public:
Element const* element_from_point(double x, double y);
Vector> elements_from_point(double x, double y);
+ JS::GCPtr scrolling_element() const;
void set_needs_to_resolve_paint_only_properties() { m_needs_to_resolve_paint_only_properties = true; }
diff --git a/Userland/Libraries/LibWeb/DOM/Document.idl b/Userland/Libraries/LibWeb/DOM/Document.idl
index c89e48ef3c..1c70fd9d6d 100644
--- a/Userland/Libraries/LibWeb/DOM/Document.idl
+++ b/Userland/Libraries/LibWeb/DOM/Document.idl
@@ -122,6 +122,7 @@ interface Document : Node {
// https://drafts.csswg.org/cssom-view/#extensions-to-the-document-interface
Element? elementFromPoint(double x, double y);
sequence elementsFromPoint(double x, double y);
+ readonly attribute Element? scrollingElement;
};
dictionary ElementCreationOptions {