Updating node attributes after rendering

I’m looking for an elegant way to set attributes for a video tag after it has been attached to the DOM to adjust its size. I’m a bit puzzled what’s the best way to accomplish this. I tried creating a node view for it but I’m not sure how to determine when the rendering happens for this.dom. Or do I need to implement this with MutationObserver?

Welcome @jorilallo =)

Not mutation observer!

If I understand you correctly, everything can be done with Transforms (https://prosemirror.net/docs/ref/#transform)

Here’s an example that may work.

const transaction = state.tr.setNodeMarkup(
          getPos(), // For custom node views, this function is passed into the constructor.  It'll return the position of the node in the document.
          undefined, // No node type change
          newAttrs // Replace (update) attributes to your `video` block here
)
view.dispatch(transaction)

The way I got your specific use case working was adding event listeners to this.dom via node view. Mousemove, mouseup and mousedown will all need to be implemented.

But in short, on mousemove, element width/height dimension is updated directly (element.style.width = Xpx) and on mouseup, the new attributes are built and a transaction is dispatched with the final resized value (the size … height / width and anything else.) These attributes need to be stored as an attribute on the video schema and utilized to render to the DOM. So after the transform, the DOM state mirrors what prosemirror renders.

Let me know if you need more help or if the above doesnt work.

I assume you mean DOM attributes? ProseMirror doesn’t currently have any feature where it’ll notify a node view that it has been attached to the DOM, so you’d have to kludge this with a call to requestAnimationFrame when you create or update the DOM structure.

Exactly that, was just curious if there would have been a more native way. Thanks for the answer and happy holidays!

Hello guys, I used the approach you mentioned @bZichett. However after dispatching that transaction, the updated node became the selected node. Is it possible to update the node attributes without changing the selection?

Thanks

Could you show a code snippet to give us a bit more to go on?

Sure, what I am trying to achieve is update a node when I focus out of the editor (like clicking outside of the editor container). In the focusout callback I have a logic like this

    let tr = this.state.tr.setNodeMarkup(nodeSelection.from, undefined, mergedAttrs);
    this.dispatch(tr);

This works however after the transaction, my editor is again focused at the position of the node that I updated(nodeSelection) .

1 Like

Making document changes based on the focus state of the view doesn’t seem like a good idea. Maybe set or remove node decorations, or even just rely on the presence of the ProseMirror-focused CSS class if at all possible.

1 Like

Would be curious to hear precisely what @vmurillo is going for - it’s highly likely @marijn’s advice would work.

But using node decorations incurs its own complexity / cost (maybe minimal) - perhaps a permanent change to the document is intended (maybe an inline “bookmark” attribute where a particular user’s caret was last observed - kind of interesting, but possibly more approachable with decorations. It reminds me somewhat of the two approaches to inline annotations.

Anyway, its possible the bug (?) might be in improperly reached syncNodeSelection / setCurSelection or if the top branch is traversed, selectionToDom, in the particular case where view.focused is false; @marijn likely to know immediately.

if (forceSelUpdate ||
        !(this.mouseDown && this.domObserver.currentSelection.eq(this.root.getSelection()) && anchorInRightPlace(this))) {
      selectionToDOM(this, forceSelUpdate);
    } else {
      syncNodeSelection(this, state.selection);
      this.domObserver.setCurSelection();
    }

It is something like that, nice intuition!! I am still learning about prosemirror; but my understanding is the recommended way to update the document is by using a transaction. However I will try to explore @marijn suggestion’s about decorators, which I guess won’t need the use of a transaction.

Decoration changes must also go through transactions.

thanks for your response, if a transaction is needed, it will also cause the node being modified to be selected like in my example?

No, decorations don’t cause any effect on the selection.

1 Like

The point of Decorator, as per docs is to modify the view but not the document. This is not my case, as @bZichett suggested, in my case I would like to trigger a callback(which modifies the state) when the whole editor gets unselected. There may be a better approach to modify the document without changing the selected node?