You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular中获取textarea删除文本及@提及词删除处理需求

Angular 捕获文本删除内容及@提及词自动删除方案

核心思路

要覆盖所有删除场景(Backspace/Delete、选中替换、剪切、全选清除),不能仅依赖键盘事件,必须通过对比文本前后状态捕获被删内容;针对@提及词,通过正则匹配识别提及范围,检测部分删除行为时自动移除整个提及词。


实现步骤及代码

1. 模板结构

绑定双向数据及关键事件,覆盖所有内容变更场景:

<textarea 
  [(ngModel)]="textContent"
  (input)="handleTextInput()"
  (keydown.backspace)="handleKeyDown($event)"
  (keydown.delete)="handleKeyDown($event)"
  (cut)="handleCut($event)"
  rows="5"
></textarea>

2. 组件核心逻辑

定义必要变量及处理函数:

import { Component } from '@angular/core';

@Component({
  selector: 'app-textarea-delete-handler',
  templateUrl: './textarea-delete-handler.component.html',
  styleUrls: ['./textarea-delete-handler.component.css']
})
export class TextareaDeleteHandlerComponent {
  textContent: string = '';
  prevTextContent: string = '';
  // 可根据需求调整提及词正则,比如允许特殊字符
  private mentionRegex: RegExp = /@[\w\-_]+/g;

  /**
   * 处理文本变更(覆盖输入、删除、替换、粘贴等所有场景)
   */
  handleTextInput(): void {
    // 捕获被删除内容及所属单词
    const deletedContent = this.getDeletedContent(this.prevTextContent, this.textContent);
    if (deletedContent) {
      const relatedWords = this.getRelatedWords(this.prevTextContent, deletedContent);
      // 这里可根据业务需求处理结果(比如上报、存储)
      console.log('被删内容:', deletedContent, '所属单词:', relatedWords);
    }

    // 检测并处理@提及词的部分删除
    this.handlePartialMentionDeletion();

    // 更新历史文本状态
    this.prevTextContent = this.textContent;
  }

  /**
   * 处理Backspace/Delete键,针对光标在提及词内部的情况直接删除整个词
   */
  handleKeyDown(event: KeyboardEvent): void {
    const textarea = event.target as HTMLTextAreaElement;
    const cursorPos = textarea.selectionStart;
    const currentText = textarea.value;

    // 检查光标是否处于某个提及词内部
    const mentions = currentText.match(this.mentionRegex) || [];
    for (const mention of mentions) {
      const startIdx = currentText.indexOf(mention);
      const endIdx = startIdx + mention.length;
      if (cursorPos > startIdx && cursorPos < endIdx) {
        event.preventDefault();
        // 删除整个提及词
        this.textContent = currentText.slice(0, startIdx) + currentText.slice(endIdx);
        // 重置光标位置到提及词原起始处
        setTimeout(() => {
          textarea.selectionStart = startIdx;
          textarea.selectionEnd = startIdx;
        }, 0);
        this.prevTextContent = this.textContent;
        break;
      }
    }
  }

  /**
   * 处理剪切事件,直接捕获选中的剪切内容
   */
  handleCut(event: ClipboardEvent): void {
    const textarea = event.target as HTMLTextAreaElement;
    const selectedText = textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);
    if (selectedText) {
      const relatedWords = this.getRelatedWords(this.prevTextContent, selectedText);
      console.log('被剪切内容:', selectedText, '所属单词:', relatedWords);
    }
  }

  /**
   * 对比前后文本,找出被删除的内容
   */
  private getDeletedContent(prevText: string, currentText: string): string | null {
    if (currentText.length >= prevText.length) return null;

    // 匹配最长公共前缀
    let prefixLen = 0;
    while (prefixLen < prevText.length && prefixLen < currentText.length && prevText[prefixLen] === currentText[prefixLen]) {
      prefixLen++;
    }

    // 匹配最长公共后缀
    let suffixLen = 0;
    while (suffixLen < prevText.length - prefixLen && suffixLen < currentText.length - prefixLen &&
           prevText[prevText.length - 1 - suffixLen] === currentText[currentText.length - 1 - suffixLen]) {
      suffixLen++;
    }

    return prevText.slice(prefixLen, prevText.length - suffixLen);
  }

  /**
   * 找出被删内容所属的单词(按空格分割)
   */
  private getRelatedWords(fullText: string, deletedContent: string): string[] {
    const words = fullText.split(/\s+/);
    return words.filter(word => word.includes(deletedContent));
  }

  /**
   * 检测@提及词是否被部分删除,若是则自动删除整个提及词
   */
  private handlePartialMentionDeletion(): void {
    const prevMentions = this.prevTextContent.match(this.mentionRegex) || [];
    const currentMentions = this.textContent.match(this.mentionRegex) || [];

    prevMentions.forEach(mention => {
      // 原提及词已不存在,且当前文本包含该提及词的部分内容
      if (!currentMentions.includes(mention) && 
          (this.textContent.includes(mention.slice(0, -1)) || this.textContent.includes(mention.slice(1)))) {
        const mentionIdx = this.prevTextContent.indexOf(mention);
        // 删除整个提及词并清理多余空格
        this.textContent = this.prevTextContent.slice(0, mentionIdx) + this.prevTextContent.slice(mentionIdx + mention.length);
        this.textContent = this.textContent.replace(/\s+/g, ' ').trim();
        this.prevTextContent = this.textContent;
      }
    });
  }
}

关键说明

  1. 全场景删除捕获:通过input事件结合前后文本对比,覆盖所有删除/剪切/替换操作,解决单一键盘事件无法覆盖所有场景的问题。
  2. 提及词处理:
    • 键盘删除时,若光标在提及词内部,直接阻止默认行为并删除整个提及词。
    • 选中替换/部分删除提及词时,通过对比前后提及词列表,检测部分删除行为并自动移除整个提及词。
  3. 扩展性:可根据业务需求调整mentionRegex正则规则,或修改getRelatedWords的单词分割逻辑(比如支持其他分隔符)。

内容的提问来源于stack exchange,提问作者Zia Khan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 13:08:11