马黑黑 发表于 2025-3-14 14:45

计算textarea指定行占用自然行行数

<style>
        .artbox { font: normal 18px/1.5 Simsun, NSimsun, serif; margin: 20px auto; }
        .artbox > p { margin: 8px 0; }
        .tMid { text-align: center; }
        #tbox, #sbox { padding: 8px; width: 90%; font: inherit; white-space: pre-wrap; word-break: break-all; box-sizing: border-box; }
        #tbox { height: 200px; min-height: 80px; min-width: 200px; resize: both;}
        #sbox { min-height: 0; border: 1px solid gray; background: silver; margin: auto; }
</style>

<div class="artbox">
        <p>textarea标签默认自动折行。这种自动折行行为姑且定义为软換行,以区别通过回车键产生的硬換行即分段行为。软換行是文本流自然流动到达边缘后自动换行的行为机制,不像手工换行那样可以通过换行符得到确认,需要额外的计算机制。</p>
        <p>textarea不具备自行计算软換行行数的能力,JS也不提供与此相关的API,所以得另辟蹊径。较为廉价的做法是设置一个主要CSS属性和textarea相一致的div或pre盒子,确保文本流在其内的行为和在textarea的一模一样,令该盒子获得textarea内部指定行的文本,然后获取盒子的高度、行高、内边距等数据,最后依据这些数据计算出当行文本的软行数。依据相关原理,计算公式为:</p>
        <blockquote>(盒子高度 - 盒子内边距×2) ÷ 行高</blockquote>
        <p>还有一个重要因素需要考虑,就是textarea是否出现滚动条。滚动条出现与否直接影响文本流自动折行效果,举例说明一下:假如textarea一个自然行可以容纳20个字符,但当出现了滚动条,它能装下的字数就会减少一两个字符,会减少多少字符取决于字号和浏览器的滚动条空间占位(不同浏览器滚动条占位数值不一样)。这个因素不影响上述计算公式的有效性,我们要做的是动态设置装载textarea指定行文本的盒子的滚动条,以保持其内文本流的流动和textarea的相一致。除此之外,边框、内边距等盒子相关属性也都和滚动条一样影响到自然行装载文本总数,这些将在后面给的代码注释中加以说明。</p>
        <p>现在我们可以来看看实例了。以下演示,可以给文本框里第一行文本续上更多的文字,或者拖曳文本框右下角改变其大小,然后点击下方银色背景盒子查看结果(可反复点击):</p>
        <p class="tMid"><textarea id="tbox">0123456789零一二三四五六七八九abcdefghijklmnoqprstuvwxyzBCDEFGHIJKLMNOPQRSTUVWXYZ</textarea></p>
        <p id="msgbox" class='tMid'>软行数 : ?</p>
        <div id="sbox">点击这里计算textarea首行文本自然折行行数</div>
        <p>检测运行效果时可以注意观察div盒子对文本框第一行文本的渲染是否和原本的文本流相一致,如果都一致,说明我们设计的算法不会在这里的环境中产生误差,是可用的。以下是上述演示示例的核心代码:</p>
        <div id="hEdiv"><pre id="hEpre">
&lt;style&gt;
/* 设计思路:用div模拟textarea文本流流动行为并得到其高度,以此为依据计算文本框某行的软行数 */

/* 文本框和div共同属性设置 */
#tbox, #sbox {
        padding: 8px; /* 内边距 */
        width: 800px; /* 宽度 */
        font: normal 18px/1.5 Simsun, NSimsun, serif; /* 文本(包含了行高) */
        tab-size: 4; /* 制表符占位 */
        white-space: pre-wrap; /* 折行方式 :自动换行 */
        word-break: break-all; /* 折行行为 :所有字符都可以断行 */
        box-sizing: border-box; /* 盒子尺寸规范 :边框内边距都算在盒子尺寸里 */
}
/* 文本框独立设置 */
#tbox {
        height: 200px; /* 高度 */
}
/* div独立设置 */
#sbox {
        min-height: 0; /* 设置最低高度为较小数值,它将会根据文本内容自动撑开或收回高度 */
        border: 1px solid gray; /* textarea默认有1像素的边框,边框会影响文本流,需要跟进 */
}
&lt;/style&gt;

&lt;textarea id="tbox"&gt;0123456789零一二三四五六七八九abcdefgABCDEFG&lt;/textarea&gt;
&lt;div id="sbox"&gt;点击统计首行文本软换行行数&lt;/div&gt;

&lt;script&gt;
// 计算软行数 :这里只针对第一行
const calcSoftLines = () =&gt; {
        // 使用getComputedStyle API获取文本框样式相关数据
        const cssdata = window.getComputedStyle(tbox);
        // 文本框可以改变宽度,所以div需要跟进
        sbox.style.width = tbox.offsetWidth + 'px';
        // div同步文本框纵向滚动条(必要时还应考虑横向滚动条)
        sbox.style.overflowY = tbox.scrollHeight > tbox.clientHeight ? 'scroll' : 'hidden';
        // 将文本框第一行文本复制一份到div里
        sbox.innerText = tbox.value.split('\n').trim();
        // 获取cssdata变量提供的行高(行高CSS可以设置1.5或2之类的数字,是倍数值,不是像素值,所以得计算)
        const lineheight = parseFloat(cssdata.getPropertyValue('line-height'));
        // 获取cssdata变量提供的内边距(复杂内边距需要更复杂的计算,这里假设是一个单值padding)
        const padding = parseFloat(cssdata.getPropertyValue('padding'));
        // 计算软行数(不同的字体等环境下会产生浮点数结果,向下取整数值得到正确结果)
        const lines = Math.floor((sbox.offsetHeight - padding * 2) / lineheight);
        return lines;
}

//div盒子单击事件 :运行 calcSoftLines 函数
sbox.onclick = () =&gt; {
        let num = calcSoftLines();
        alert('软行数:' + num);
}
&lt;/script&gt;
        </pre></div>
        <p>演示示例应该存在考虑不周的现象,影响div模拟文本框文本流自然流动的因素或有遗漏,抑或我设计的算法存在问题。若是,恳请斧正,同时欢迎讨论交流。</p>
</div>

<script type="module">
import hlight from 'https://638183.freep.cn/638183/web/helight/helight1.js';

const calcSoftLines = () => {
        const cssdata = window.getComputedStyle(tbox);
        sbox.style.width = tbox.offsetWidth + 'px';
        sbox.style.overflowY = tbox.scrollHeight > tbox.clientHeight ? 'scroll' : 'hidden';
        sbox.innerText = tbox.value.split('\n').trim();
        const lineheight = parseFloat(cssdata.getPropertyValue('line-height'));
        const padding = parseFloat(cssdata.getPropertyValue('padding'));
        const lines = Math.floor((sbox.offsetHeight - padding * 2) / lineheight);
        return lines;
}

sbox.onclick = () => {
        msgbox.innerText = '软行数信息 : ' + calcSoftLines();
}

hlight.hl(hEdiv, hEpre);
</script>

红影 发表于 2025-3-14 16:56

去试了一下示例,十分灵验,很好呢{:4_187:}

红影 发表于 2025-3-14 16:57

其实软換行也就是自然换行吧{:4_204:}

马黑黑 发表于 2025-3-14 18:43

红影 发表于 2025-3-14 16:57
其实软換行也就是自然换行吧

是这个意思。web中,盒子的文本默认自动折行,可以设置为不允许折行,

马黑黑 发表于 2025-3-14 18:46

红影 发表于 2025-3-14 16:56
去试了一下示例,十分灵验,很好呢
这个,我曾问过DS怎么做一个代码自动折行、支持行号显示的Web编辑器,它反复提到自动折行的话涉及的算法非常复杂,然后丢给我一个示例,很粗糙,行号对不上代码行,也没有自动折行的计算机制。我又去问通义,得到的结果大同小异。于是自己花点时间思考、尝试,做出个大概。

红影 发表于 2025-3-14 20:13

马黑黑 发表于 2025-3-14 18:43
是这个意思。web中,盒子的文本默认自动折行,可以设置为不允许折行,

不允许这行太难受了,横向滚动条要长了。

红影 发表于 2025-3-14 20:14

马黑黑 发表于 2025-3-14 18:46
这个,我曾问过DS怎么做一个代码自动折行、支持行号显示的Web编辑器,它反复提到自动折行的话涉及的算法 ...

这个已经超出它们的知识范畴了{:4_173:}

马黑黑 发表于 2025-3-14 21:12

本帖最后由 马黑黑 于 2025-3-14 21:13 编辑

红影 发表于 2025-3-14 20:13
不允许这行太难受了,横向滚动条要长了。
这个也不能这么说。之前介绍过女程序员的作品 prism.js,它是目前最轻量级的代码着色框架(可以小到只有10多KB),它就禁用纵向滚动条、启用纵向滚动条,这就是设置了禁用自动折行。

这些处理方式都是根据需要来的:代码编写规范中有横向占用多少个字符的规范,一般是80个,就是说,一行代码不要太长。另外,一旦允许自动折行,就得像我本文所使用的方式或其它方式去计算自然行数,增加了额外的算法开销,所以为了规避过多的算法,折中的做法是最恰当的。

小辣椒 发表于 2025-3-14 21:27

小辣椒是雾里看花{:4_170:}

红影 发表于 2025-3-14 21:27

马黑黑 发表于 2025-3-14 21:12
这个也不能这么说。之前介绍过女程序员的作品 prism.js,它是目前最轻量级的代码着色框架(可以小到只有1 ...

嗯,这个说法有理,不能无限制地增长,会造成一些麻烦的{:4_187:}

花飞飞 发表于 2025-3-14 22:13

首先看到这个換字好象有点不太一样。。{:4_173:}

花飞飞 发表于 2025-3-14 22:13

这个还是编辑器的事,之前编辑器和着色代码就是太长的该换就换了,还显示为一个同一行
这个软换行就是换了之后,会自动根据公式算出它有几行。。
看演示很成功的,又能看到一个新效果了。{:4_199:}

马黑黑 发表于 2025-3-14 22:23

花飞飞 发表于 2025-3-14 22:13
这个还是编辑器的事,之前编辑器和着色代码就是太长的该换就换了,还显示为一个同一行
这个软换行就是换了 ...

编辑器自动折行的行号在web里不能么好处理,需要有一个计算一个 \r\n 的硬行里面实际占用的自然行是多少行

马黑黑 发表于 2025-3-14 22:24

花飞飞 发表于 2025-3-14 22:13
首先看到这个換字好象有点不太一样。。

其实都一个道理,就像编写Word文档一样,文本到了右边缘都会自动折行

马黑黑 发表于 2025-3-14 22:24

红影 发表于 2025-3-14 21:27
嗯,这个说法有理,不能无限制地增长,会造成一些麻烦的

一切设计主要是看使用需求

马黑黑 发表于 2025-3-14 22:25

小辣椒 发表于 2025-3-14 21:27
小辣椒是雾里看花

一个字:美

红影 发表于 2025-3-14 22:36

马黑黑 发表于 2025-3-14 22:24
一切设计主要是看使用需求

当然了,实用性是首要的。

花飞飞 发表于 2025-3-14 22:38

马黑黑 发表于 2025-3-14 22:23
编辑器自动折行的行号在web里不能么好处理,需要有一个计算一个 \r\n 的硬行里面实际占用的自然行是多少 ...

{:4_173:}好哒,小白联想的天马行空~~
平时用得多的是查看源文件,比如你出了新贴,我在你网站打开后查看源文件,有些部分显示为一行,超级长,它也不换行。。

马黑黑 发表于 2025-3-14 22:39

花飞飞 发表于 2025-3-14 22:38
好哒,小白联想的天马行空~~
平时用得多的是查看源文件,比如你出了新贴,我在你网站打开后查 ...

这都根据当时处理问题的情况而定

花飞飞 发表于 2025-3-14 22:39

马黑黑 发表于 2025-3-14 22:24
其实都一个道理,就像编写Word文档一样,文本到了右边缘都会自动折行

嗯哪这个是天经地义自然而然的事{:4_173:}
页: [1] 2 3 4
查看完整版本: 计算textarea指定行占用自然行行数