探索在网页中使用“标注”

2020-11-03 09:52发布

说起“标注”,在HTML5之前,你可能想起的是各种浏览器插件,emmmmmmm或者说你根本不认为浏览器上可以有这种玩意。

但是HTML5来了,这是它的时代。

我们完全可以不借助CSS、JavaScript的力量实现这个东西 —— 因为浏览器实现了 <ruby></ruby> 这个神奇的标签:

<ruby>
	<rb>中文</rb>
	<rp>(</rp><rt>zhongwen</rt><rp>)</rp></ruby>1234

它是这样表现的:
support-ruby
据说在不支持ruby的浏览器中也能这样适应:
dissupport-ruby

除此之外,随着前端的发展,CSS3也给我们带来了“惊喜” —— 文字强调装饰 text-emphasis
text-emphasis家族总共有4个CSS属性,分别是:

  1. text-emphasis

  2. text-emphasis-color

  3. text-emphasis-style

  4. text-emphasis-position

其中,text-emphasistext-emphasis-colortext-emphasis-style这两个CSS属性的缩写,注意,并不包含text-emphasis-position属性,text-emphasis-position属性是独立的!

比如:

<p id="p">aishfaoihfoiahfoahdfoiahfdoshoigsoidshioshghudsihfisjhiodshoishoighdihishoighsoiv</p>//csscolor: red;-webkit-text-emphasis-style: '·';text-emphasis-style: '·';-webkit-text-emphasis-color: red;text-emphasis-color:red;-webkit-text-emphasis-position:under;text-emphasis-position: under;   /* 这里还能是over(上方),不过最好加上left/right组合使用! */12345678910

它是这样表现的:
-webkit-text-emphasis

稍稍有些小遗憾的是:它不能“针对每个字体设置不同的重点标志”,所以常常只用来做辅助突出功能

笔者一直认同的是:能用HTML完成的就不用CSS,能用CSS的就不用JS。并在日常实践中愈发觉得这是一条“至理”!


那么问题来了,现在我想实现这样一个功能:现在的「网页翻译」大多是“页面整体翻译”或者“弹框拖入”,少部分是“选中文字后在文字旁弹出一个提示框”,但是这几种方式不管是哪一种都会有一丝丝的影响:比如遮挡页面其余内容、精确度不高等等。
那能不能“当用户选中文字后在选中文本下方有突出强调、在文本上方出现翻译”呢?

(这个笔者在本文先不说,以免造成“长篇大论”,本文只把实现的基础知识全盘托出!)

首先是HTML:这里我们简单的做一个p标签:

<p id="p">
	aishfaoihfoiahfoahdfoiahfdoshoigsoidshioshghudsihfisjhiodshoishoighdihishoighsoiv</p>123

笔者的思路是:当用户鼠标“抬起”时,去判断有没有选中文本,如果没有则啥事没有、反之则要将这一部分选中的文本替换成标签!

p.onmouseup=function(e){
    var txt = window.getSelection();
	console.log(txt)
	var selectStr = txt.toString();
	console.log(selectStr)
	if(selectStr!==''){
		replaceSelectedStrByEle(txt,selectStr,'nite-writer-pen')
	}}123456789

这里常和JavaScript页面选中事件onselect一起使用:
xxx.onselect=function(){ if(document.selection){ let text=document.selection.createRange().text; //xxx }else{ let p1=xxx.selectionStart; let p2=xxx.selectionEnd; let text=xxx的值(如文本框用xxx.value).substr(p1,p2); } }

这里 window.getSelection() 是浏览器API,专门用于获取用户选中的文本,其具体值用 .toString() 即可获得。

/**
  * 用元素替换被选中的文本
  */var replaceSelectedStrByEle = function(selecter,selectStr,className){
  if (selectStr.trim != "") {
    var rang = selecter.getRangeAt(0);
	var ele = document.createElement("span");
	ele.style.cssText="-webkit-text-emphasis-style: '·';text-emphasis-style: '·';text-emphasis-color:red;-webkit-text-emphasis-position:under;text-emphasis-position:under";
    ele.className = className;
    ele.textContent = selectStr;
    rang.surroundContents(ele);
  }}12345678910111213

selecter.getRangeAt(0):selection API是将每次选中的都保存到内部的数组里,而且是最新的保存到第一个这样的顺序。
没错这里就是用的 -webkit-text-emphasis 突出强调符 —— 如果要为某个元素一次添加多个样式,cssText可以优化性能!

受笔者“信奉”准则的影响,其实在这里一开始还想用纯CSS的 伪类::selection 去做突出强调,但是很不幸的是:这个伪类里面只能改变选中文字的颜色相关:如背景颜色、字体本身颜色。其他的什么都改变不了(不知道为啥,感觉很奇怪:虽说它是子选择器行为,但是其影响应该是和display之流是一样的,并不会产生太大的变动)。诸君请看:
::selection伪类实现


2020-08-30更新
今天又想了一下,::selection不行,那能不能用::selection::after?试了一下,依然不行。也不知道selection伪元素是怎么实现的。。。


ok

selection

(如图)至此,选中状态已经差不多了 —— 至于没说的翻译,这里如果你没有足够的能力建一个“词库”,那么我还是建议你启用“第三方库/插件”或者在线翻译API。这里还有一个问题是:在笔者实践过程中发现,ruby标签是没有办法嵌套在行内元素中的:它会带着其内包裹的文字消失不见 !这一点一定注意。
(估计是因为ruby是一个块级元素吧:w3c规定——行内元素中不能放块级元素,块级元素中虽然能放其它块级或行内元素,但是p标签例外;很尴尬,这两点本文情况全占了)

好了,你总不能让用户一直处于这个状态吧。那就要在一定情况下取消上面的状态 —— 这里笔者也遇到了一些“奇葩”问题:

  1. 什么时候结束选中状态?
    我建议,在点击页面其余空白地方时改变状态 —— 因为为了更好的体验,上面选中使用的mouseup:这里涉及到一个“浏览器事件触发的优先级”。你可以让文本处于“高zIndex区域”、或者用JS去隔离。

  2. 怎么取消?
    对这个才是大问题:你这里可能“理所应当的”想到了“把元素的标签去掉不就完了”,这里你可以尝试一下,可不是一件简单的事。一开始笔者想到了 将dom再转化回string,但是随即想到了这个string怎么插入到父p标签中,而且要插入到原位置!

辗转了一上午,想到了一个“取巧的方法”:因为选中的文本已经是一个dom了,将选中的文本都转化为string,然后再用字符串替换替换掉父p标签的innerText内容的相同之处!~

/**
 * 将dom转化为字符串
*/function nodeToString ( node ) {  
	//createElement()返回一个Element对象
	var tmpNode = document.createElement( "div" ); 
	//appendChild()  参数Node对象   返回Node对象  Element方法
	//cloneNode()  参数布尔类型  返回Node对象   Element方法
	tmpNode.appendChild( node.cloneNode( true ) );  
	var str = tmpNode.innerHTML;  
	tmpNode = node = null;
	return str;  }12345678910111213
mxc.onclick=function(){
	if(document.querySelector('p .nite-writer-pen')){
		let p=document.querySelector('p .nite-writer-pen').parentNode		let nite=document.querySelector('p .nite-writer-pen')
		console.log(nite)
		console.log(p)
		p.innerText=(p.innerHTML).replace(nodeToString(nite),nite.innerText)
	}}123456789

display


作者:X北辰北

链接:https://yunxiaomeng.blog.csdn.net/article/details/108222517

来源:CSDN
著作权归作者所有,转载请联系作者获得授权,切勿私自转载。