# `Premailex.DOM`
[🔗](https://github.com/danschultzer/premailex/blob/v1.0.1/lib/premailex/dom.ex#L1)

DOM manipulation.

Supports the following pseudo-classes: `:first-child`, `:last-child`, `:only-child`, `:first-of-type`, `:last-of-type`, `:only-of-type`, `:empty`, `:root`, `:nth-child`, `:nth-of-type`, `:nth-last-child`, `:nth-last-of-type`.

Functional pseudo-classes that take arguments (e.g. `:nth-child`) support
the An+B syntax.

## Selector support limitations

  * Pseudo-classes that are not supported will never match and will emit a
    debug log.

  * Functional pseudo-classes that take arguments do not support the
    `of <complex-selector-list>` clause; the entire expression is treated as
    invalid and the rule is ignored.

  * The column combinator (`||`) parses but never matches and emits a debug
    log.

# `needle`

```elixir
@type needle() :: Premailex.html_element() | tag_name() | :comment
```

# `selector`

```elixir
@type selector() :: String.t()
```

# `selector_item`

```elixir
@type selector_item() :: %{:selector =&gt; selector(), optional(any()) =&gt; any()}
```

A selector item is a map that contains a `:selector` key.

Any additional keys attached by the caller are preserved during traversal
and returned as part of the matched selector items in the
`traverse_with_matching_items/3` callback.

# `tag_name`

```elixir
@type tag_name() :: String.t()
```

# `all`

```elixir
@spec all(Premailex.html_tree() | Premailex.html_element(), selector()) :: [
  Premailex.html_element()
]
```

Returns a list of HTML elements matching the selector.

## Examples

    iex> Premailex.DOM.all([{"html", [], [{"head", [], []}, {"body", [], [{"h1", [], ["Title"]}]}]}], "h1")
    [{"h1", [], ["Title"]}]

    iex> Premailex.DOM.all({"div", [], [{"p", [], ["a"]}, {"p", [], ["b"]}]}, "p")
    [{"p", [], ["a"]}, {"p", [], ["b"]}]

# `reject`

```elixir
@spec reject(Premailex.html_tree() | Premailex.html_element(), selector()) ::
  Premailex.html_tree()
```

Filters HTML elements matching the selector from the tree, collapsing
redundant whitespace text nodes left behind.

Always returns a `t:Premailex.html_tree/0`, even when a single element is
passed in; if that element matches the selector, the result is `[]`.

## Examples

    iex> Premailex.DOM.reject([{"html", [], [{"head", [], []}, {"body", [], [{"h1", [], ["Title"]}]}]}], "h1")
    [{"html", [], [{"head", [], []}, {"body", [], []}]}]

    iex> Premailex.DOM.reject({"div", [], [{"p", [], ["a"]}, {"h1", [], ["b"]}]}, "h1")
    [{"div", [], [{"p", [], ["a"]}]}]

    iex> Premailex.DOM.reject({"h1", [], ["Title"]}, "h1")
    []

# `replace_all_matches`

```elixir
@spec replace_all_matches(
  Premailex.html_tree() | Premailex.html_element(),
  needle() | [needle()],
  (Premailex.html_node() -&gt; Premailex.html_node())
) :: Premailex.html_tree() | Premailex.html_element()
```

Traverses HTML elements searching for needles and calls the provided function
on each match.

The elements are traversed depth-first, replacing each matching node with the
function's return value.

## Examples

    iex> Premailex.DOM.replace_all_matches([{"div", [], [{"p", [], ["First paragraph"]}, {"p", [], ["Second paragraph"]}]}], "p", fn {name, attrs, _children} -> {name, attrs, ["Updated"]} end)
    [{"div", [], [{"p", [], ["Updated"]}, {"p", [], ["Updated"]}]}]

    iex> Premailex.DOM.replace_all_matches({"div", [], [{"p", [], ["First paragraph"]}, {"p", [], ["Second paragraph"]}]}, {"p", [], ["Second paragraph"]}, fn {name, attrs, _children} -> {name, attrs, ["Updated"]} end)
    {"div", [], [{"p", [], ["First paragraph"]}, {"p", [], ["Updated"]}]}

    iex> Premailex.DOM.replace_all_matches([{"div", [], [{:comment, "This is a comment"}, {"p", [], ["Paragraph"]}]}], :comment, fn {:comment, _comment} -> {:comment, "Updated"} end)
    [{"div", [], [{:comment, "Updated"}, {"p", [], ["Paragraph"]}]}]

# `replace_first_match`

```elixir
@spec replace_first_match(
  Premailex.html_tree() | Premailex.html_element(),
  needle(),
  (Premailex.html_node() -&gt; Premailex.html_node())
) :: Premailex.html_tree() | Premailex.html_element()
```

Traverses HTML elements until the first match of a needle.

The elements are traversed depth-first, and the first matching node is
replaced with the function's return value.

## Examples

    iex> Premailex.DOM.replace_first_match([{"div", [], [{"p", [], ["First paragraph"]}, {"p", [], ["Second paragraph"]}]}], "p", fn {name, attrs, _children} -> {name, attrs, ["Updated"]} end)
    [{"div", [], [{"p", [], ["Updated"]}, {"p", [], ["Second paragraph"]}]}]

# `text_content`

```elixir
@spec text_content(Premailex.html_tree() | Premailex.html_element()) :: String.t()
```

Extracts and concatenates the text content from HTML elements.

## Examples

    iex> Premailex.DOM.text_content([{"html", [], [{"head", [], []}, {"body", [], [{"h1", [], ["Title"]}]}]}])
    "Title"

# `traverse_with_matching_items`

```elixir
@spec traverse_with_matching_items(
  Premailex.html_tree() | Premailex.html_element(),
  [selector_item()],
  (Premailex.html_element(), [selector_item(), ...] -&gt; Premailex.html_element())
) :: Premailex.html_tree() | Premailex.html_element()
```

Traverses HTML elements, calling the provided function on each element
matching one or more selector items and replacing it with the function's
return value.

## Examples

    iex> Premailex.DOM.traverse_with_matching_items(
    ...>   {"div", [], [{"p", [], ["Hi"]}]},
    ...>   [%{selector: "p"}],
    ...>   fn {tag, attrs, children}, _matched ->
    ...>     {tag, [{"matched", ""} | attrs], children}
    ...>   end)
    {"div", [], [{"p", [{"matched", ""}], ["Hi"]}]}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
