diff --git a/README.md b/README.md index 8b6d3d36..223c72ca 100644 --- a/README.md +++ b/README.md @@ -259,9 +259,10 @@ Here you find all the [CSS selectors](https://www.w3.org/TR/selectors/#selectors There are also some selectors based on non-standard specifications. They are: -| Pattern | Description | -|----------------------|-----------------------------------------------------| -| E:fl-contains('foo') | an E element that contains "foo" inside a text node | +| Pattern | Description | +|-----------------------|------------------------------------------------------------------------| +| E:fl-contains('foo') | an E element that contains "foo" inside a text node | +| E:fl-icontains('foo') | an E element that contains "foo" inside a text node (case insensitive) | ## Special thanks diff --git a/lib/floki/selector.ex b/lib/floki/selector.ex index 4e9a671c..f1a1cca8 100644 --- a/lib/floki/selector.ex +++ b/lib/floki/selector.ex @@ -210,6 +210,11 @@ defmodule Floki.Selector do PseudoClass.match_contains?(tree, html_node, pseudo_class) end + # Case insensitive contains + defp pseudo_class_match?(html_node, pseudo_class = %{name: "fl-icontains"}, tree) do + PseudoClass.match_icontains?(tree, html_node, pseudo_class) + end + defp pseudo_class_match?(html_node, %{name: "root"}, tree) do PseudoClass.match_root?(html_node, tree) end diff --git a/lib/floki/selector/pseudo_class.ex b/lib/floki/selector/pseudo_class.ex index 9ceab407..34f488d6 100644 --- a/lib/floki/selector/pseudo_class.ex +++ b/lib/floki/selector/pseudo_class.ex @@ -72,6 +72,21 @@ defmodule Floki.Selector.PseudoClass do res != nil end + # Case insensitive contains + def match_icontains?(tree, html_node, %__MODULE__{value: value}) do + downcase_value = String.downcase(value) + + res = + Enum.find(html_node.children_nodes_ids, fn id -> + case Map.get(tree.nodes, id) do + %Text{content: content} -> String.downcase(content) =~ downcase_value + _ -> false + end + end) + + res != nil + end + defp match_position?(relative_position, value, name) do case value do position when is_integer(position) -> diff --git a/test/floki_test.exs b/test/floki_test.exs index f16f61dc..c0c1c5a2 100644 --- a/test/floki_test.exs +++ b/test/floki_test.exs @@ -1160,6 +1160,14 @@ defmodule FlokiTest do ] end + test "icontains pseudo-class" do + doc = document!(html_body(~s(

One

Two

nothing42
))) + + assert Floki.find(doc, "p:fl-icontains('two')") == [ + {"p", [], ["Two"]} + ] + end + test "contains psuedo-class with substring" do html = document!(