From 2f4d8ecb7aca1d9254c05aeb144dc4cc936c389b Mon Sep 17 00:00:00 2001 From: Vinicius Ferreira Negrisolo Date: Sun, 4 Oct 2020 17:29:03 -0400 Subject: [PATCH] Add :disabled pseudo selector --- README.md | 1 + lib/floki/selector.ex | 4 +++ lib/floki/selector/pseudo_class.ex | 13 +++++++++ test/floki_test.exs | 42 ++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+) diff --git a/README.md b/README.md index 0de8f2c4..4b5aec5b 100644 --- a/README.md +++ b/README.md @@ -241,6 +241,7 @@ Here you find all the [CSS selectors](https://www.w3.org/TR/selectors/#selectors | E:first-of-type | an E element, first child of its type among its siblings | | E:last-of-type | an E element, last child of its type among its siblings | | E:checked | An E element (checkbox, radio, or option) that is checked | +| E:disabled | An E element (button, input, select, textarea, or option) that is disabled | | E.warning | an E element whose class is "warning" | | E#myid | an E element with ID equal to "myid" | | E:not(s) | an E element that does not match simple selector s | diff --git a/lib/floki/selector.ex b/lib/floki/selector.ex index c5b14960..78595ef0 100644 --- a/lib/floki/selector.ex +++ b/lib/floki/selector.ex @@ -201,6 +201,10 @@ defmodule Floki.Selector do PseudoClass.match_checked?(html_node) end + defp pseudo_class_match?(html_node, %{name: "disabled"}, _tree) do + PseudoClass.match_disabled?(html_node) + end + defp pseudo_class_match?(html_node, pseudo_class = %{name: "fl-contains"}, tree) do PseudoClass.match_contains?(tree, html_node, pseudo_class) end diff --git a/lib/floki/selector/pseudo_class.ex b/lib/floki/selector/pseudo_class.ex index 8219f34a..18c0fd8c 100644 --- a/lib/floki/selector/pseudo_class.ex +++ b/lib/floki/selector/pseudo_class.ex @@ -131,6 +131,19 @@ defmodule Floki.Selector.PseudoClass do false end + @disableable_html_nodes ~w[button input select option textarea] + + def match_disabled?(%{type: type} = html_node) when type in @disableable_html_nodes do + case List.keyfind(html_node.attributes, "disabled", 0) do + {"disabled", _} -> true + _ -> false + end + end + + def match_disabled?(_html_node) do + false + end + defp node_position(ids, %HTMLNode{node_id: node_id}) do {_node_id, position} = Enum.find(ids, fn {id, _} -> id == node_id end) diff --git a/test/floki_test.exs b/test/floki_test.exs index f352f4be..bf11b30d 100644 --- a/test/floki_test.exs +++ b/test/floki_test.exs @@ -986,6 +986,48 @@ defmodule FlokiTest do ] = Floki.find(doc, ":checked") end + test "disabled pseudo-class" do + doc = + document!( + html_body(~s""" + + + + + + + + + + + + + + + + + + + + """) + ) + + assert [ + {"button", [{"disabled", _}], ["button 1"]}, + {"button", [{"disabled", _}], ["button 2"]}, + {"input", [{"type", "text"}, {"name", "text 1"}, {"disabled", _}], []}, + {"input", [{"type", "text"}, {"name", "text 2"}, {"disabled", _}], []}, + {"select", [{"name", "select 1"}, {"disabled", _}], + [{"option", [{"value", "option 1"}], ["Option 1"]}]}, + {"select", [{"name", "select 2"}, {"disabled", _}], + [{"option", [{"value", "option 2"}], ["Option 2"]}]}, + {"option", [{"value", "option 4"}, {"disabled", _}], ["Option 4"]}, + {"option", [{"value", "option 5"}, {"disabled", _}], ["Option 5"]}, + {"textarea", [{"name", "text area 1"}, {"disabled", _}], ["Text Area 1"]}, + {"textarea", [{"name", "text area 2"}, {"disabled", _}], ["Text Area 2"]} + ] = Floki.find(doc, ":disabled") + end + # Floki.find/2 - XML and invalid HTML test "get elements inside a XML structure" do