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
相关产品推荐
相关产品推荐

