この記事では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 設定チュートリアル
初心者は、いくつかの HTML タグを理解することで HTML を学習できます。この入門書は、初心者...
1. /etc/init.dディレクトリに新しいrabbitmqを作成します。 [root@loca...
目次1. 画像1. 鏡とは何ですか? 2. 画像の構成と目的(1) Dockerファイル(2)スクラ...
次の Web デザイン プロジェクトはレスポンシブにする必要があると上司をようやく納得させることがで...
<br />記事と同様に、Web ページにも明確な段落と重要度の異なるタイトルが必要です...
この記事では、jQueryでカルーセルチャートを実装するための具体的なコードを参考までに共有します。...
この記事では、カード ウォーターフォール レイアウトを実現するための CSS3 列のサンプル コード...
テーブルを美しくするために、ヘッダーに異なる境界線の色を設定できます。基本的な構文<TH 境界...
目次1. 参照と反応1. 反応的2.参照2. shallowRef と shallowReactiv...
要素 ui テーブルにはドラッグ アンド ドロップによる並べ替え機能が組み込まれておらず、サードパー...
1. はじめにこの記事には MySQL インストール部分のスクリーンショットがないので、ある程度の基...
基本的な構文CREATE VIEW ステートメントを使用してビューを作成できます。構文の形式は次のと...
私たちの日常的な開発作業では、テキストのオーバーフロー、切り捨て、省略は、考慮する必要がある非常に一...
<br />2006年10月12日のNetEaseの新ホームページの公開から、2008年...
目次序文キーの役割差分アルゴリズムにおけるキーの役割ヘッドノードを同期するテールノードを同期する新し...