slate icon indicating copy to clipboard operation
slate copied to clipboard

"Cannot resolve a Slate point from DOM point" for non-editable text rendered inside of Element

Open hcharley opened this issue 4 years ago • 17 comments

Do you want to request a feature or report a bug?

Bug

What's the current behavior?

When selecting (supposed to be) non-editable text that is rendered inside of an element, I am seeing an error "Cannot resolve a Slate point from DOM point". My usecase is to have a prefix that isn't editable, but explains the purpose of the block.

Slate: 0.57.1 Browser: Chrome 79.0.3945.117 OS: MacOS 10.15.2 (19C57)

Large GIF (996x358)

Edit React Slate

VM469:1691 Uncaught Error: Cannot resolve a Slate point from DOM point: [object Text],7
    at Object.toSlatePoint (eval at Dr (eval.js:61), <anonymous>:1691:13)
    at Object.toSlateRange (eval at Dr (eval.js:61), <anonymous>:1736:30)
    at HTMLDocument.eval (eval at Dr (eval.js:61), <anonymous>:800:43)
    at later (eval at Dr (eval.js:61), <anonymous>:27:23)
toSlatePoint @ VM469:1691
toSlateRange @ VM469:1736
eval @ VM469:800
later @ VM474:27
setTimeout (async)
later @ VM474:23
setTimeout (async)
later @ VM474:23
setTimeout (async)
later @ VM474:23
setTimeout (async)
debounced @ VM474:38

What's the expected behavior?

To see no error, and have Slate ignore the non-editable text.

hcharley avatar Jan 10 '20 22:01 hcharley

This is kind of strange, but I think a hack solution is to use both a CSS style and an HTML attribute.

✅ For example, this works to render elements:

<div {...attributes}>
  <div
    style={{ userSelect: "none" }}
    contentEditable={false}
  >
    Non-editable
  </div>
  <p>{children}</p>
</div>

🛑 But oddly, this doesn't work:

<div {...attributes}>
  <div
    contentEditable={false}
  >
    Non-editable
  </div>
  <p>{children}</p>
</div>

🛑 Nor does this:

<div {...attributes}>
  <div
    style={{ userSelect: "none" }}
  >
    Non-editable
  </div>
  <p>{children}</p>
</div>

I added these examples to the example code sandbox above.

I say this is a "hack solution" because ideally all you would need is contentEditable={false} to avoid this problem. An example usecase for this: you might want to let users select this non-editable text, and userSelect: none CSS prevents that.

NOTE: This does not seem to stop the error from happening when doubleclicking into a child, and selecting all of the text. It seems to sometimes stop it, but not always. This specifically stops the error from happening when selecting or clicking on the non-interactive text specfically.

hcharley avatar Jan 11 '20 15:01 hcharley

I am experiencing this problem as well, @charlex thanks for the hack solution for the time being :-)

lionel-lints avatar Jan 15 '20 18:01 lionel-lints

This has been a long standing behavior of slate. It assumes that elements on the DOM that are children of the editor are either

  1. represented in some way in the in the slate value or
  2. contentEditable=false and userSelect: none.

contentEditable=false does not work by itself because this is the state void nodes have.

cameracker avatar Feb 26 '20 06:02 cameracker

Thank you so much @charlex for the workaround! I've struggled so long with this issue (with slate 0.58.3)! :cold_sweat:

BTW, I find the "Checklist" example (https://github.com/ianstormtaylor/slate/blob/master/site/examples/check-lists.js) misleading on that matter: it works with the <input> element but crashes in the same way as described here with a <select> one. So bad for such a tiny change...

joffreyvillard avatar Jul 03 '20 16:07 joffreyvillard

i am encountering similar issues, only while running end to end tests using a headless puppeteer environment, @charlex does this sound similar to what you have there? I am unable to fix this though, having a hard time figuring out what is wrong

 Cannot resolve a Slate node from DOM node: [object HTMLSpanElement]

      at Object.toSlateNode (http:/localhost:3000/static/js/2.f3428cd3.chunk.js:2:203920)
      at http:/localhost:3000/static/js/2.f3428cd3.chunk.js:2:221289
      at Object.Ve (http:/localhost:3000/static/js/2.f3428cd3.chunk.js:2:1666433)
      at $e (http:/localhost:3000/static/js/2.f3428cd3.chunk.js:2:1666587)
      at http:/localhost:3000/static/js/2.f3428cd3.chunk.js:2:1684794
      at jr (http:/localhost:3000/static/js/2.f3428cd3.chunk.js:2:1684888)
      at kr (http:/localhost:3000/static/js/2.f3428cd3.chunk.js:2:1685303)
      at http:/localhost:3000/static/js/2.f3428cd3.chunk.js:2:1690956
      at De (http:/localhost:3000/static/js/2.f3428cd3.chunk.js:2:1766815)
      at http:/localhost:3000/static/js/2.f3428cd3.chunk.js:2:1686764

vamshikilari avatar Dec 22 '20 09:12 vamshikilari

it's useful.

gorpher avatar Jan 16 '21 13:01 gorpher

I find this happens if you're rendering (and then interacting) with an element that doesn't have slate's data attributes, which get passed in renderLeaf or renderNode btw.

for example:

renderLeaf={props => <span>{props.children}</span>}

will cause this problem. To fix it you do this:

renderLeaf={props => <span {...props.attributes}>{props.children}</span>}

There are occasions when you want to render content that you don't want to be part of slates content, but appear alongside it. In this case you just add `contentEditable={false} to that element. For example

renderLeaf={props => <span {...props.attributes}><i contentEditable={false}>👍</i>{props.children}</span>}

If an element will have contentEditable set to false, slate will not try to get the selected range when you click on it, hence the error will not occur. Hope that helps others.

Probably would be good to throw a better error cc @ianstormtaylor

juliankrispel avatar Jan 29 '21 07:01 juliankrispel

@juliankrispel thank you for providing that context.

hcharley avatar Feb 28 '21 19:02 hcharley

If someone like me getting this error when using some interactive non-slate-elements inside slate-element (it was td in my case), even with contentEditable={false} and userSelect: "none", you must check that your nearest slate-element not wrapped in styled-components (maybe it also the case for some other wrappers)

Because toSlateNode method was trying to find the nearest slate-element to the clicked non-slate-element, it was searching weakmap ELEMENT_TO_NODE by <td class="your random styled class" ...>, but in ELEMENT_TO_NODE this element was actually StyledComponent wrapper. image image

I don't get this error anymore, after I removed styled-wrapper and started using style prop for td slate-element, I hope it helps someone.

DraGonM avatar Jun 23 '21 09:06 DraGonM

@juliankrispel Thank you for providing the solution. contentEditable={false} work for me

deepaksisodiya avatar Jul 07 '21 06:07 deepaksisodiya

I have found that wrapping children in a span and adding attributes to the span as opposed to the Styled Component solves @DraGonM 's issue above:

// bad
<XLargeText as="h2" {...attributes}> // Styled Component
  {children}
</XLargeText>

// good
<XLargeText as="h2"> // Styled Component
  <span {...attributes}>{children}</span>
</XLargeText>

jamestowers avatar Jul 23 '21 21:07 jamestowers

If you have empty initial state remove autoFocus prop in <Editable /> component

Darginec05 avatar Jan 25 '22 20:01 Darginec05

I ran into this problem when rendering nothing in the slate component, since the introduction tutorial starts off with nothing in the useState([]). So I guess it tries to render an empty array, but has nothing to put the values into at the start.

To solve this, I set this as the default value:

  const [value, setValue] = useState<Descendant[]>([
    {
      type: "paragraph",
      children: [{ text: "" }],
    },
  ]);

dark-swordsman avatar Feb 25 '22 07:02 dark-swordsman

Thank you @dark-swordsman.

I solved this problem using your solution.

duarteEdu avatar Mar 21 '22 14:03 duarteEdu

I ran into this problem when rendering nothing in the slate component, since the introduction tutorial starts off with nothing in the useState([]). So I guess it tries to render an empty array, but has nothing to put the values into at the start.

To solve this, I set this as the default value:

  const [value, setValue] = useState<Descendant[]>([
    {
      type: "paragraph",
      children: [{ text: "" }],
    },
  ]);

Exactly- I was just passing in [] into the value prop. This fixes it

nipunahh avatar Apr 19 '22 02:04 nipunahh

@dark-swordsman that solved my problem too. However, it would be much better if slate would give us a very clear message about empty content. And I think it should be able to handle empty content as well.

Nefcanto avatar May 22 '22 04:05 Nefcanto

@dark-swordsman that solved my problem too. However, it would be much better if slate would give us a very clear message about empty content. And I think it should be able to handle empty content as well.

i think this issue becomes enchantment at this point. it looks like solution works for everybody 👍 😊

bekogeko avatar Jul 20 '22 13:07 bekogeko

I keep on bumping into this error, I have absolutely no idea why, or when it happens as it seems completely random. And it's driving me completely crazy

fgatti675 avatar Apr 03 '23 20:04 fgatti675

In my case, I've added a draggable item, which required the ref of the element. but apparently attributes wants the ref too

from

  <div  ref={draggable.innerRef} {...attributes}>

to

  <div
      ref={el => {
        attributes.ref(el);
        draggable.innerRef(el); 
      }}
      {...attributes}>

(but I have another issue, so it's still broken :D)

jm-maniego avatar Sep 09 '23 17:09 jm-maniego