---
title: "纯 JavaScript"
description: "学习如何在文档中使用纯 JavaScript 设置 Tiptap 编辑器、安装依赖并初始化编辑器！"
canonical_url: "https://tiptap.zhcndoc.com/editor/getting-started/install/vanilla-javascript"
---

# 纯 JavaScript

学习如何在文档中使用纯 JavaScript 设置 Tiptap 编辑器、安装依赖并初始化编辑器！

你是在没有使用 React 或 Vue 之类前端框架的情况下开发吗？没问题，你可以直接在原生 JavaScript 中使用 Tiptap。

> **提示:**
>
> 这里的“原生 JavaScript”指的是**不使用任何前端框架**，但你仍然在使用带有 **ES module 导入** 的**现代 JavaScript**（例如通过 Vite、Rollup 或 Webpack）。
>
> 如果你想完全跳过构建工具，可以查看下面的 [CDN 部分](#without-a-build-tool-cdn)，了解一个可直接在浏览器中运行的配置。

> **提示:**
>
> 如果你没有使用像 Webpack 或 Rollup 这样的打包工具，请改为按照[CDN](https://tiptap.zhcndoc.com/editor/getting-started/install/cdn.md)指南操作。由于 Tiptap 采用模块化构建，你需要在 HTML 中使用 `<script type="module">` 来使我们的 CDN 导入生效。

## 使用构建工具（Vite、Webpack、Rollup）

如果你已经在使用打包工具，或者只是想使用标准的 npm 工作流，推荐采用这种方式。

### 安装依赖

开始使用时，你需要以下 Tiptap 核心包：

- `@tiptap/core` – 主要的编辑器 API
- `@tiptap/pm` – ProseMirror，Tiptap 背后的引擎
- `@tiptap/starter-kit` – 常用扩展的便捷集合包

```bash
npm install @tiptap/core @tiptap/pm @tiptap/starter-kit
```

### 添加标记

在你希望挂载编辑器的位置添加以下 HTML：

```html
<div class="element"></div>
```

### 初始化编辑器

一切准备就绪，现在开始设置编辑器。将以下代码添加到你的 JavaScript 文件中：

```js
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'

new Editor({
  element: document.querySelector('.element'),
  extensions: [StarterKit],
  content: '<p>Hello World!</p>',
})
```

在浏览器中打开你的项目，看看 Tiptap 的效果吧。干得漂亮！

---

## 不使用构建工具（CDN）

如果你想完全跳过 npm 和构建工具，可以使用 ES module 导入，直接从 CDN 加载 Tiptap。这在所有现代浏览器中都可用。

> **注意:**
>
> 你的 `<script>` 标签必须包含 `type="module"`，导入语法才能在浏览器中生效。

### 最小示例

```html
<script type="module">
  import { Editor } from 'https://esm.sh/@tiptap/core'
  import StarterKit from 'https://esm.sh/@tiptap/starter-kit'

  new Editor({
    element: document.querySelector('.element'),
    extensions: [StarterKit],
    content: '<p>Hello from CDN!</p>',
  })
</script>

<div class="element"></div>
```

### 带工具栏的完整 HTML 文件

下面是一个可直接复制粘贴的示例，其中包含带格式化按钮的工具栏：

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Tiptap 编辑器</title>
    <style>
      body {
        margin: 2rem;
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      }
      .editor-container {
        max-width: 48rem;
        margin: 0 auto;
      }
      .toolbar {
        display: flex;
        gap: 0.25rem;
        padding: 0.5rem;
        border: 1px solid #d1d5db;
        border-bottom: none;
        border-radius: 0.375rem 0.375rem 0 0;
        background: #f9fafb;
        flex-wrap: wrap;
      }
      .toolbar button {
        padding: 0.25rem 0.5rem;
        font-size: 0.875rem;
        border: 1px solid transparent;
        border-radius: 0.25rem;
        background: transparent;
        cursor: pointer;
      }
      .toolbar button:hover {
        background: #e5e7eb;
      }
      .toolbar button.is-active {
        background: #111827;
        color: #fff;
      }
      .tiptap {
        padding: 0.75rem 1rem;
        border: 1px solid #d1d5db;
        border-radius: 0 0 0.375rem 0.375rem;
        min-height: 10rem;
        outline: none;
      }
      .tiptap p { margin: 0.5rem 0; }
      .tiptap h1, .tiptap h2, .tiptap h3 { margin: 1rem 0 0.5rem; }
      .tiptap ul, .tiptap ol { padding-left: 1.5rem; }
      .tiptap pre {
        background: #111827;
        color: #f3f4f6;
        padding: 0.75rem;
        border-radius: 0.25rem;
        font-size: 0.875rem;
      }
      .tiptap code {
        background: #f3f4f6;
        padding: 0.125rem 0.25rem;
        border-radius: 0.25rem;
        font-size: 0.875rem;
      }
      .tiptap pre code {
        background: transparent;
        padding: 0;
      }
    </style>
  </head>
  <body>
    <div class="editor-container">
      <div class="toolbar" id="toolbar">
        <button data-tiptap-button="bold">粗体</button>
        <button data-tiptap-button="italic">斜体</button>
        <button data-tiptap-button="strike">删除线</button>
        <button data-tiptap-button="code">代码</button>
        <button data-tiptap-button="h1">H1</button>
        <button data-tiptap-button="h2">H2</button>
        <button data-tiptap-button="bulletList">无序列表</button>
        <button data-tiptap-button="orderedList">有序列表</button>
        <button data-tiptap-button="blockquote">引用块</button>
        <button data-tiptap-button="codeBlock">代码块</button>
      </div>
      <div id="editor"></div>
    </div>

    <script type="module">
      import { Editor } from 'https://esm.sh/@tiptap/core'
      import StarterKit from 'https://esm.sh/@tiptap/starter-kit'

      const editor = new Editor({
        element: document.querySelector('#editor'),
        extensions: [StarterKit],
        content: `
          <h1>Welcome to Tiptap</h1>
          <p>This is a text editor built with <strong>Vanilla JavaScript</strong> and loaded from a CDN.</p>
          <p>Try the toolbar buttons above to format your text.</p>
        `,
      })

      // 绑定工具栏按钮
      const buttons = document.querySelectorAll('[data-tiptap-button]')
      buttons.forEach((button) => {
        button.addEventListener('click', () => {
          const command = button.dataset.tiptapButton

          switch (command) {
            case 'bold':
              editor.chain().focus().toggleBold().run()
              break
            case 'italic':
              editor.chain().focus().toggleItalic().run()
              break
            case 'strike':
              editor.chain().focus().toggleStrike().run()
              break
            case 'code':
              editor.chain().focus().toggleCode().run()
              break
            case 'h1':
              editor.chain().focus().toggleHeading({ level: 1 }).run()
              break
            case 'h2':
              editor.chain().focus().toggleHeading({ level: 2 }).run()
              break
            case 'bulletList':
              editor.chain().focus().toggleBulletList().run()
              break
            case 'orderedList':
              editor.chain().focus().toggleOrderedList().run()
              break
            case 'blockquote':
              editor.chain().focus().toggleBlockquote().run()
              break
            case 'codeBlock':
              editor.chain().focus().toggleCodeBlock().run()
              break
          }

          updateActiveButtons()
        })
      })

      function updateActiveButtons() {
        const map = {
          bold: () => editor.isActive('bold'),
          italic: () => editor.isActive('italic'),
          strike: () => editor.isActive('strike'),
          code: () => editor.isActive('code'),
          h1: () => editor.isActive('heading', { level: 1 }),
          h2: () => editor.isActive('heading', { level: 2 }),
          bulletList: () => editor.isActive('bulletList'),
          orderedList: () => editor.isActive('orderedList'),
          blockquote: () => editor.isActive('blockquote'),
          codeBlock: () => editor.isActive('codeBlock'),
        }

        buttons.forEach((button) => {
          const command = button.dataset.tiptapButton
          const check = map[command]
          if (check && check()) {
            button.classList.add('is-active')
          } else {
            button.classList.remove('is-active')
          }
        })
      }

      editor.on('selectionUpdate', updateActiveButtons)
      editor.on('update', updateActiveButtons)
    </script>
  </body>
</html>
```

将此文件保存为 `index.html`，然后在任何现代浏览器中打开它即可——无需服务器。

### 在线演示

你可以在 CodeSandbox 上查看一个类似的可运行示例：

[https://codesandbox.io/s/throbbing-smoke-now37q](https://codesandbox.io/s/throbbing-smoke-now37q?file=/index.html)

---

## 连接工具栏（原生 JS）

Tiptap 是无头的，因此它不自带内置工具栏。你需要自己连接 UI 按钮。下面是原生 JavaScript 中的实现方式：

```js
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'

const editor = new Editor({
  element: document.querySelector('#editor'),
  extensions: [StarterKit],
  content: '<p>Hello World!</p>',
})

// 监听点击并调用 editor 命令
document.querySelector('#bold-button').addEventListener('click', () => {
  editor.chain().focus().toggleBold().run()
})

// 检查某个 mark 或节点是否处于激活状态，以便为按钮设置样式
const isBold = editor.isActive('bold')
const isHeading1 = editor.isActive('heading', { level: 1 })
const isBulletList = editor.isActive('bulletList')

// 在选择变化时更新按钮的激活状态
editor.on('selectionUpdate', () => {
  document.querySelector('#bold-button').classList.toggle('is-active', editor.isActive('bold'))
})
```

### 常用编辑器命令

| 命令     | 代码                                                         |
| ------ | ---------------------------------------------------------- |
| 粗体     | `editor.chain().focus().toggleBold().run()`                |
| 斜体     | `editor.chain().focus().toggleItalic().run()`              |
| 删除线    | `editor.chain().focus().toggleStrike().run()`              |
| 代码（行内） | `editor.chain().focus().toggleCode().run()`                |
| 标题 1   | `editor.chain().focus().toggleHeading({ level: 1 }).run()` |
| 标题 2   | `editor.chain().focus().toggleHeading({ level: 2 }).run()` |
| 无序列表   | `editor.chain().focus().toggleBulletList().run()`          |
| 有序列表   | `editor.chain().focus().toggleOrderedList().run()`         |
| 引用块    | `editor.chain().focus().toggleBlockquote().run()`          |
| 代码块    | `editor.chain().focus().toggleCodeBlock().run()`           |
| 撤销     | `editor.chain().focus().undo().run()`                      |
| 重做     | `editor.chain().focus().redo().run()`                      |

可用命令请参阅各个扩展的文档。

---

## 样式

Tiptap 默认不包含视觉样式——它只输出语义化的 HTML。你可以使用自己的 CSS，或者使用 Tailwind 或 Bootstrap 之类的框架来按你喜欢的方式设置样式。

StarterKit 包含最小化的默认样式，可使文本呈现为一个基础文档的样子。

在 [样式指南](https://tiptap.zhcndoc.com/editor/getting-started/style-editor.md) 中了解更多。

## 接下来的步骤

- [配置你的编辑器](https://tiptap.zhcndoc.com/editor/getting-started/configure.md)
- [为编辑器添加样式](https://tiptap.zhcndoc.com/editor/getting-started/style-editor.md)
- [了解更多关于 Tiptap 的概念](https://tiptap.zhcndoc.com/editor/core-concepts/introduction.md)
- [学习如何持久化编辑器状态](https://tiptap.zhcndoc.com/editor/core-concepts/persistence.md)
- [开始构建你自己的扩展](https://tiptap.zhcndoc.com/editor/extensions/custom-extensions.md)
