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

Svelte中实现文本域自动换行(Word Wrap)时行号同步匹配换行效果

Svelte中实现文本域自动换行(Word Wrap)时行号同步匹配换行效果

嘿,我完全明白你遇到的问题了——当文本域里的长单词自动换行时,行号没法跟着同步“断行”,要么行号数量不够,要么和文本不对齐,确实影响体验。咱们来一步步解决这个问题!

问题根源

你原来的实现是基于**实际换行符(\n)**生成行号的,每个换行符对应一个行号元素。但浏览器的自动换行是纯排版行为,不会产生新的换行符,所以那些被自动拆分的行没有对应的行号元素,导致行号容器的高度和文本域脱节,行号自然没法和文本对齐。

解决方案思路

核心是让行号和文本的换行行为完全同步:我们需要计算每个实际行(由\n分隔的行)会被自动拆分成多少行,然后为每个自动换行的行生成相同的实际行号。具体来说,我们用一个临时元素模拟文本域的排版环境,计算每个实际行的自动换行次数,再生成对应的行号数组。

修改后的完整代码

<script lang="ts">
import { onMount } from 'svelte';

export let source = `jon

sup

me

fixin to do sumthin good that is really long and will wrap around multiple lines in the textarea`;

let lineNumbersArray: number[] = [];
let textareaElement: HTMLTextAreaElement;

// 计算并更新行号数组
function updateLineNumbers() {
  if (!textareaElement) return;
  
  // 创建临时元素模拟文本域的排版,计算每行的自动换行次数
  const tempElement = document.createElement('span');
  tempElement.style.fontFamily = 'monospace';
  tempElement.style.lineHeight = '21px';
  tempElement.style.whiteSpace = 'pre-wrap';
  tempElement.style.position = 'absolute';
  tempElement.style.visibility = 'hidden';
  tempElement.style.width = `${textareaElement.offsetWidth}px`;
  document.body.appendChild(tempElement);
  
  const lines = source.split('\n');
  lineNumbersArray = [];
  
  lines.forEach((line, index) => {
    // 空行也要计算高度,避免行号缺失
    tempElement.textContent = line || ' ';
    // getClientRects()会返回每个自动换行后的行的位置信息
    const lineCount = tempElement.getClientRects().length;
    // 为每个自动换行的行添加相同的行号
    for (let i = 0; i < lineCount; i++) {
      lineNumbersArray.push(index + 1);
    }
  });
  
  document.body.removeChild(tempElement);
}

const KeyUp = (event: Event) => {
  const textarea = event.target as HTMLTextAreaElement;
  source = textarea.value;
  updateLineNumbers();
};

const KeyDown = (event: Event) => {
  const textarea = event.target as HTMLTextAreaElement;
  if ((event as KeyboardEvent).key === 'Tab') {
    const start = textarea.selectionStart;
    const end = textarea.selectionEnd;
    if (start !== null && end !== null) {
      source = source.substring(0, start) + '\t' + source.substring(end);
      textarea.value = source;
      // 恢复光标位置,提升输入体验
      textarea.selectionStart = start + 1;
      textarea.selectionEnd = start + 1;
      event.preventDefault();
      updateLineNumbers();
    }
  }
};

// 组件挂载后初始化行号,监听窗口大小变化
onMount(() => {
  updateLineNumbers();
  const handleResize = () => updateLineNumbers();
  window.addEventListener('resize', handleResize);
  return () => window.removeEventListener('resize', handleResize);
});
</script>

<center>
  <div class="editor">
    <div class="line-numbers">
      {#each lineNumbersArray as lineNumber}
        <span class="line-number">{lineNumber}</span>
      {/each}
    </div>
    <textarea 
      bind:this={textareaElement}
      class="text-content"
      on:keyup={KeyUp} 
      on:keydown={KeyDown} 
      bind:value={source} 
      rows="15" 
    />
  </div>
</center>

<style>
.editor {
  display: inline-flex;
  gap: 10px;
  font-family: monospace;
  line-height: 21px;
  background: #282a3a;
  border-radius: 2px;
  padding: 20px 10px;
  align-items: flex-start; /* 确保行号与文本顶部对齐 */
}

.line-numbers {
  width: 20px;
  text-align: right;
}

.line-number {
  display: block;
  color: #506882;
}

.text-content {
  line-height: 21px;
  overflow-y: auto; /* 内容过多时允许滚动 */
  padding: 0;
  border: 0;
  background: #282a3a;
  color: #fff;
  min-width: 500px;
  outline: none;
  resize: both; /* 允许调整文本域大小 */
  white-space: pre-wrap; /* 开启自动换行 */
  word-wrap: break-word;
}
</style>

关键细节说明

  1. 临时元素模拟排版:我们创建一个和文本域样式完全一致的临时元素,通过getClientRects()获取每个实际行被自动拆分后的行数,确保行号数量和文本的实际行数完全匹配。
  2. 实时更新行号:监听文本内容变化、Tab键输入、窗口大小变化,随时重新计算行号数组,保证行号始终和文本对齐。
  3. CSS对齐优化:设置align-items: flex-start让行号和文本顶部对齐,文本域开启white-space: pre-wrap和word-wrap: break-word确保自动换行行为正常。

现在测试一下效果:输入长文本时,自动换行的行都会显示对应的实际行号,行号和文本完全对齐,就像Notepad++或GitHub的代码块那样啦!

备注:内容来源于stack exchange,提问作者Jonathan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.23 08:23:10