CKEditor4多个span标签不合并的问题
编辑器编辑完成之后的html源码如下
由于项目要求,我们考虑在php端解决这个问题,采用dom的方式,也考虑过正则匹配,但是考虑的意外情况太多,代码可读性也不高
下面是解决的一个demo源码
1 <?php 2 /** 3 * 处理返回界面中多个span不合并的问题 4 * 5 * @param $html 6 * 7 * @return string 8 * 1.查找全部的span 节点 9 * 2.找出节点中需要处理的span节点(只有为span标签的父亲,无兄无弟无子) 10 * 3.遍历节点并处理 11 */ 12 function handleHtmlManySpan($html) 13 { 14 $dom=new DOMDocument(); 15 $dom->loadHTML($html); 16 $nodes = $dom->getElementsByTagName('span'); 17 $waitingNodes = []; 18 foreach ($nodes as $node) { 19 if (isMinimalSpanNode($node)) { 20 $waitingNodes[] = $node; 21 } 22 } 23 foreach ($waitingNodes as $node) { 24 handleMinimalSpanNode($node); 25 } 26 $html = $dom->saveHTML(); 27 preg_match('/<body>(.*?)<\/body>/', $html, $matches); 28 return isset($matches[1])?$matches[1]:''; 29 } 30 31 /** 32 * 判断html的dom节点是否是只有span的父亲,无兄无弟无子 33 * @param $node 34 * 35 * @return bool 36 */ 37 function isMinimalSpanNode($node) 38 { 39 return $node->parentNode->nodeName=='span' && $node->nextSibling===null&&$node->previousSibling===null && $node->hasChildNodes() && count($node->childNodes)==1 && $node->childNodes[0]->nodeType==XML_TEXT_NODE; 40 } 41 42 /** 43 * 将符合条件的span节点删除,并将样式及内容交还给父亲,并且递归调用 44 * @param $node 45 */ 46 function handleMinimalSpanNode($node) 47 { 48 $nodeStyle = $node->getAttribute('style'); 49 $nodeValue = $node->nodeValue; 50 $parentNode = $node->parentNode; 51 $parentNode->setAttribute('style', $parentNode->getAttribute('style').$nodeStyle); 52 $parentNode->removeChild($node); 53 $parentNode->nodeValue = $nodeValue; 54 if (isMinimalSpanNode($parentNode)) { 55 handleMinimalSpanNode($parentNode); 56 } 57 } 58 59 $str = '<p><span style="color:red;"><span></span><strong><span style="font-weight:bold;"><span style="font-style: italic;"><span style="font-size:12px;">ceshi</span></span></span></strong></span></p><p><span style="color:red;"><span></span><strong><span style="font-weight:bold;"><span style="font-style: italic;"><span style="font-size:12px;">ceshi</span></span></span></strong></span></p>'; 60 echo handleHtmlManySpan($str);
正则替换的方式解决的不够彻底,下面是代码,也可能是我的正则技术不够优秀,欢迎留言
/** * 解决返回到主页的html显示有多个span的问题 * 将返回的html中的多个span改为1个,将多个span的样式集中为一个 */ function replaceSpanReg($htmlText) { $pattern = "/<span style=\"(.*?)\">/"; $pregNum = preg_match_all($pattern,$htmlText,$matches); if($pregNum>1){ $style = implode("",$matches[1]); $strLeftBefore = implode("",$matches[0]); $strRightBefore = ''; for($i=1;$i<=$pregNum;$i++){ $strRightBefore .= '</span>'; } $strLeftNow = '<span style="'.$style.'">'; $htmlText = str_replace('\r\n','',$htmlText); $htmlText = str_replace($strLeftBefore,$strLeftNow,$htmlText); $htmlText = str_replace($strRightBefore,'</span>',$htmlText); } return $htmlText; }
最上面的代码只能解决最内层的span标签的逐渐删除,下面代码是优化版本,个人感觉更佳
/** * 处理返回界面中多个span不合并的问题 * * @param $html * * @return string * 1.查找全部的span 节点 * 2.找出节点中需要处理的span节点(只有为span标签的父亲,无兄无弟无子) * 3.遍历节点并处理 */ function handleHtmlManySpan($html) { $dom=new DOMDocument(); $dom->loadHTML($html); $nodes = $dom->getElementsByTagName('span'); $waitingNodes = []; foreach ($nodes as $node) { if (isWaitingHandleSpanNode($node)) { $waitingNodes[] = $node; } } foreach ($waitingNodes as $node) { handleSpanNode($node); } $html = $dom->saveHTML(); preg_match('/<body>(.*?)<\/body>/', $html, $matches); return isset($matches[1])?$matches[1]:''; } /** * 判断当前的span标签是否需要处理 * 1.如果父标签是span而且没有兄弟标签即处理 ,并且只有一个子元素的时候不是span,也可以有多个子元素 * @param $node * * @return bool */ function isWaitingHandleSpanNode($node) { $isWaiting = false; //父标签为span且无兄无弟 if($node->parentNode->nodeName=='span' && $node->nextSibling===null&&$node->previousSibling===null) { if(count($node->childNodes)!=1){ $isWaiting = true; }else if(count($node->childNodes)==1 && $node->childNodes[0]->nodeName!='span'){ $isWaiting = true; } } return $isWaiting; //return $node->parentNode->nodeName=='span' && $node->nextSibling===null&&$node->previousSibling===null; } /** * 将符合条件的span节点删除,并将样式及内容交还给父亲,并且递归调用 * @param $node */ function handleSpanNode($node) { $nodeStyle = $node->getAttribute('style'); $parentNode = $node->parentNode; $parentNode->setAttribute('style', $parentNode->getAttribute('style').';'.$nodeStyle); if(count($node->childNodes)>0){ foreach($node->childNodes as $child) { $parentNode->appendChild($child); } } $parentNode->removeChild($node); if (isWaitingHandleSpanNode($parentNode)) { handleSpanNode($parentNode); } }
$str = '<p><span style="color:red"><span style="font-size:12px"><strong><span style="backgrount:red"><span>ces</span></span></strong></span></span></p>';
echo handleHtmlManySpan($str);