tiptap/docs/src/docPages/guide/node-views/js.md

124 lines
4.0 KiB
Markdown
Raw Normal View History

2021-03-18 05:35:41 +08:00
# Node views with JavaScript
## toc
## Introduction
2021-04-03 22:13:47 +08:00
Using frameworks like Vue or React can feel too complex, if youre used to work without those two. Good news: You can use plain JavaScript in your node views. There is just a little bit you need to know, but lets go through this one by one.
## Render a node view with JavaScript
Here is what you need to do to render a node view inside your editor:
2021-04-07 05:36:07 +08:00
1. [Create a node extension](/guide/custom-extensions)
2021-04-03 22:13:47 +08:00
2. Register a new node view with `addNodeView()`
3. Write your render function
4. [Configure tiptap to use your new node extension](/guide/configuration)
This is how your node extension could look like:
2021-03-18 05:35:41 +08:00
```js
import { Node } from '@tiptap/core'
import Component from './Component.vue'
export default Node.create({
2021-04-03 22:13:47 +08:00
// configuration …
2021-03-18 05:35:41 +08:00
addNodeView() {
return ({ editor, node, getPos, HTMLAttributes, decorations, extension }) => {
const dom = document.createElement('div')
dom.innerHTML = 'Hello, Im a node view!'
return {
dom,
}
2021-03-18 06:21:36 +08:00
}
2021-03-18 05:35:41 +08:00
},
})
```
2021-03-18 06:21:36 +08:00
2021-04-03 22:13:47 +08:00
Got it? Lets see it in action. Feel free to copy the below example to get started.
2021-03-18 06:21:36 +08:00
<demo name="Guide/NodeViews/JavaScript" />
2021-04-03 22:13:47 +08:00
That node view even interacts with the editor. Time to see how that is wired up.
2021-03-18 06:21:36 +08:00
## Access node attributes
2021-04-03 22:13:47 +08:00
The editor passes a few helpful things to your render function. One of them is the the `node` prop. This one enables you to access node attributes in your node view. Lets say you have [added an attribute](/guide/extend-extensions#attributes) named `count` to your node extension. You could access the attribute like this:
```js
addNodeView() {
return ({ node }) => {
console.log(node.attrs.count)
// …
}
}
```
2021-03-18 06:21:36 +08:00
## Update node attributes
2021-04-03 22:13:47 +08:00
You can even update node attributes from your node view, with the help of the `getPos` prop passed to your render function. Dispatch a new transaction with an object of the updated attributes:
```js
addNodeView() {
return ({ editor, node, getPos }) => {
const { view } = editor
// Create a button …
const button = document.createElement('button')
button.innerHTML = `This button has been clicked ${node.attrs.count} times.`
// … and when its clicked …
button.addEventListener('click', () => {
if (typeof getPos === 'function') {
// … dispatch a transaction, for the current position in the document …
view.dispatch(view.state.tr.setNodeMarkup(getPos(), undefined, {
count: node.attrs.count + 1,
}))
// … and set the focus back to the editor.
editor.commands.focus()
}
})
// …
}
}
```
Does seem a little bit too complex? Consider using [React](/guide/node-views/react) or [Vue](/guide/node-views/vue), if you have one of those in your project anyway. It gets a little bit easier with those two.
2021-03-18 06:21:36 +08:00
## Adding a content editable
2021-04-03 22:13:47 +08:00
To add editable content to your node view, you need to pass a `contentDOM`, a container element for the content. Here is a simplified version of a node view with non-editable and editable text content:
```js
// Create a container for the node view
const dom = document.createElement('div')
// Give other elements containing text `contentEditable = false`
const label = document.createElement('span')
label.innerHTML = 'Node view'
label.contentEditable = false
// Create a container for the content
const content = document.createElement('div')
// Append all elements to the node view container
dom.append(label, content)
return {
// Pass the node view container …
dom,
// … and the content container:
contentDOM: content,
}
```
Got it? Youre free to do anything you like, as long as you return a container for the node view and another one for the content. Here is the above example in action:
<demo name="Guide/NodeViews/JavaScriptContent" />
Keep in mind that this content is rendered by tiptap. That means you need to tell what kind of content is allowed, for example with `content: 'inline*'` in your node extension (thats what we use in the above example).
2021-03-18 06:21:36 +08:00