domnodelistreasonbucklescriptbs-webapi

How to get innerHTML of each element in a node list


Say I have the following html:

<div class="item">one</div>
<div class="item">two</div>
<div class="item">three</div>

I would like to log the innerHTML for each element. I have tried the following:

document
|> Document.querySelectorAll(".item")
|> NodeList.toArray /* not sure if this is needed */
|> Array.map(Document.ofNode) /* not sure if this is needed */
|> Array.map(Element.innerHTML)
|> Array.iter((number) => {
  Js.log(number);
});

The error is on line |> Array.map(Element.innerHTML)

And the error is:

This has type:
    array(Webapi.Dom.Element.t) => array(string)
  But somewhere wanted:
    array(Dom.node) => 'a

How can I use the Dom.node so that I access the innerHTML?


Solution

  • Document.ofNode should be Element.ofNode, but the error message indicates that this line isn't present at all in the code producing the error. ofNode also returns an option, since not all nodes are elements, so you have to unwrap that as well. Something like this should work:

    document
    |> Document.querySelectorAll(".item")
    |> NodeList.toArray
    |> Array.map(Element.ofNode)
    |> Array.map(Js.Option.getExn)
    |> Array.map(Element.innerHTML)
    |> Array.iter(Js.log);
    

    Or to avoid a bit of unnecessary array allocation and mapping:

    document
    |> Document.querySelectorAll(".item")
    |> NodeList.toArray
    |> Array.iter(node =>
       node
       |> Element.ofNode
       |> Js.Option.getExn
       |> Element.innerHTML
       |> Js.log
      );
    

    It also has to be said that direct DOM manipulation isn't a great fit for Reason, or any statically typed functional language for that matter, since the DOM is inherently a very dynamically typed and object-oriented API. NodeList.toArray, for example, is required because a NodeList is not an array but an "array-like", which supports only a few array operations.