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('
','',$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);
 
原文地址:https://www.cnblogs.com/bafeiyu/p/12441092.html