diff --git a/lib/floki.ex b/lib/floki.ex index fc59bf99..3291e93e 100644 --- a/lib/floki.ex +++ b/lib/floki.ex @@ -308,12 +308,7 @@ defmodule Floki do """ @spec get_by_id(html_tree() | html_node(), String.t()) :: html_node() | nil - def get_by_id(html_tree_as_tuple, id) - when is_list(html_tree_as_tuple) or is_html_node(html_tree_as_tuple) do - html_tree_as_tuple - |> Finder.find(%Floki.Selector{id: id}) - |> List.first() - end + defdelegate get_by_id(html_tree_as_tuple, id), to: Finder, as: :find_by_id @doc """ Changes the attribute values of the elements matched by `selector` diff --git a/lib/floki/finder.ex b/lib/floki/finder.ex index 8e671562..ded1e198 100644 --- a/lib/floki/finder.ex +++ b/lib/floki/finder.ex @@ -9,6 +9,28 @@ defmodule Floki.Finder do alias Selector.PseudoClass import Floki, only: [is_html_node: 1] + @spec find_by_id(Floki.html_tree() | Floki.html_node(), String.t()) :: Floki.html_node() | nil + def find_by_id(html_tree_as_tuple, id) do + html_tree_as_tuple = List.wrap(html_tree_as_tuple) + traverse_find_by_id(html_tree_as_tuple, id) + end + + defp traverse_find_by_id([{_type, _attributes, children} = html_tuple | rest], id) do + if Selector.id_match?(html_tuple, id) do + html_tuple + else + traverse_find_by_id(children, id) || traverse_find_by_id(rest, id) + end + end + + defp traverse_find_by_id([_ | rest], id) do + traverse_find_by_id(rest, id) + end + + defp traverse_find_by_id([], _id) do + nil + end + # Find elements inside a HTML tree. # Second argument can be either a selector string, a selector struct or a list of selector structs. diff --git a/lib/floki/selector.ex b/lib/floki/selector.ex index e34139ec..d38523e1 100644 --- a/lib/floki/selector.ex +++ b/lib/floki/selector.ex @@ -99,8 +99,8 @@ defmodule Floki.Selector do defp can_match_combinator?(_node, _combinator), do: true - defp id_match?(_node, nil), do: true - defp id_match?(node, id), do: attribute_value(node, "id") == id + def id_match?(_node, nil), do: true + def id_match?(node, id), do: attribute_value(node, "id") == id defp namespace_match?(_node, namespace) when is_wildcard(namespace), do: true