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

JS实现输入框修改选中内容字号时保持选区选中状态

解决contenteditable选区点击输入框消失并实时修改字号的问题

核心问题

点击输入框时,浏览器会自动取消contenteditable区域的文本选区,导致无法直接对原选区应用字号修改。需要先保存选区状态,修改完成后再恢复选区。

解决方案

通过保存选区范围、实时监听输入变化、修改后恢复选区三个步骤实现需求,同时优化原代码的DOM操作逻辑:

完整代码实现

<!-- HTML部分 -->
<div contenteditable="true" id="editor" style="min-height: 200px; border: 1px solid #ccc; padding: 10px;">
  选中这段文字,然后输入字号试试
</div>
<li class="nav-item">
  <input type="number" id="size" class="btn btn-primary my-2 mx-1 options" style="width: 80px;" placeholder="字号" />
</li>
// JavaScript部分
let savedRange = null;
const editor = document.getElementById('editor');
const sizeInput = document.getElementById('size');

// 保存选区:在编辑器失去焦点前记录当前选区
editor.addEventListener('blur', () => {
  const selection = window.getSelection();
  if (selection.rangeCount > 0) {
    savedRange = selection.getRangeAt(0);
  }
});

// 实时监听输入框变化,修改字号并恢复选区
sizeInput.addEventListener('input', () => {
  const fontSize = `${sizeInput.value}px`;
  if (!savedRange || !fontSize) return;

  // 先移除选区的原有字号样式(避免多层span嵌套)
  removeFontSizeFromRange(savedRange);
  
  // 对选区应用新字号
  applyFontSizeToRange(savedRange, fontSize);

  // 恢复选区
  const selection = window.getSelection();
  selection.removeAllRanges();
  selection.addRange(savedRange);
});

// 给选区文本应用字号样式
function applyFontSizeToRange(range, fontSize) {
  const content = range.extractContents();
  const span = document.createElement('span');
  span.style.fontSize = fontSize;

  // 处理文本节点和元素节点
  processNodes(content, span, fontSize);
  range.insertNode(content);

  // 更新savedRange到新插入的内容范围
  savedRange = document.createRange();
  savedRange.selectNodeContents(content);
}

// 递归处理节点,避免重复嵌套span
function processNodes(parent, templateSpan, fontSize) {
  for (let i = 0; i < parent.childNodes.length; i++) {
    const node = parent.childNodes[i];
    if (node.nodeType === Node.TEXT_NODE) {
      // 文本节点直接用模板span包裹
      const newSpan = templateSpan.cloneNode(false);
      newSpan.appendChild(node.cloneNode(true));
      parent.replaceChild(newSpan, node);
    } else if (node.nodeType === Node.ELEMENT_NODE) {
      // 如果是已有的span,直接修改样式,不用嵌套
      if (node.tagName === 'SPAN') {
        node.style.fontSize = fontSize;
      } else {
        // 其他元素递归处理子节点
        processNodes(node, templateSpan, fontSize);
      }
    }
  }
}

// 移除选区的字号样式(可选,避免样式冲突)
function removeFontSizeFromRange(range) {
  const content = range.cloneContents();
  const spans = content.querySelectorAll('span[style*="font-size"]');
  spans.forEach(span => {
    // 将span的子节点替换掉span本身
    const parent = span.parentNode;
    while (span.firstChild) {
      parent.insertBefore(span.firstChild, span);
    }
    parent.removeChild(span);
  });
}

关键说明

  • 保存选区:在编辑器失去焦点时记录当前选区的Range对象,确保点击输入框后不会丢失选区信息
  • 实时响应:使用input事件代替onchange,输入框内容变化时立即触发字号修改
  • 避免DOM嵌套:处理节点时优先修改已有span的样式,而不是重复嵌套新的span,保持DOM结构简洁
  • 恢复选区:修改完成后重新将选区设置到更新后的内容上,保持选中状态

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 02:34:57