diff --git a/Tests/LibWeb/Text/expected/radio-node-list.txt b/Tests/LibWeb/Text/expected/radio-node-list.txt
new file mode 100644
index 0000000000..66262d0c32
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/radio-node-list.txt
@@ -0,0 +1,47 @@
+ value1 value2 ===================
+non-checked-with-value
+===================
+2
+value1
+value2
+value = ''
+value = ''
+===================
+checked-with-value
+===================
+2
+value3
+value4
+value = 'value3'
+value = 'value3'
+===================
+checked-no-value
+===================
+2
+on
+on
+value = 'on'
+value = 'on'
+===================
+no-radio-button
+===================
+2
+value1
+value2
+value = ''
+value = ''
+===================
+non-checked-with-value
+===================
+value =
+value = value1
+===================
+non-checked-no-value
+===================
+value =
+value = on
+===================
+non-checked-value-on
+===================
+value =
+value = on
diff --git a/Tests/LibWeb/Text/input/radio-node-list.html b/Tests/LibWeb/Text/input/radio-node-list.html
new file mode 100644
index 0000000000..5a0e948988
--- /dev/null
+++ b/Tests/LibWeb/Text/input/radio-node-list.html
@@ -0,0 +1,52 @@
+
+
+
diff --git a/Userland/Libraries/LibWeb/DOM/RadioNodeList.cpp b/Userland/Libraries/LibWeb/DOM/RadioNodeList.cpp
index 2828064410..92db6fef50 100644
--- a/Userland/Libraries/LibWeb/DOM/RadioNodeList.cpp
+++ b/Userland/Libraries/LibWeb/DOM/RadioNodeList.cpp
@@ -8,6 +8,7 @@
#include
#include
#include
+#include
namespace Web::DOM {
@@ -29,4 +30,80 @@ void RadioNodeList::initialize(JS::Realm& realm)
set_prototype(&Bindings::ensure_web_prototype(realm, "RadioNodeList"));
}
+static HTML::HTMLInputElement const* radio_button(Node const& node)
+{
+ if (!is(node))
+ return nullptr;
+
+ auto const& input_element = verify_cast(node);
+ if (input_element.type_state() != HTML::HTMLInputElement::TypeAttributeState::RadioButton)
+ return nullptr;
+
+ return &input_element;
+}
+
+// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#dom-radionodelist-value
+FlyString RadioNodeList::value() const
+{
+ // 1. Let element be the first element in tree order represented by the RadioNodeList object that is an input element whose type
+ // attribute is in the Radio Button state and whose checkedness is true. Otherwise, let it be null.
+ auto* element = verify_cast(first_matching([](Node const& node) -> bool {
+ auto const* button = radio_button(node);
+ if (!button)
+ return false;
+
+ return button->checked();
+ }));
+
+ // 2. If element is null, return the empty string.
+ if (!element)
+ return String {};
+
+ // 3. If element is an element with no value attribute, return the string "on".
+ auto const value = element->get_attribute(HTML::AttributeNames::value);
+ if (value.is_null())
+ return "on"_fly_string;
+
+ // 4. Otherwise, return the value of element's value attribute.
+ return MUST(FlyString::from_deprecated_fly_string(value));
+}
+
+void RadioNodeList::set_value(FlyString const& value)
+{
+ HTML::HTMLInputElement* element = nullptr;
+
+ auto deprecated_value = value.to_deprecated_fly_string();
+
+ // 1. If the new value is the string "on": let element be the first element in tree order represented by the RadioNodeList object
+ // that is an input element whose type attribute is in the Radio Button state and whose value content attribute is either absent,
+ // or present and equal to the new value, if any. If no such element exists, then instead let element be null.
+ if (value == "on"sv) {
+ element = verify_cast(first_matching([&deprecated_value](auto const& node) {
+ auto const* button = radio_button(node);
+ if (!button)
+ return false;
+
+ auto const value = button->get_attribute(HTML::AttributeNames::value);
+ return value.is_null() || value == deprecated_value;
+ }));
+ }
+ // 2. Otherwise: let element be the first element in tree order represented by the RadioNodeList object that is an input element whose
+ // type attribute is in the Radio Button state and whose value content attribute is present and equal to the new value, if any. If
+ // no such element exists, then instead let element be null.
+ else {
+ element = verify_cast(first_matching([&deprecated_value](auto const& node) {
+ auto const* button = radio_button(node);
+ if (!button)
+ return false;
+
+ auto const value = button->get_attribute(HTML::AttributeNames::value);
+ return !value.is_null() && value == deprecated_value;
+ }));
+ }
+
+ // 3. If element is not null, then set its checkedness to true.
+ if (element)
+ element->set_checked(true);
+}
+
}
diff --git a/Userland/Libraries/LibWeb/DOM/RadioNodeList.h b/Userland/Libraries/LibWeb/DOM/RadioNodeList.h
index cd2cb0d202..8204ca7740 100644
--- a/Userland/Libraries/LibWeb/DOM/RadioNodeList.h
+++ b/Userland/Libraries/LibWeb/DOM/RadioNodeList.h
@@ -19,6 +19,9 @@ public:
virtual ~RadioNodeList() override;
+ FlyString value() const;
+ void set_value(FlyString const&);
+
protected:
virtual void initialize(JS::Realm&) override;
diff --git a/Userland/Libraries/LibWeb/DOM/RadioNodeList.idl b/Userland/Libraries/LibWeb/DOM/RadioNodeList.idl
index 66a6ffc47a..e7b2676d27 100644
--- a/Userland/Libraries/LibWeb/DOM/RadioNodeList.idl
+++ b/Userland/Libraries/LibWeb/DOM/RadioNodeList.idl
@@ -3,5 +3,5 @@
// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#radionodelist
[Exposed=Window, UseNewAKString]
interface RadioNodeList : NodeList {
- // FIXME: attribute DOMString value;
+ attribute DOMString value;
};