JS文本换行算法-模拟计算文字换行位置-基于DOM元素自发换行行为和字符分割原理-支持实体编码、不支持标签嵌套和富文本...

本文主要是介绍JS文本换行算法-模拟计算文字换行位置-基于DOM元素自发换行行为和字符分割原理-支持实体编码、不支持标签嵌套和富文本...,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

简介
之前在学习HTML的时候一直很想弄清楚HTML内部换行的逻辑,特别是有时候我们想知道一个字符串放入一个DOM元素之后究竟在哪个字符位发生的换行,然后就可以知道在一个固定宽高且隐藏溢出的容器中当前用户看见的字符到底有多少个,具体是哪几个等。然而,原生的HTML并没有提供这个功能,所以就要自己写算法来实现咯。

下一篇实现小程序文本换行算法

1.原理解析1.1 常见HTML换行效果
想要模拟HTML的换行的话,首先要对HTML自发换行的原理有一些了解,在我所学的知识中,HTML的换行逻辑是这样的。对于一个标签内文字的渲染如:

<div style="width: 200px;border: 1px solid #000;">1 3&nbsp;sad中文       12o3iaslkdjaksldj aslkdj alskdjklsj    这是一段测试文字,没有什么实际含义。 no sense! 123  
</div>

渲染出来的效果是这样的:

1.2文本换行相关的因素
简单分析一下html处理换行的逻辑:
首先,截止20190725,HTML中DOM元素内文本换行的逻辑主要和以下几个方面有关:

  1. 容器的宽度width;
  2. 文本的样式,如font-size等;
  3. 容器的空白符处理逻辑,主要是看标签容器的white-space属性;
  4. 容器的单词截断逻辑,主要是看标签容器的word-wrap(或叫overflow-wrap)和word-break属性;关于,white-space,word-wrap,word-break这三个属性,大家可以参考下面的文字文章,或自己去实验看看。作者:杭电茶娃,链接:https://blog.csdn.net/c11073138/article/details/79534394 ,标题如下:常见样式问题七、word-break、word-wrap、white-space区别

1.3HTML换行原理简单分析
一个html标签,如上面1.1中的div,文本换行的时候其实做了如下的操作:
1.取出标签中间的文字存入字符串,去掉首尾空格;
2.根据white-space的空白符逻辑完成空白符替换;
3.根据word-break和word-wrap进行单字或单词分割。
4.渲染出div的宽高和文字的样式,往容器中填充文字,当文字宽度溢出时进行换行。
5.一些额外的细节处理,这里就不说了,大家可以多去实验观察一下,我也是实验试出来的。

1.4我的解决方案
要求外部传入一个字符串str,和需要模拟的容器的样式styles,自动在模拟换行的位置插入\n。具体操作如下:
1.创建一个div容器,将styles样式设置到div上,将str作为div的innerHTML。
2.获取div的innerText作为空白符处理的结果(innerText是innerHTML解析并按照white-space进行空白符替换的结果)
3.对innerText进行分割,先用正则表达式\b(单词边界)进行初次分割,在根据word-wrap和word-break的设置进行二次分割。最终得到一个数组,如字符串"123你好 ab 2就2d"默认会被分割为[“123”,“你”,“好”," “,“ab”,” ",“2”,“就”,“2d”]。
4.将div容器转变为行容器,即限制高度为1个font-size,尝试往行容器中添加单字或单词,当添加一个单字时行容器的实际高度高于限制高度时,说明产生了换行,则将行容器中已有的字符认为是一行,清空行容器,并从当前单词或单字开始重复进行试添加。
5.在需要换行的地方插入’\n’得到最终结果。

1.5注意点
HTML在进行单词或单字分割的时候,会区分CJK字符与非CJK字符,CJK即Chinese、Japanese、Korean,是指以中日韩统一文字为代表的字符。CJK字符相比起英文等字符的区别在于,CJK字符可以在任意字符处换行,不像英文字符默认情况下只允许在空格处或部分标点符号处换行。
代码中包含CJK字符的正则识别:

2.上代码

//代码风格模仿jQuery,用闭包和对象存储函数
;(function(window,document,$){
//使用示例:dc.getWrapString('asl kaswdasdsadjsk 中文加快速度 12123奥术大师是的撒 asdsds sadfsfdsgf asdsafsdfdsf3',{"font-size":"12px","width":"80px"});
var dc = {//通过传入的字符串、容器宽度、字体大小样式等进行模拟计算,自动在需要换行的地方拼接上换行符\n,效果类似于html的换行算法,默认将所有空白符当成一个空格处理getWrapString: function(str,styles){//传入的str需是能被html识别的字符串,暂不支持富文本,函数返回值也是字符串if(!str||str.length<=0) return '';/*解释:此函数是用于模拟html中处理文本换行的逻辑,但未必可以完全模拟,大多时候只用于模拟定宽div标签和pre标签的换行行为html中控制文本换行主要由white-space、word-break(或word-wrap(别名overflow-wrap))这些属性来控制.其中word-wrap(overflow-wrap)比较简单,取值只有break-word和normal。控制英文单词是否允许换行时截断word-break类似于word-wrap但是它有一个取值keep-all比较特殊,这里不做过多说明。在dom元素中渲染文字换行时,首先根据white-space的取值去替换空白符,再结合word-wrap和word-break去做换行截断。其中CJK字符和非CJK字符在换行时有一些区别。CJK可简单理解为中国日本韩国文字字符,意义是可以在任意字符处换行的字符,不像英文等字符常规状态下需要把单词当做一个整体,遇到空格才允许换行。此算法将大部分逻辑交由DOM元素的自发行为去完成,主要的功能在于计算出换行的位置*///1.将str填充到一个不可见的容器中,利用html的空白符逻辑进行初步处理var $div = $('<div></div>').html(str).appendTo('body').css({"position": "absolute","z-index": -1,"opacity": 0,"filter": "alpha(opacity=0)","-ms-filter": "alpha(opacity=0)"});//styles为容器的样式,需外部传入宽度,另可以传入font-size、white-space等信息try{$div.css(styles);}catch(e){}//1.1获取初步处理的字符串,即已完成空白符替换,但还未计算溢出换行。var rawText = $div.text();//1.2复用刚才的文本容器作为行容器,增加一些所需样式var $line = $div.empty().css({"overflow": "hidden",//原本是为了方便计算水平溢出,但是后面我发现其实用不上"line-height": "1"//设置行高为1个font-size便于计算是否产生换行});//1.3记录word-wrap等设置var isBreakWord = ($line.css('word-wrap')+$line.css('word-break')).indexOf('break')>=0;var lineHeight = parseFloat($line.css("font-size"));//记录行高判断是否产生换行//下面将上面初步处理的字符串进行字符分割。var words = [],temp = rawText.split(/\b/);//根据单词边界进行初次分割,分割后的non-CJK字符会变成一个整体,但CJK字符需要进行二次分割//2.对英文单词或一串中文进行二次分割for(var i=0;i<temp.length;i++){//将包含CJK字符的项进行单字分割后存入words数字中 //另外,如果设置了break-word则将英文单词分割为单字if(dc.isCJKCharacter(temp[i])||isBreakWord){words.push.apply(words,temp[i].split(''));}else{//不包含CJK字符的项则视为一个整体直接存进words数组words.push(temp[i]);}}//预留用一个二维数组保存计算结果,如lines=[['12',' ','中'],['asdf','哈']]则表示结果有2行,第一行为'12 中',第二行为'asdf哈'var lines = [],oneline=[];//oneline是一维数组,保存一行中的单词//3.遍历求解换行位置,即尝试向行容器中添加字符。for(var i=0;i<words.length;i++){var word = words[i];if(word=='\n'){//如果当前单词是换行符//则将之前的内容存为1行,并追加保存一个空行lines.push(oneline,[]);//清空行容器$line.empty();//开始记录新行oneline = [];//结束本次循环continue;}$line.append(word);//往行容器中增加内容if($line.height()<=lineHeight){//如果没有产生换行//将当前单词或单字追加记录到oneline中oneline.push(word);}else{//如果产生了换行//前面的单词存为一行lines.push(oneline);//清空行容器$line.empty();//开始记录新行oneline=[];//回退,在新行中尝试添加当前单词或单字i--;continue;}}//3.1矫正,手动插入最后一行。lines.push(oneline);//4.收尾工作//4.1将二维数组还原为字符串var string = '';for(var i=0;i<lines.length;i++){string+=lines[i].join('')+'\n';}string = string.substring(0,string.length-1);//4.2移除行容器$line.remove();//返回计算结果return string;},//传入一个字符串,判断是否包含CJK字符串,CJK即中国日本韩国文字字符。isCJKCharacter: function(ch){/*参考网站:https://blog.csdn.net/iteye_4476/article/details/81652883,来源Unicode官网CJK字符不同于其他字符,CJK字符可以在任意地方换行,不像英文字符等正常情况必须在单词后换行截止20190725,根据我查到的资料(Unicode 5.0版),js中的Unicode为2个字节,CJK字符在2个字节的unicode中包括以下范围//大多数情况下,可以取1.~5.来判断CJK字符1.标准CJK文字1.1 [\u3400-\u3db5] => CJK统一表意文字扩展A (发行版3.0)1.2 [\u4e00-\u9fa5] => CJK统一表意文字 (发行版1.1) (常用来简单判断中文字符)1.3 [\u9fa6-\u9fbb] => CJK统一表意文字 (发行版4.1)1.4 [\uf900-\ufa2d] => CJK兼容文字 (发行版1.1)1.5 [\ufa30-\ufa6a] => CJK兼容文字 (发行版3.2)1.6 [\ufa70-\ufad9] => CJK兼容文字 (发行版4.1)(以下两项编码超过两个字节,js中暂时不能用,但是还是写一下(js中无法使用的编码范围前面有双斜杠//标志)://1.7 [\u20000-\u2a6d6] => CJK统一表意文字扩展B (发行版3.1)//1.8 [\u2f800-\u2fa1d] => CJK兼容补充 (发行版3.1)) 2. [\uff00-\uffef] => 全角中英文标点符号、半宽片假名、半宽平假名、半宽韩文字母3. [\u2e80-\u2eff] => CJK部首补充4. [\u3000-\u303f] => CJK标点符号5. [\u31c0-\u31ef] => CJK笔划6. [\u2f00-\u2fdf] => 康熙部首7. [\u2ff0-\u2fff] => 汉字结构描述字符8. [\u3100-\u312f] => 注音符号9. [\u31a0-\u31bf] => 注音符号(闽南语客家语扩展)10. [\u3040-\u309f] => 日文平假名11. [\u30a0-\u30ff] => 日文片假名12. [\u31f0-\u31ff] => 日文片假名拼音扩展13. [\uac00-\ud7af] => 韩文拼音14. [\u1100-\u11ff] => 韩文字母15. [\u3130-\u318f] => 韩文兼容字母//16. [\u1d300-\u1d35f] => 太玄经符号17. [\u4dc0-\u4dff] => 易经六十四卦象18. [\ua000-\ua48f] => 彝文音节19. [\ua490-\ua4cf] => 彝文部首20. [\u2800-\u28ff] => 盲文符号21. [\u3200-\u32ff] => CJK字母及月份22. [\u3300-\u33ff] => CJK特殊符号(日期合并)23. [\u2700-\u27bf] => 装饰符号(非CJK专用)24. [\u2600-\u26ff] => 杂项符号(非CJK专用)25. [\ufe10-\ufe1f] => 中文竖排标点26. [\ufe30-\ufe4f] => CJK兼容符号(竖排变体、下划线、顿号)*/var cjk = {NO1Unihan: ['\\u3400-\\u3db5','\\u4e00-\\u9fa5','\\u9fa6-\\u9fbb','\\uf900-\\ufa2d','\\ufa30-\\ufa6a','\\ufa70-\\ufad9','',''],NO2UFF00: ['\\uff00-\\uffef'],NO3U2E80: ['\\u2e80-\\u2eff'],NO4U3000: ['\\u3000-\\u303f'],NO5U31C0: ['\\u31c0-\\u31ef'],NO6U2F00: ['\\u2f00-\\u2fdf'],NO7U2FF0: ['\\u2ff0-\u2fff'],NO8U3100: ['\\u3100-\\u312f'],NO9U31A0: ['\\u31a0-\\u31bf'],NO10U3040: ['\\u3040-\\u309f'],NO11U30A0: ['\\u30a0-\\u30ff'],NO12U31F0: ['\\u31f0-\\u31ff'],NO13UAC00: ['\\uac00-\\ud7af'],NO14U1100: ['\\u1100-\\u11ff'],NO15U3130: ['\\u3130-\\u318f'],NO16U1D300: [''],//\\u1d300-\\u1d35fNO17U4DC0: ['\\u4dc0-\\u4dff'],NO18UA000: ['\\ua000-\\ua48f'],NO19UA490: ['\\ua490-\\ua4cf'],NO20U2800: ['\\u2800-\\u28ff'],NO21U3200: ['\\u3200-\\u32ff'],NO22U3300: ['\\u3300-\\u33ff'],NO23U2700: ['\\u2700-\\u27bf'],NO24U2600: ['\\u2600-\\u26ff'],NO25UFE10: ['\\ufe10-\\ufe1f'],NO26UFE30: ['\\ufe30-\\ufe4f']};var reg,str='[';for(var k in cjk){str+=cjk[k].join('');}str+=']+';if(str!='[]+'){reg = new RegExp(str,'m');return reg.test(ch);}return null;}
};
window.dc = dc;
dc.getWrapString('asl kaswdasdsadjsk 中文加快速度 12123奥术大师是的撒 asdsds sadfsfdsgf asdsafsdfdsf3',{"font-size":"12px","width":"80px"});
})(window,document,jQuery);

3.运行结果

可以看到,代码计算出的换行位置和html的显示效果是一致的,我觉得基本满足我的需求了,就没有继续再往下写了,如果要完全模拟html的文字换行机制从而搬到其他语言的图形界面中,那么就会涉及到标签的渲染和实体编码的替换了,分析起来还是挺麻烦的,就到此为止吧。

4.结语
本文写于20190729,算法其实上周就写好了,不过没有时间整理成博客,整理成博客之后也没有太多时间去雕琢。写出来是方便自己查阅,也让一些和我一样曾经纠结于html的换行机制的小伙伴们有一些启发。最后还是那句话,走过路过点个赞再走呗。被人关注还是有一些开心的。
————————————————
版权声明:本文为CSDN博主「月桦剑士」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_26909801/article/details/97298710

这篇关于JS文本换行算法-模拟计算文字换行位置-基于DOM元素自发换行行为和字符分割原理-支持实体编码、不支持标签嵌套和富文本...的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/170038

相关文章

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

hdu1254(嵌套bfs,两次bfs)

/*第一次做这种题感觉很有压力,思路还是有点混乱,总是wa,改了好多次才ac的思路:把箱子的移动当做第一层bfs,队列节点要用到当前箱子坐标(x,y),走的次数step,当前人的weizhi(man_x,man_y),要判断人能否将箱子推到某点时要嵌套第二层bfs(人的移动);代码如下:

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

hdu4407(容斥原理)

题意:给一串数字1,2,......n,两个操作:1、修改第k个数字,2、查询区间[l,r]中与n互质的数之和。 解题思路:咱一看,像线段树,但是如果用线段树做,那么每个区间一定要记录所有的素因子,这样会超内存。然后我就做不来了。后来看了题解,原来是用容斥原理来做的。还记得这道题目吗?求区间[1,r]中与p互质的数的个数,如果不会的话就先去做那题吧。现在这题是求区间[l,r]中与n互质的数的和