この記事ではvueを使用し、マウスクリックイベントといくつかの小さなページの最適化を追加します。 基本構造 関数の基本構造を記述するためのsandBox.vueファイルを作成する <div class="content"> <!--テキストボックス--> <div クラス="エディター" ref="divRef" コンテンツ編集可能 @keyup="handkeKeyUp" @keydown="ハンドルキーダウン" </div> <!--オプション--> <ダイアログ v-if="showDialog" :visible="ダイアログを表示" :position="位置" :queryString="クエリ文字列" @onPickUser="ハンドルPickUser" @onHide="ハンドルを非表示" @onShow="ハンドル表示" </ダイアログ> </div> <スクリプト> '../components/AtDialog' から AtDialog をインポートします。 エクスポートデフォルト{ 名前: 'サンドボックス', コンポーネント: { AtDialog }, データ () { 戻る { node: '', // ノードを取得 user: '', // 選択された項目の内容 endIndex: '', // 最後のカーソル位置 queryString: '', // 検索値 showDialog: false, // ポップアップウィンドウを表示するかどうか position: { x: 0, 年: 0 }//ポップアップウィンドウの表示位置} }, メソッド: { // カーソル位置を取得する getCursorIndex () { 定数選択 = window.getSelection() return selection.focusOffset // 先頭の focusNode のオフセットを選択します}, // ノードを取得する getRangeNode () { 定数選択 = window.getSelection() return selection.focusNode // 選択された終了ノード}, // ポップアップウィンドウが表示される場所 getRangeRect () { 定数選択 = window.getSelection() const range = selection.getRangeAt(0) // 選択範囲を管理するための汎用オブジェクトです const rect = range.getClientRects()[0] // テキストを選択し、選択されたテキストの範囲を取得します const LINE_HEIGHT = 30 戻る { x: 矩形x、 y: rect.y + LINE_HEIGHT } }, // @を表示するかどうか 表示At() { 定数ノード = this.getRangeNode() if (!node || node.nodeType !== Node.TEXT_NODE) falseを返す 定数コンテンツ = node.textContent || '' 定数regx = /@([^@\s]*)$/ 定数マッチ = regx.exec(content.slice(0, this.getCursorIndex())) マッチ&&match.length === 2を返す }, // @user を取得する getAtUser () { const コンテンツ = this.getRangeNode().textContent || '' 定数regx = /@([^@\s]*)$/ 定数マッチ = regx.exec(content.slice(0, this.getCursorIndex())) マッチ&&マッチ.長さ=== 2の場合{ リターンマッチ[1] } 未定義を返す }, // ラベルを作成する createAtButton (user) { const btn = document.createElement('span') btn.style.display = 'インラインブロック' btn.dataset.user = JSON.stringify(user) btn.className = 'at-button' btn.contentEditable = 'false' btn.textContent = `@${user.name}` const ラッパー = document.createElement('span') wrapper.style.display = 'インラインブロック' wrapper.contentEditable = 'false' 定数スペース要素 = document.createElement('span') spaceElem.style.whiteSpace = 'pre' スペース要素.textContent = '\u200b' spaceElem.contentEditable = 'false' const clonedSpaceElem = spaceElem.cloneNode(true) wrapper.appendChild(スペース要素) wrapper.appendChild(btn) wrapper.appendChild(クローンされたスペース要素) 返品ラッパー }, replaceString (raw, replacer) { raw.replace(/@([^@\s]*)$/, replacer) を返します }, // @ タグを挿入 replaceAtUser (user) { 定数ノード = this.node if (ノード && ユーザー) { 定数コンテンツ = node.textContent || '' 定数 endIndex = this.endIndex const preSlice = this.replaceString(content.slice(0, endIndex), '') const restSlice = content.slice(endIndex) 定数 parentNode = ノード.parentNode 定数 nextNode = node.nextSibling const previousTextNode = 新しい Text(preSlice) const nextTextNode = new Text('\u200b' + restSlice) // 0 個のワイド文字を追加 const atButton = this.createAtButton(user) 親ノードの子ノードを削除します。 // テキストボックスに挿入 if (nextNode) { 親ノード。前のテキストノード、次のノードの前に挿入します。 親ノード。ボタンの前に挿入します。次のノード。 親ノード。次のテキストノードの前に挿入します。 } それ以外 { 親ノード.appendChild(前のテキストノード) 親ノード.appendChild(ボタン上) 親ノード。次のテキストノードに子を追加します。 } // カーソル位置をリセットする const range = new Range() 定数選択 = window.getSelection() 範囲.setStart(次のテキストノード、0) 範囲.setEnd(次のテキストノード、0) 選択範囲をすべて削除() 選択範囲を追加します(範囲) } }, //キーボードアップイベントhandkeKeyUp() { if (this.showAt()) { 定数ノード = this.getRangeNode() 定数 endIndex = this.getCursorIndex() this.node = ノード this.endIndex = 終了インデックス this.position = this.getRangeRect() this.queryString = this.getAtUser() || '' this.showDialog = true } それ以外 { this.showDialog = false } }, //キーボード押下イベント handleKeyDown (e) { ダイアログを表示する if (e.code === 'ArrowUp' || e.code === '矢印下' || e.code === 'Enter') { e.preventDefault() } } }, // タグを挿入した後、選択ボックスを非表示にする handlePickUser (user) { this.replaceAtUser(ユーザー) this.user = ユーザー this.showDialog = false }, //選択ボックスを非表示にするhandleHide() { this.showDialog = false }, // 選択ボックスを表示する handleShow () { this.showDialog = true } } } </スクリプト> <スタイル スコープ lang="scss"> 。コンテンツ { フォントファミリー: サンセリフ; h1{ テキスト配置: 中央; } } .エディター{ マージン: 0 自動; 幅: 600ピクセル; 高さ: 150px; 背景: #fff; 境界線: 1px 青 境界線の半径: 5px; テキスト配置: 左; パディング: 10px; オーバーフロー:自動; 行の高さ: 30px; &:集中 { アウトライン: なし; } } </スタイル> クリックイベントが追加された場合、ノードとカーソルの位置は[キーボードアップイベント]で取得され、データに保存される必要があります。 //キーボードアップイベントhandkeKeyUp() { if (this.showAt()) { const node = this.getRangeNode() // ノードを取得 const endIndex = this.getCursorIndex() // カーソルの位置を取得 this.node = node this.endIndex = 終了インデックス this.position = this.getRangeRect() this.queryString = this.getAtUser() || '' this.showDialog = true } それ以外 { this.showDialog = false } }, 新しいコンポーネントを作成し、ポップアップオプションを編集します <テンプレート> <div クラス="ラッパー" :style="{位置:'固定'、上:位置.y +'px'、左:位置.x+'px'}"> <div v-if="!mockList.length" class="empty">検索結果はありません</div> <div v-for="(item,i) in mockList" :key="アイテムID" クラス="アイテム" :class="{'アクティブ': i === インデックス}" ref="ユーザー参照" @click="clickAt($event,item)" @mouseenter="hoverAt(i)" > <div class="name">{{item.name}}</div> </div> </div> </テンプレート> <スクリプト> const モックデータ = [ { 名前: 'HTML'、 ID: 'HTML' }, { 名前: 'CSS'、 ID: 'CSS' }, { 名前: 'Java'、 ID: 'Java' }, { 名前: 'JavaScript'、 ID: 'JavaScript' } ] エクスポートデフォルト{ 名前: 'AtDialog', 小道具: { 表示: ブール値、 位置: オブジェクト、 クエリ文字列: 文字列 }, データ () { 戻る { ユーザー: [], インデックス: -1, モックリスト: モックデータ } }, 時計: クエリ文字列 (値) { val ? this.mockList = mockData.filter(({ name }) => name.startsWith(val)) : this.mockList = mockData.slice(0) } }, マウントされた(){ document.addEventListener('keyup'、this.keyDownHandler) は、 }, 破壊された(){ document.removeEventListener('keyup'、this.keyDownHandler) を削除します。 }, メソッド: { キーダウンハンドラ(e) { if (e.code === 'エスケープ') { これを$emit('onHide') 戻る } //キーボードが押された => ↓ if (e.code === 'ArrowDown') { this.index >= this.mockList.length - 1 の場合 { this.index = 0 } それ以外 { this.index = this.index + 1 } } //キーボードが押された => ↑ if (e.code === 'ArrowUp') { (this.index <= 0)の場合{ this.index = this.mockList.length - 1 } それ以外 { this.index = this.index - 1 } } //キーボードが押された => Enterif (e.code === 'Enter') { if (this.mockList.length) { 定数ユーザー = { 名前: this.mockList[this.index].name, id: this.mockList[this.index].id } this.$emit('onPickUser', ユーザー) this.index = -1 } } }, clickAt (e, アイテム) { 定数ユーザー = { 名前: アイテム名、 id: アイテム.id } this.$emit('onPickUser', ユーザー) this.index = -1 }, hoverAt (インデックス) { this.index = インデックス } } } </スクリプト> <スタイル スコープ lang="scss"> .ラッパー{ 幅: 238ピクセル; 境界線: 1px 実線 #e4e7ed; 境界線の半径: 4px; 背景色: #fff; ボックスシャドウ: 0 2px 12px 0 rgb(0 0 0 / 10%); ボックスのサイズ: 境界線ボックス; パディング: 6px 0; } 。空の{ フォントサイズ: 14px; パディング: 0 20px; 色: #999; } 。アイテム { フォントサイズ: 14px; パディング: 0 20px; 行の高さ: 34px; カーソル: ポインタ; 色: #606266; &。アクティブ { 背景: #f5f7fa; 色: 青; .id { 色: 青; } } &:最初の子 { 境界線の半径: 5px 5px 0 0; } &:最後の子 { 境界線の半径: 0 0 5px 5px; } .id { フォントサイズ: 12px; 色: rgb(83, 81, 81); } } </スタイル> 以上がVueで@人機能を実装する方法の詳細です。Vueの@人機能の詳細については、123WORDPRESS.COMの他の関連記事に注目してください。 以下もご興味があるかもしれません:
|
<<: Bootstrap 3.0 学習ノート CSS関連補足
>>: Linux システムファイル共有 samba 設定チュートリアル
MySQL 高度な SQL ステートメント kgc を使用します。 テーブルlocation(Reg...
コードをコピーコードは次のとおりです。 <!DOCTYPE html PUBLIC "...
「Enter != Submit」問題を実装するには、通常、「ボタンの種類」と「入力ボックスの数」か...
このプロジェクトでは、環境を構築するために Docker コンテナを使用します。Dockerfile...
目次Tomcat でプロジェクトを展開する 3 つの方法プロジェクトをwebappsディレクトリに直...
1 はじめに「DockerでPostgreSQLを起動する方法といくつかの接続ツールのおすすめ」の記...
この記事では、CSS ワープ シャドウの実装コードを紹介し、皆さんと共有します。詳細は以下の通りです...
<br />言葉は、人間の思考や感情を伝えるために必然的に生み出されるものです。人類の文...
目次インストール: 1. ファイアウォールの基本的な使い方2. ファイアウォールd-cmdを設定する...
説明する: Windows 10 に VM をインストールし、VM で Docker を実行し、Do...
目次1. Vue3コンポーネント通信方式2. Vue3通信の使い方2.1 小道具2.2 $エミット2...
## 1最近、docker デプロイメントを学習しており、当初は nginx を docker 化す...
質問Alibaba Cloud イメージを使用して Docker をインストールすると、次の図に示す...
この記事では、Docker コンテナとフロントエンド プロセスの関係と、コンテナを永続的に実行できる...
序文この記事は主に CentOS7 で PHP スケジュールタスクを実行することに関する関連コンテン...