对比两个简单的、没有格式的文本可以直接使用google的diff-match-patch进行对比。

    对比html文本难点在于

    1. 如何忽略掉不需要进行对比的标签,只对比标签中间的文本
    2. 如果修改了样式我们该怎么对比出来
    3. 对比完文本后,我们该怎样把标签原封不动的分别加到两段html标签中

    github地址:https://github.com/NPCDW/HtmlDiff
    demo地址:https://npcdw.github.io/HtmlDiff/HtmlDiff.html
    我提供的思路只解决了第一条和第三条,关于第二条,没找到解决的方法,下面说一下我的思路和实现:
    首先、我们需要保存好两段原始的html文本,以下简称为原始html1和原始html2。
    然后、分别创建一个原始html1和原始html2的副本,然后分别将副本的标签去掉,只保留其中的文本,以下简称原始文本1和原始文本2。
    第三、使用google的diff-match-patch将文本进行对比,对比完成返回的结果样式应该是这样的

    1. [
    2. {
    3. "0": 0,
    4. "1": "Hamlet: Do you see "
    5. },
    6. {
    7. "0": -1,
    8. "1": "yonder cloud"
    9. },
    10. {
    11. "0": 1,
    12. "1": "the cloud over there"
    13. }
    14. ]

    解释一下:
    字符串"0"代表对比结果的类型,数字0代表相同的文本,-1代表原始文本1相对于原始文本2删除的文本,1代表原始文本1相对于原始文本2增加的文本。
    字符串"1"代表对比结果的文本
    这个列表是有顺序的,所有的类型为0和类型为-1的文本前后拼接起来就是原始文本1,所有的类型为0和类型为1的文本前后拼接起来就是原始文本2。
    我们将所有的类型为0和类型为-1的列表称为对比列表1,将所有的类型为0和类型为1的列表称为对比列表2
    第四、开始还原原始的html文本,并在其中加入插入标签<ins></ins>和删除标签<del></del>,还原的过程大致如下:
    因为两个html文本的还原过程一致,我们只拿第一个举例。
    1、创建一个用于还原的html文本,我们称之为还原html
    2、我们取出原始html1中的最前面一段,选取的规则是取出一段【文本】,如果一开始就是文本,而非标签(开始标签和结束标签都算标签),那么就取这段文本,遇到标签结束,如果一开始就是标签,那么取出这段标签和之后遇到文本,同样是遇到下一段标签结束,例如:

    1. </span><div></div><div><p>这是一块文本

    我们将取出的标签和文本称之为选取标签和选取文本
    3、将选取标签直接追加到还原文本末尾,将选取文本与对比列表中的第一段文本(以下简称第一段对比文本)进行对比,

    1. 如果选取文本等于第一段对比文本,将选取文本追加到还原文本末尾,删除原始html的选取部分,删除选取列表的第一块
    2. 如果选取文本小于第一段对比文本,将选取文本追加到还原文本末尾,删除原始html的选取部分,删除第一段对比文本中选取文本的部分,再取出原始html文本。。。。。重复此过程
    3. 如果选取文本大于第一段对比文本,将第一段对比文本追加到还原文本末尾,删除选取文本中第一段对比文本的部分,删除选取列表的第一块,再取出选取列表的第一块,再与选取文本对比。。。。重复此过程

    直到原始html的长度变成0,或对比列表的长度变成0,直接将剩下的部分都追加到还原文本末尾,那么还原的过程就结束了。

    附:JS版本的实现

    1. var HtmlDiff = function() {
    2. this.ignore_tag = []
    3. this.Diff_Timeout = 0
    4. }
    5. HtmlDiff.prototype.diff_launch = function(html1, html2) {
    6. var text1 = this.convertTextFromHtml(html1);
    7. var text2 = this.convertTextFromHtml(html2);
    8. var dmp = new diff_match_patch();
    9. dmp.Diff_Timeout = this.Diff_Timeout;
    10. var ms_start = (new Date).getTime();
    11. var diff = dmp.diff_main(text1, text2, true);
    12. var ms_end = (new Date).getTime();
    13. // if (diff.length > 2) {
    14. // dmp.diff_cleanupSemantic(diff);
    15. // }
    16. let time = ms_end - ms_start;
    17. let oldDiff = [];
    18. let newDiff = [];
    19. for (let i = 0,len=diff.length; i < len; i++) {
    20. if (diff[i][0] === 0) {
    21. oldDiff.push({...diff[i]})
    22. newDiff.push({...diff[i]})
    23. } else if (diff[i][0] === -1) {
    24. oldDiff.push({...diff[i]})
    25. } else {
    26. newDiff.push({...diff[i]})
    27. }
    28. }
    29. let oldDiffHtml = this.restoreToHtml(html1, oldDiff)
    30. let newDiffHtml = this.restoreToHtml(html2, newDiff)
    31. return {time, oldDiffHtml, newDiffHtml}
    32. }
    33. HtmlDiff.prototype.restoreToHtml = function(originalHtml, diffResultList) {
    34. let diffHtml = ''
    35. while (true) {
    36. let {tag, text} = this.getOneTextFromHtml(originalHtml)
    37. diffHtml += tag
    38. originalHtml = originalHtml.substr(tag.length + text.length)
    39. for (let i = 0,len=diffResultList.length; i < len; i++) {
    40. let diffType = diffResultList[i][0]
    41. let diffText = diffResultList[i][1]
    42. if (diffText === text) {
    43. diffHtml += this.formatText(diffType, diffText)
    44. diffResultList.splice(i,1)
    45. break
    46. }
    47. if (diffText.length > text.length) {
    48. diffHtml += this.formatText(diffType, text)
    49. diffResultList[i][1] = diffText.substr(text.length)
    50. break
    51. }
    52. if (text.length > diffText.length) {
    53. diffHtml += this.formatText(diffType, diffText)
    54. text = text.substr(diffText.length)
    55. diffResultList.splice(i,1)
    56. i--
    57. len--
    58. }
    59. }
    60. if (!originalHtml || !diffResultList || diffResultList.length <= 0) {
    61. break
    62. }
    63. }
    64. return diffHtml + originalHtml
    65. }
    66. HtmlDiff.prototype.convertTextFromHtml = function(html) {
    67. let text = ''
    68. let tagFlag = false
    69. this.ignore_tag.map(item => {
    70. item.flag = false
    71. })
    72. for (let i=0,len=html.length;i<len;i++) {
    73. if (!tagFlag && html[i] === '<') {
    74. tagFlag = true
    75. this.ignore_tag.map(item => {
    76. if (html.substr(i+1, item.openTag.length) == item.openTag) {
    77. item.flag = true
    78. }
    79. })
    80. } else if (tagFlag && html[i] === '>') {
    81. tagFlag = false
    82. this.ignore_tag.map(item => {
    83. if (item.flag && html.substring(i-item.closeTag.length, i) == item.closeTag) {
    84. item.flag = false
    85. }
    86. })
    87. continue
    88. }
    89. let notDiffFlag = false
    90. this.ignore_tag.map(item => {
    91. if (item.flag) {
    92. notDiffFlag = true
    93. }
    94. })
    95. if (!tagFlag && !notDiffFlag) {
    96. text += html[i]
    97. }
    98. }
    99. return text
    100. }
    101. HtmlDiff.prototype.getOneTextFromHtml = function(html) {
    102. let tag = ''
    103. let text = ''
    104. let tagFlag = false
    105. this.ignore_tag.map(item => {
    106. item.flag = false
    107. })
    108. for (let i=0,len=html.length;i<len;i++) {
    109. if (!tagFlag && html[i] === '<') {
    110. tagFlag = true
    111. if (text) {
    112. return {tag, text}
    113. }
    114. this.ignore_tag.map(item => {
    115. if (html.substr(i+1, item.openTag.length) == item.openTag) {
    116. item.flag = true
    117. }
    118. })
    119. } else if (tagFlag && html[i] === '>') {
    120. tagFlag = false
    121. tag += html[i]
    122. this.ignore_tag.map(item => {
    123. if (item.flag && html.substring(i-item.closeTag.length, i) == item.closeTag) {
    124. item.flag = false
    125. }
    126. })
    127. continue
    128. }
    129. let notDiffFlag = false
    130. this.ignore_tag.map(item => {
    131. if (item.flag) {
    132. notDiffFlag = true
    133. }
    134. })
    135. if (!tagFlag && !notDiffFlag) {
    136. text += html[i]
    137. } else {
    138. tag += html[i]
    139. }
    140. }
    141. return {tag, text}
    142. }
    143. HtmlDiff.prototype.formatText = function(diffType, diffText) {
    144. if (diffType === 0) {
    145. return diffText
    146. } else if (diffType === -1) {
    147. return '<del>' + diffText + '</del>'
    148. } else {
    149. return '<ins>' + diffText + '</ins>'
    150. }
    151. }
    152. this.HtmlDiff = HtmlDiff;