正则匹配到了只是段落内标签的结果啊,外面的没有啊。哦,对,有匹配到的索引,上次匹配到的位置加上上次处理的长度,就是一段直接文本的开始。下一次匹配到的索引-1就是这段直接文本的结束。这只是匹配过程中的,还有首尾要单独处理。又回到烦的老路上去了。。。 这么烦,一个段落分隔能这么繁琐,我不信! 突然想到了,有文本节点这么个东西,删繁就简嘛,正则先到边上去,直接处理段落的所有节点不就行了。 文本节点则分隔直接包裹,标签节点则对内容进行包裹,这种情况下处理的直接是dom,更省事。 文本节点里放标签?这是在开玩笑么,是也不是。文本节点里确实只能放文本,但是我把标签直接放进去,它会自动转义,那最后再替换出来不就行了。 好了,方案终于有了,而且这个方案逻辑多简单,代码逻辑自然也不会烦。 /** * 正文内容分段处理 * @param {jQueryObject/HTMLElement/String} $content 要处理的正文jQ对象或HTMLElement或其对应选择器 */ function splitConent($content) { $content = $($content); $content.find(splitConfig.unitTag).each(function (index, item) { var $item = $(item), text = $.trim($item.text()); if (!text) return; var nodes = $item[0].childNodes; $.each(nodes, function (i, node) { switch (node.nodeType) { case 3: // text 节点 // 由于是文本节点,标签被转义了,后续再转回来 node.data = '<' + splitConfig.wrapTag + '>' + node.data.replace(splitConfig.splitReg, '$&<' + splitConfig.wrapTag + '>') + ''; break; case 1: // 元素节点 var innerHtml = node.innerHTML, start = '', end = ''; // 如果内部还有直接标签,先去掉 var startResult = /^<\w+?>/.exec(innerHtml); if (startResult) { start = startResult[0]; innerHtml = innerHtml.substr(start.length); } var endResult = /<\/\w+?>$/.exec(innerHtml); if (endResult) { end = endResult[0]; innerHtml = innerHtml.substring(0, endResult.index); } // 更新内部内容 node.innerHTML = start + '<' + splitConfig.wrapTag + '>' + innerHtml.replace(splitConfig.splitReg, '$&<' + splitConfig.wrapTag + '>') + '' + end; break; default: break; } }); // 处理文本节点中被转义的html标签 $item[0].innerHTML = $item[0].innerHTML .replace(new RegExp('<' + splitConfig.wrapTag + '>', 'g'), '<' + splitConfig.wrapTag + '>') .replace(new RegExp('</' + splitConfig.wrapTag + '>', 'g'), ''); $item.find(splitConfig.wrapTag).addClass(splitConfig.wrapCls); }); } 上面代码中最后对文本节点中被转义的包裹标签替换似乎有点麻烦,但是没办法,ES5之前JavaScript并不支持正则的后行断言(也就是正则表达式中“后顾”)。所以没办法对包裹标签前后的 < 和 > 进行精准替换,只能连同标签名一起替换。 事件处理 在上面完成了文本获取和段落分隔,下面要做的就是鼠标移动上去时获取文本触发朗读即可,移开时停止朗读即可。 鼠标移动,只读一次,基于这两点原因,使用 mouseenter 和 mouseleave 事件来完成。 原因: 不冒泡,不会触发父元素的再次朗读 不重复触发,一个元素内移动时不会重复触发。 /** * 在页面上写入高亮样式 */ function createStyle() { if (document.getElementById('speak-light-style')) return; var style = document.createElement('style'); style.id = 'speak-light-style'; style.innerText = '.' + splitConfig.hightlightCls + '{' + splitConfig.hightStyle + '}'; document.getElementsByTagName('head')[0].appendChild(style); } // 非正文需要朗读的标签 逗号分隔 var speakTags = 'a, p, span, h1, h2, h3, h4, h5, h6, img, input, button'; $(document).on('mouseenter.speak-help', speakTags, function (e) { var $target = $(e.target); // 排除段落内的 if ($target.parents('.' + splitConfig.wrapCls).length || $target.find('.' + splitConfig.wrapCls).length) { return; } // 图片样式单独处理 其他样式统一处理 if (e.target.nodeName.toLowerCase() === 'img') { $target.css({ border: '2px solid #000' }); } else { $target.addClass(splitConfig.hightlightCls); } // 开始朗读 speakText(getText(e.target)); }).on('mouseleave.speak-help', speakTags, function (e) { var $target = $(e.target); if ($target.find('.' + splitConfig.wrapCls).length) { return; } // 图片样式 if (e.target.nodeName.toLowerCase() === 'img') { $target.css({ border: 'none' }); } else { $target.removeClass(splitConfig.hightlightCls); } // 停止语音 stopSpeak(); }); // 段落内文本朗读 $(document).on('mouseenter.speak-help', '.' + splitConfig.wrapCls, function (e) { $(this).addClass(splitConfig.hightlightCls); // 开始朗读 speakText(getText(this)); }).on('mouseleave.speak-help', '.' + splitConfig.wrapCls, function (e) { $(this).removeClass(splitConfig.hightlightCls); // 停止语音 stopSpeak(); }); (责任编辑:admin) |