テキストエディタ

Framework7 には、タッチフレンドリーなリッチテキストエディタコンポーネントが付属しています。最新の "contenteditable" API に基づいているため、そのままどこでも動作するはずです。

基本的なフォーマット機能が付属しています。ただし、その機能は、あらゆる要件に合わせて簡単に拡張およびカスタマイズできます。

テキストエディタのレイアウト

<div class="text-editor">
  <div class="text-editor-content" contenteditable></div>
</div>

エディタのサイズを変更可能にするには (高さがコンテンツに合わせて調整される場合)、エディタ要素に text-editor-resizable クラスを追加する必要があります

<!-- additional "text-editor-resizable" class -->
<div class="text-editor text-editor-resizable">
  <div class="text-editor-content" contenteditable></div>
</div>

テキストエディタアプリメソッド

テキストエディタを操作するための関連アプリメソッドを見てみましょう

app.textEditor.create(パラメータ)- テキストエディタインスタンスを作成します

  • パラメータ - オブジェクト。テキストエディタパラメータを持つオブジェクト

メソッドは、作成されたテキストエディタインスタンスを返します

app.textEditor.destroy(el)- テキストエディタインスタンスを破棄します

  • el - HTMLElement または 文字列 (CSS セレクター付き) または オブジェクト。破棄するテキストエディタ要素またはテキストエディタインスタンス。

app.textEditor.get(el)- HTML 要素によってテキストエディタインスタンスを取得します

  • el - HTMLElement または 文字列 (CSS セレクター付き)。テキストエディタ要素。

メソッドはテキストエディタのインスタンスを返します

例えば

var textEditor = app.textEditor.create({
  el: '#my-text-editor',
  value: <code><p>Hello</p></code>,
});

テキストエディタパラメータ

利用可能なすべてのテキストエディタパラメータのリストを見てみましょう

パラメータタイプデフォルト説明
elHTMLElement
文字列
テキストエディタ要素。 HTMLElement またはエディタ要素の CSS セレクターを持つ文字列
value文字列

テキストエディタの初期 HTML コンテンツ値。初期値は、text-editor-content 要素の内部 HTML コンテンツとして配置することもできます。例:

<div class="text-editor">
  <div class="text-editor-content" contenteditable>
    <p>Initial HTML value</p>
  </div>
</div>
placeholder文字列エディタプレースホルダーコンテンツは、空の場合に表示されます。デフォルトでは指定されていません
mode文字列toolbar

テキストエディタボタンモード。次のいずれかになります

  • toolbar - テキストエディタコンテナ要素にエディタボタン付きのツールバーを追加します
  • popover - エディタの選択されたテキストの上にエディタボタン付きのポップオーバーバブルを表示します
  • keyboard-toolbar - エディタがフォーカスされていると、仮想キーボードの上にエディタボタン付きのツールバーが表示されます。 iOS、Android Cordova アプリ、および Android Chrome でのみサポートされています。サポートされていない場合は、popover モードにフォールバックします。
buttons配列

エディタボタンの配列、またはエディタボタンを含む配列 (グループ) の配列。デフォルトではすべてのボタンが有効になっており、そのデフォルト値は次のとおりです

[
  ['bold', 'italic', 'underline', 'strikeThrough'],
  ['orderedList', 'unorderedList'],
  ['link', 'image'],
  ['paragraph', 'h1', 'h2', 'h3'],
  ['alignLeft', 'alignCenter', 'alignRight', 'alignJustify'],
  ['subscript', 'superscript'],
  ['indent', 'outdent'],
]
dividers真偽値trueボタングループの間に視覚的な区切り線を追加します
imageUrlText文字列画像 URL を挿入する画像 URL リクエストに表示されるプロンプトテキスト
linkUrlText文字列リンク URL を挿入するリンク URL リクエストに表示されるプロンプトテキスト
clearFormattingOnPaste真偽値true有効にすると、クリップボードから貼り付けるときにフォーマットがクリアされます
customButtonsオブジェクト

カスタムボタン付きのオブジェクト。オブジェクトプロパティキーは、有効にするために buttons で使用する必要があるボタン ID です。

例えば、<hr> を追加するカスタムボタンを指定するには、次の宣言を使用できます

var textEditor = app.textEditor.create({
  el: '.my-text-editor',
  customButtons: {
    // property key is the button id
    hr: {
      // button html content
      content: '&lt;hr&gt;',
      // button click handler
      onClick(event, buttonEl) {
        document.execCommand('insertHorizontalRule', false);
      }
    }
  },
  // now we use custom button id "hr" to add it to buttons
  buttons: ['bold', 'italic', 'hr']
})
onオブジェクト

イベントハンドラを持つオブジェクト。例えば

var textEditor = app.textEditor.create({
  ...
  on: {
    change: function () {
      console.log('Text Editor value changed')
    }
  }
})

次のすべてのパラメータは、textEditor プロパティの下のグローバルアプリパラメータで使用して、すべてのテキストエディタのデフォルトを設定できることに注意してください。例えば

var app = new Framework7({
  textEditor: {
    buttons: ['bold', 'italic'],
  }
});

テキストエディタメソッドとプロパティ

テキストエディタを初期化すると、初期化されたインスタンスが変数 (上記の例の textEditor 変数など) に格納され、便利なメソッドとプロパティが使用できます

プロパティ
textEditor.appグローバルアプリインスタンスへのリンク
textEditor.elテキストエディタコンテナ HTML 要素
textEditor.$elテキストエディタコンテナ HTML 要素を含む Dom7 インスタンス
textEditor.contentElテキストエディタコンテンツ (contenteditalbe) HTML 要素
textEditor.$contentElテキストエディタコンテンツ (contenteditalbe) HTML 要素を含む Dom7 インスタンス
textEditor.valueテキストエディタの HTML 値
textEditor.params初期化パラメータを含むオブジェクト
メソッド
textEditor.setValue()新しいテキストエディタ値を設定します。 は HTML 文字列です。
textEditor.getValue()現在のテキストエディタ値を返します
textEditor.clearValue()テキストエディタ値をクリアします
textEditor.getSelectionRange()現在の選択範囲を返します
textEditor.setSelectionRange(範囲)渡された範囲に基づいて選択を設定します
textEditor.destroy()テキストエディタインスタンスを破棄し、すべてのイベントを削除します
textEditor.on(イベント, ハンドラ)イベントハンドラを追加します
textEditor.once(イベント, ハンドラ)起動後に削除されるイベントハンドラを追加します
textEditor.off(イベント, ハンドラ)イベントハンドラを削除します
textEditor.off(イベント)指定されたイベントのすべてのハンドラを削除します
textEditor.emit(イベント, ...args)インスタンスでイベントを発生させます

テキストエディタイベント

テキストエディタは、テキストエディタ要素で次の DOM イベントを発生させ、アプリとテキストエディタインスタンスでイベントを発生させます

DOM イベント

イベント説明
texteditor:initエディタの初期化時にイベントがトリガーされます
texteditor:changeエディタ値が変更されたときにイベントがトリガーされます
texteditor:inputエディタのコンテンツ "input" イベントでイベントがトリガーされます
texteditor:focusエディタのコンテンツフォーカスでイベントがトリガーされます
texteditor:blurエディタのコンテンツぼかしでイベントがトリガーされます
texteditor:buttonclickエディタボタンのクリックでイベントがトリガーされます
texteditor:keyboardopenエディタキーボードツールバーが表示されたときにイベントがトリガーされます
texteditor:keyboardcloseエディタキーボードツールバーが消えたときにイベントがトリガーされます
texteditor:popoveropenエディタポップオーバーが開いたときにイベントがトリガーされます
texteditor:popovercloseエディタポップオーバーが閉じたときにイベントがトリガーされます
texteditor:beforedestroyText Editor インスタンスが破棄される直前にイベントがトリガーされます

アプリとテキストエディタインスタンスイベント

Text Editor インスタンスは、自身とアプリインスタンスの両方でイベントを発行します。アプリインスタンスイベントの名前は、textEditor というプレフィックスが付いた同じ名前です。

イベントターゲット引数説明
inittextEditor(editor)エディタの初期化時にイベントがトリガーされます。
textEditorInitapp
changetextEditor(editor)エディタの初期化時にイベントがトリガーされます。
textEditorChangeapp
inputtextEditor(editor)エディタのコンテンツの「入力」イベントでイベントがトリガーされます。
textEditorInputapp
focustextEditor(editor)エディタのコンテンツフォーカスでイベントがトリガーされます。
textEditorFocusapp
blurtextEditor(editor)エディタのコンテンツぼかしでイベントがトリガーされます。
textEditorBlurapp
buttonClicktextEditor(editor, button)エディタボタンのクリックでイベントがトリガーされます。
2 番目の引数として、イベントハンドラはクリックされたボタンの ID を受け取ります。例:bold
textEditorButtonClickapp
keyboardOpentextEditor(editor)エディタキーボードツールバーが表示されたときにイベントがトリガーされます。
textEditorKeyboardOpenapp
keyboardClosetextEditor(editor)エディタキーボードツールバーが消えたときにイベントがトリガーされます。
textEditorKeyboardCloseapp
popoverOpentextEditor(editor)エディタポップオーバーが開いたときにイベントがトリガーされます。
textEditorPopoverOpenapp
popoverClosetextEditor(editor)エディタポップオーバーが閉じたときにイベントがトリガーされます。
textEditorPopoverCloseapp
beforeDestroytextEditor(editor)Text Editor インスタンスが破棄される直前にイベントがトリガーされます。
textEditorBeforeDestroyapp

テキストエディタの自動初期化

Text Editor APIを使用する必要がなく、Text Editorがページ内にあり、ページ初期化時にDOMに存在する場合、追加のtext-editor-initクラスを追加するだけで自動的に初期化できます。

<!-- Add text-editor-init class -->
<div class="text-editor text-editor-init">
  <div class="text-editor-content" contenteditable></div>
</div>

この場合、作成されたText Editorインスタンスにアクセスする必要がある場合は、app.textEditor.getアプリメソッドを使用できます。

var textEditor = app.textEditor.get('.my-text-editor');

if (!textEditor.value) {
  // do something
}

自動初期化を使用する場合、追加のパラメータを渡す必要がある場合があります。これは、パネル要素のdata-属性を使用して行うことができます。

<!-- parameters set via data- attributes -->
<div
  class="text-editor text-editor-init"
  data-mode="popover"
  data-placeholder="Description"
>
  ...
</div>

パラメータはcamelCase(例:imageUrlText)を使用しますが、data-属性ではkebab-case(例:data-image-url-text)を使用する必要があります。

CSS変数

以下は、関連するCSS変数(CSSカスタムプロパティ)のリストです。

:root {
  --f7-text-editor-font-size: inherit;
  --f7-text-editor-font-weight: inherit;
  --f7-text-editor-border-width: 1px;
  --f7-text-editor-height: 250px;
  --f7-text-editor-margin: 16px;
  --f7-text-editor-padding: 8px;
  --f7-text-editor-button-bg-color: transparent;
  --f7-text-editor-button-size: 28px;
  --f7-text-editor-button-icon-size: 20px;
  --f7-text-editor-button-margin: 2px;
  --f7-text-editor-text-color: #000;
  --f7-text-editor-bg-color: #fff;
  --f7-text-editor-button-divider-color: rgba(0, 0, 0, 0.15);
}
:root .dark,
:root.dark {
  --f7-text-editor-bg-color: #121212;
  --f7-text-editor-text-color: #fff;
  --f7-text-editor-button-divider-color: rgba(255, 255, 255, 0.15);
}
.ios {
  --f7-text-editor-toolbar-padding: 6px;
  --f7-text-editor-button-border-radius: 2px;
  --f7-text-editor-placeholder-color: rgba(0, 0, 0, 0.35);
  --f7-text-editor-toolbar-border-color: rgba(0, 0, 0, 0.25);
  --f7-text-editor-toolbar-bg-color: #fff;
  --f7-text-editor-border-color: rgba(0, 0, 0, 0.1);
  --f7-text-editor-button-text-color: #333;
}
.ios .dark,
.ios.dark {
  --f7-text-editor-placeholder-color: rgba(255, 255, 255, 0.35);
  --f7-text-editor-toolbar-bg-color: #121212;
  --f7-text-editor-toolbar-border-color: rgba(255, 255, 255, 0.1);
  --f7-text-editor-toolbar-bg-color: #202020;
  --f7-text-editor-border-color: rgba(255, 255, 255, 0.1);
  --f7-text-editor-button-text-color: #fff;
}
.md {
  --f7-text-editor-button-border-radius: 8px;
  --f7-text-editor-toolbar-padding: 8px;
}
.md,
.md .dark,
.md [class*='color-'] {
  --f7-text-editor-placeholder-color: var(--f7-md-on-surface-variant);
  --f7-text-editor-toolbar-bg-color: var(--f7-md-surface-1);
  --f7-text-editor-border-color: var(--f7-md-outline);
  --f7-text-editor-button-text-color: var(--f7-md-on-surface);
}

text-editor.html
<template>
  <div class="page">
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner sliding">
        <div class="title">Text Editor</div>
      </div>
    </div>
    <div class="page-content">
      <div class="block">
        <p>Framework7 comes with a touch-friendly Rich Text Editor component. It is based on modern "contenteditable"
          API so it should work everywhere as is.</p>
        <p>It comes with the basic set of formatting features. But its functionality can be easily extended and
          customized to fit any requirements.</p>
      </div>

      <div class="block-title">Default Setup</div>
      <div class="text-editor text-editor-init">
        <div class="text-editor-content" contenteditable></div>
      </div>

      <div class="block-title">With Placeholder</div>
      <div class="text-editor text-editor-init" data-placeholder="Enter text...">
        <div class="text-editor-content" contenteditable></div>
      </div>

      <div class="block-title">With Default Value</div>
      <div class="text-editor text-editor-init" data-placeholder="Enter text...">
        <div class="text-editor-content" contenteditable>
          <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Consequatur sunt, sapiente quis eligendi
            consectetur hic asperiores assumenda quidem dolore quasi iusto tenetur commodi qui ullam sint sed alias!
            Consequatur, dolor!</p>
          <p>Provident reiciendis exercitationem reprehenderit amet repellat laborum, sequi id quam quis quo quos facere
            veniam ad libero dolorum animi. Nobis, illum culpa explicabo dolorem vitae ut dolor at reprehenderit magnam?
          </p>
          <p>Qui, animi. Dolores dicta, nobis aut expedita enim eum assumenda modi, blanditiis voluptatibus excepturi
            non pariatur. Facilis fugit facere sequi molestias nemo in, suscipit inventore consequuntur, repellat
            perferendis, voluptas odit.</p>
          <p>Tempora voluptates, doloribus architecto eligendi numquam facilis perspiciatis autem quam voluptas maxime
            ratione harum laudantium cum deleniti. In, alias deserunt voluptatibus eligendi libero nobis est unde et
            perspiciatis cumque voluptatum.</p>
          <p>Quam error doloribus qui laboriosam eligendi. Aspernatur quam pariatur perspiciatis reprehenderit atque
            dicta culpa, aut rem? Assumenda, quibusdam? Reprehenderit necessitatibus facere nemo iure maiores porro
            voluptates accusamus quibusdam. Nesciunt, assumenda?</p>
        </div>
      </div>

      <div class="block-title">Specific Buttons</div>
      <div class="block-header">It is possible to customize which buttons (commands) to show.</div>
      <div class="text-editor text-editor-init" data-placeholder="Enter text..."
        data-buttons='[["bold", "italic", "underline", "strikeThrough"], ["orderedList", "unorderedList"]]'>
        <div class="text-editor-content" contenteditable></div>
      </div>

      <div class="block-title">Custom Button</div>
      <div class="block-header">It is possible to create custom editor buttons. Here is the custom "hr" button that adds
        horizontal rule:</div>
      <div class="text-editor text-editor-custom-buttons">
        <div class="text-editor-content" contenteditable></div>
      </div>

      <div class="block-title">Resizable</div>
      <div class="block-header">Editor will be resized based on its content.</div>
      <div class="text-editor text-editor-init text-editor-resizable" data-placeholder="Enter text..."
        data-buttons='["bold", "italic", "underline", "strikeThrough"]'>
        <div class="text-editor-content" contenteditable></div>
      </div>

      <div class="block-title">Popover Mode</div>
      <div class="block-header">In this mode, there is no toolbar with buttons, but they appear as popover when you
        select any text in editor.</div>
      <div class="text-editor text-editor-init" data-placeholder="Enter text..."
        data-buttons='["bold", "italic", "underline", "strikeThrough"]' data-mode="popover"
        style="--f7-text-editor-height: 150px">
        <div class="text-editor-content" contenteditable></div>
      </div>

      <div class="block-title">Keyboard Toolbar Mode</div>
      <div class="block-header">In this mode, toolbar with buttons will appear on top of virtual keyboard when editor is
        in the focus. It is supported only in iOS, Android cordova apps and in Android Chrome. When not supported it
        will fallback to "popover" mode.</div>
      <div class="text-editor text-editor-init" data-placeholder="Enter text..." data-mode="keyboard-toolbar"
        style="--f7-text-editor-height: 150px">
        <div class="text-editor-content" contenteditable></div>
      </div>

      <div class="block-title">As List Input</div>
      <div class="block-header">Text editor can be used in list with other inputs. In this example it is enabled with
        "keyboard-toolbar"/"popover" type for "About" field.</div>
      <div class="list list-strong-ios list-dividers-ios list-outline-ios">
        <ul>
          <li class="item-content item-input">
            <div class="item-media">
              <i class="icon demo-list-icon"></i>
            </div>
            <div class="item-inner">
              <div class="item-title item-label">Name</div>
              <div class="item-input-wrap">
                <input type="text" placeholder="Your name" />
              </div>
            </div>
          </li>
          <li class="item-content item-input">
            <div class="item-media">
              <i class="icon demo-list-icon"></i>
            </div>
            <div class="item-inner">
              <div class="item-title item-label">About</div>
              <div class="item-input-wrap">
                <div class="text-editor text-editor-init text-editor-resizable" data-placeholder="About"
                  data-buttons='["bold", "italic", "underline", "strikeThrough"]' data-mode="popover">
                  <div class="text-editor-content" contenteditable></div>
                </div>
              </div>
            </div>
          </li>
        </ul>
      </div>
    </div>
  </div>
</template>
<script>
  export default (props, { $f7, $el, $on }) => {
    let textEditorCustomButtons;
    $on('pageInit', () => {
      textEditorCustomButtons = $f7.textEditor.create({
        el: $el.value.find('.text-editor-custom-buttons'),
        // define custom "hr" button
        customButtons: {
          hr: {
            content: '<hr>',
            onClick(editor, buttonEl) {
              document.execCommand('insertHorizontalRule', false);
            },
          },
        },
        buttons: [["bold", "italic", "underline", "strikeThrough"], "hr"],
      });
    });
    $on('pageBeforeRemove', () => {
      textEditorCustomButtons.destroy()
    });

    return $render;
  };
</script>