# Understanding node structure and positions

**URL:** <https://discuss.prosemirror.net/t/understanding-node-structure-and-positions/4038>\
**Category:** Uncategorized\
**Created:** [September 7, 2021, 11:09am UTC](https://discuss.prosemirror.net/t/understanding-node-structure-and-positions/4038 "2021-09-07T11:09:12Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![tslocke](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/tslocke/32/2218_2.png) [@tslocke](https://discuss.prosemirror.net/u/tslocke)\
**Post date:** [September 7, 2021, 11:09am UTC](https://discuss.prosemirror.net/t/understanding-node-structure-and-positions/4038/1 "2021-09-07T11:09:12Z")

</div>

I’m running into trouble writing some fairly simple transaction code, that makes me think I haven’t properly groked the way positions and the node hierarchy work together.

Suppose I have a document with nested block nodes (e.g. list, list items, paragraphs) and I want to set some attribute on every node.

The following code gives me a “No node at given position” RangeError on some documents. I think I’m not correctly taking into account the start and end markers, but I can’t figure out the solution.

```javascript
function addAttr(node, pos, tr) {
  if (node.isBlock) {
    tr.setNodeMarkup(pos, null, {...node.attrs, myAttr: "yup"})
    node.forEach((n, offset) => {
      addAttr(n, pos+offset, tr)
    })
  }
}

```

I kick it off with `addAttr(doc, 0, state.tr)`

Thanks for any advice!

---

<div class="post-metadata">

**Author:** ![tslocke](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/tslocke/32/2218_2.png) [@tslocke](https://discuss.prosemirror.net/u/tslocke)\
**Post date:** [September 7, 2021, 11:32am UTC](https://discuss.prosemirror.net/t/understanding-node-structure-and-positions/4038/2 "2021-09-07T11:32:16Z")

</div>

The solution I’ve come to, though I’m not super confident about it, is

```auto
function addAttr(node, pos, tr) {
  if (node.isBlock) {
    tr.setNodeMarkup(pos-1, null, {...node.attrs, myAttr: "yup"}) // Note -1
    node.forEach((n, offset) => {
      addAttr(n, pos+offset+1, tr) // add 1 for the block node start
    })
  }
}

```

(I’ve also noticed there is `node.descendants` which would be better here, but it would still be useful to know if the logic is correct)

---

<div class="post-metadata">

**Author:** ![bhl](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/bhl/32/4074_2.png) [@bhl](https://discuss.prosemirror.net/u/bhl)\
**Post date:** [September 9, 2021, 6:38am UTC](https://discuss.prosemirror.net/t/understanding-node-structure-and-positions/4038/3 "2021-09-09T06:38:30Z")

</div>

I would look at `Fragment.nodesBetween`, which is what `Node.descendants` calls.

> <https://github.com/ProseMirror/prosemirror-model/blob/5ff774fa853dd63ecf30e42ff487b111b3c1c5fd/src/fragment.js#L23-L34>

---

<div class="post-metadata">

**Author:** ![TeemuKoivisto](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/teemukoivisto/32/1185_2.png) [@TeemuKoivisto](https://discuss.prosemirror.net/u/TeemuKoivisto)\
**Post date:** [September 9, 2021, 7:03am UTC](https://discuss.prosemirror.net/t/understanding-node-structure-and-positions/4038/4 "2021-09-09T07:03:17Z")

</div>

Having done quite similar things, it goes something like

```auto
doc.nodesBetween(blockPos, blockPos + blockNode.nodeSize, (node, pos) => {
  if (node.isBlock) {
    tr.setNodeMarkup(pos, undefined, {...node.attrs, myAttr: "yup"}, node.marks)
  }
})

```

This will start iterating from the node at that position so you won’t have to set its attributes separately.

---

<div class="post-metadata">

**Author:** ![marijn](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/marijn/32/15_2.png) [@marijn](https://discuss.prosemirror.net/u/marijn)\
**Post date:** [September 9, 2021, 8:15am UTC](https://discuss.prosemirror.net/t/understanding-node-structure-and-positions/4038/5 "2021-09-09T08:15:57Z")

</div>

Your original code was indeed ignoring the 1 token for the opening node. However, your new code still looks like it will try to set an attribute on the top document node at -1, which won’t work. Also there seems to be remaining confusion about where to add the extra 1 position for the opening token—`pos - 1` suggests you’re passing the position of the start of the node’s content to your recursive function, but then you add `+ 1` in the `forEach`, which only makes sense if `pos` is the node’s actual start position.

---

<div class="post-metadata">

**Author:** ![tslocke](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/tslocke/32/2218_2.png) [@tslocke](https://discuss.prosemirror.net/u/tslocke)\
**Post date:** [September 11, 2021, 3:37pm UTC](https://discuss.prosemirror.net/t/understanding-node-structure-and-positions/4038/6 "2021-09-11T15:37:44Z")

</div>

I think I’ve figured this out now. If you want to walk the node tree with a recursive function, passing a node and its start position on each call, then you naturally want to start the recursion with `doc` as the root node. But the doc node is unlike other nodes in that there is no opening node position, so you either need some conditional logic to only `+1` when the node is not the document, or simply pass `-1` as the initial value.

@marijn point taken about setting attributes on the document. That was only a problem with my simplified example code.
