计算textarea指定行占用软行行数
textarea标签默认自动折行。这种自动折行行为姑且定义为软換行,以区别通过回车键产生的硬換行即分段行为。软換行是文本流自然流动到达边缘后自动换行的行为机制,不像手工换行那样可以通过换行符得到确认,需要额外的计算机制。
textarea不具备自行计算软換行行数的能力,JS也不提供与此相关的API,所以得另辟蹊径。较为廉价的做法是设置一个主要CSS属性和textarea相一致的div或pre盒子,确保文本流在其内的行为和在textarea的一模一样,令该盒子获得textarea内部指定行的文本,然后获取盒子的高度、行高、内边距等数据,最后依据这些数据计算出当行文本的软行数。依据相关原理,计算公式为:
(盒子高度 - 盒子内边距×2) ÷ 行高
还有一个重要因素需要考虑,就是textarea是否出现滚动条。滚动条出现与否直接影响文本流自动折行效果,举例说明一下:假如textarea一个自然行可以容纳20个字符,但当出现了滚动条,它能装下的字数就会减少一两个字符,会减少多少字符取决于字号和浏览器的滚动条空间占位(不同浏览器滚动条占位数值不一样)。这个因素不影响上述计算公式的有效性,我们要做的是动态设置装载textarea指定行文本的盒子的滚动条,以保持其内文本流的流动和textarea的相一致。除此之外,边框、内边距等盒子相关属性也都和滚动条一样影响到自然行装载文本总数,这些将在后面给的代码注释中加以说明。
现在我们可以来看看实例了。以下演示,可以给文本框里第一行文本续上更多的文字,或者拖曳文本框右下角改变其大小,然后点击下方银色背景盒子查看结果(可反复点击):
软行数 : ?
检测运行效果时可以注意观察div盒子对文本框第一行文本的渲染是否和原本的文本流相一致,如果都一致,说明我们设计的算法不会在这里的环境中产生误差,是可用的。以下是上述演示示例的核心代码:
<style> /* 设计思路:用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像素的边框,边框会影响文本流,需要跟进 */ } </style> <textarea id="tbox">0123456789零一二三四五六七八九abcdefgABCDEFG</textarea> <div id="sbox">点击统计首行文本软换行行数</div> <script> // 计算软行数 :这里只针对第一行 const calcSoftLines = () => { // 使用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')[0].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 = () => { let num = calcSoftLines(); alert('软行数:' + num); } </script>
演示示例应该存在考虑不周的现象,影响div模拟文本框文本流自然流动的因素或有遗漏,抑或我设计的算法存在问题。若是,恳请斧正,同时欢迎讨论交流。
前一篇: CSS交互之子元素控制父元素
下一篇: 精准滚动到指定行
评论列表 [1条]
#1 | 悄然 于 2025-3-14 21:24 发布: 看公式,感觉结果软换行的行数不难理解。。看效果,这种交互式的演示太好玩了,直观看出行数和盒子高度的影响。。而且还给个银色的框进行说明。。看代码。。。我还没看。。