diff --git a/Tests/LibWeb/Text/expected/selectionchange-event.txt b/Tests/LibWeb/Text/expected/selectionchange-event.txt
new file mode 100644
index 0000000000..022b8325cb
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/selectionchange-event.txt
@@ -0,0 +1,6 @@
+
+ Ladybird is an ongoing project to build an independent web browser from scratch.
+ Events:
+selectionchange anchorNode=<#text > anchorOffset=1 type=Caret focusNode=<#text > focusOffset=1 isCollapsed=true
+selectionchange anchorNode=<#text > anchorOffset=1 type=Caret focusNode=<#text > focusOffset=1 isCollapsed=true
+selectionchange anchorNode=<#text > anchorOffset=2 type=Caret focusNode=<#text > focusOffset=2 isCollapsed=true
diff --git a/Tests/LibWeb/Text/input/selectionchange-event.html b/Tests/LibWeb/Text/input/selectionchange-event.html
new file mode 100644
index 0000000000..b3b1fb8dbb
--- /dev/null
+++ b/Tests/LibWeb/Text/input/selectionchange-event.html
@@ -0,0 +1,46 @@
+
+
+
+ Ladybird is an ongoing project to build an independent web browser from scratch.
+
+
diff --git a/Userland/Libraries/LibWeb/DOM/EventHandler.idl b/Userland/Libraries/LibWeb/DOM/EventHandler.idl
index 00af7c66f5..afdc2d0082 100644
--- a/Userland/Libraries/LibWeb/DOM/EventHandler.idl
+++ b/Userland/Libraries/LibWeb/DOM/EventHandler.idl
@@ -70,6 +70,7 @@ interface mixin GlobalEventHandlers {
attribute EventHandler onseeked;
attribute EventHandler onseeking;
attribute EventHandler onselect;
+ attribute EventHandler onselectionchange;
attribute EventHandler onslotchange;
attribute EventHandler onstalled;
attribute EventHandler onsubmit;
diff --git a/Userland/Libraries/LibWeb/DOM/Range.cpp b/Userland/Libraries/LibWeb/DOM/Range.cpp
index 2bdce1eaa0..75183ddaaf 100644
--- a/Userland/Libraries/LibWeb/DOM/Range.cpp
+++ b/Userland/Libraries/LibWeb/DOM/Range.cpp
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -100,6 +101,20 @@ void Range::update_associated_selection()
layout_root->recompute_selection_states();
layout_root->paintable()->set_needs_display();
}
+
+ // https://w3c.github.io/selection-api/#selectionchange-event
+ // When the selection is dissociated with its range, associated with a new range or the associated range's boundary
+ // point is mutated either by the user or the content script, the user agent must queue a task on the user interaction
+ // task source to fire an event named selectionchange, which does not bubble and is not cancelable, at the document
+ // associated with the selection.
+ auto document = m_associated_selection->document();
+ queue_global_task(HTML::Task::Source::UserInteraction, relevant_global_object(*document), [document] {
+ EventInit event_init;
+ event_init.bubbles = false;
+ event_init.cancelable = false;
+ auto event = DOM::Event::create(document->realm(), HTML::EventNames::selectionchange, event_init);
+ document->dispatch_event(event);
+ });
}
// https://dom.spec.whatwg.org/#concept-range-root
diff --git a/Userland/Libraries/LibWeb/HTML/AttributeNames.h b/Userland/Libraries/LibWeb/HTML/AttributeNames.h
index b14e9c61c8..d26321d555 100644
--- a/Userland/Libraries/LibWeb/HTML/AttributeNames.h
+++ b/Userland/Libraries/LibWeb/HTML/AttributeNames.h
@@ -180,6 +180,7 @@ namespace AttributeNames {
__ENUMERATE_HTML_ATTRIBUTE(onseeked) \
__ENUMERATE_HTML_ATTRIBUTE(onseeking) \
__ENUMERATE_HTML_ATTRIBUTE(onselect) \
+ __ENUMERATE_HTML_ATTRIBUTE(onselectionchange) \
__ENUMERATE_HTML_ATTRIBUTE(onslotchange) \
__ENUMERATE_HTML_ATTRIBUTE(onstalled) \
__ENUMERATE_HTML_ATTRIBUTE(onstorage) \
diff --git a/Userland/Libraries/LibWeb/HTML/EventNames.h b/Userland/Libraries/LibWeb/HTML/EventNames.h
index cbf05baeb2..bd162483e6 100644
--- a/Userland/Libraries/LibWeb/HTML/EventNames.h
+++ b/Userland/Libraries/LibWeb/HTML/EventNames.h
@@ -88,6 +88,7 @@ namespace Web::HTML::EventNames {
__ENUMERATE_HTML_EVENT(scroll) \
__ENUMERATE_HTML_EVENT(scrollend) \
__ENUMERATE_HTML_EVENT(securitypolicyviolation) \
+ __ENUMERATE_HTML_EVENT(selectionchange) \
__ENUMERATE_HTML_EVENT(seeked) \
__ENUMERATE_HTML_EVENT(seeking) \
__ENUMERATE_HTML_EVENT(select) \
diff --git a/Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.h b/Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.h
index a7e1dc7070..9206c140fb 100644
--- a/Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.h
+++ b/Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.h
@@ -63,6 +63,7 @@
E(onseeked, HTML::EventNames::seeked) \
E(onseeking, HTML::EventNames::seeking) \
E(onselect, HTML::EventNames::select) \
+ E(onselectionchange, HTML::EventNames::selectionchange) \
E(onslotchange, HTML::EventNames::slotchange) \
E(onstalled, HTML::EventNames::stalled) \
E(onsubmit, HTML::EventNames::submit) \