Coder Social home page Coder Social logo

fiduswriter / diffdom Goto Github PK

View Code? Open in Web Editor NEW
799.0 20.0 103.0 1.41 MB

A diff for DOM elements, as client-side JavaScript code. Gets all modifications, insertions and removals between two DOM fragments.

License: GNU Lesser General Public License v3.0

HTML 68.02% JavaScript 13.66% TypeScript 18.31%
diff diffhtml dom-element

diffdom's Introduction

diffDOM - A JavaScript diffing algorithm for DOM elements

This library allows the abstraction of differences between DOM elements as a "diff" object, representing the sequence of modifications that must be applied to one element in order to turn it into the other element. This diff is non-destructive, meaning that relocations of DOM nodes are preferred over remove-insert operations.

License

This project is licensed under the LGPL v. 3. For details see LICENSE.txt.

Demo and tests

Check http://fiduswriter.github.io/diffDOM for demo and tests.

Usage

Include the diffDOM file in your HTML like this:

<script src="browser/diffDOM.js"></script>

Or like this if you import from npm:

import { DiffDOM } from "diff-dom"

Then create an instance of diffDOM within the javascript code:

dd = new diffDOM.DiffDOM()

(leave out the diffdom. if you use the npm-version)

Now you can create a diff to get from dom elementA to dom elementB like this:

diff = dd.diff(elementA, elementB)

You can now apply this diff like this:

dd.apply(elementA, diff)

Now elementA will have been changed to be structurally equal to elementB.

Virtual DOM and HTML strings

You can also use HTML strings or the virtual DOM objects diffDOM uses internally to create diffs.

diff = dd.diff(elementA, "<div>hello</div>")

You can create the Virtual DOM objects diffDOM uses, create them like this:

import { nodeToObj, stringToObj } from "diff-dom"

obj1 = nodeToObj(elementA)
obj2 = stringToObj("<div>hello</div>")

Diffing between these objects will be faster than diffing DOM nodes and can be useful in environments without access to the DOM.

Advanced uses

Undo

Continuing on from the previous example, you can also undo a diff, like this:

dd.undo(elementA, diff)

Now elementA will be what it was like before applying the diff.

Remote changes

If you need to move diffs from one machine to another one, you will likely want to send the diffs through a websocket connection or as part of a form submit. In both cases you need to convert the diff to a json string.

To convert a diff to a json string which you can send over the network, do:

diffJson = JSON.stringify(diff)

On the receiving end you then need to unpack it like this:

diff = JSON.parse(diffJson)

Error handling when patching/applying

Sometimes one may try to patch an elment without knowing whether the patch actually will apply cleanly. This should not be a problem. If diffDOM determines that a patch cannot be executed, it will simple return false. Else it will return true:

result = dd.apply(element, diff)

if (result) {
    console.log("no problem!")
} else {
    console.log("diff could not be applied")
}

Advanced merging of text node changes

diffDOM does not include merging for changes to text nodes. However, it includes hooks so that you can add more advanced handling. Simple overwrite the textDiff function of the diffDOM instance. The functions TEXTDIFF and TEXTPATCH need to be defined in the code:

dd = new diffDOM.DiffDOM({
    textDiff: function (node, currentValue, expectedValue, newValue) {
        if (currentValue === expectedValue) {
            // The text node contains the text we expect it to contain, so we simple change the text of it to the new value.
            node.data = newValue
        } else {
            // The text node currently does not contain what we expected it to contain, so we need to merge.
            difference = TEXTDIFF(expectedValue, currentValue)
            node.data = TEXTPATCH(newValue, difference)
        }
        return true
    },
})

Pre and post diff hooks

diffDOM provides extension points before and after virtual and actual diffs, exposing some of the internals of the diff algorithm, and allowing you to make additional decisions based on that information.

dd = new diffDOM.DiffDOM({
    preVirtualDiffApply: function (info) {
        console.log(info)
    },
    postVirtualDiffApply: function (info) {
        console.log(info)
    },
    preDiffApply: function (info) {
        console.log(info)
    },
    postDiffApply: function (info) {
        console.log(info)
    },
})

Additionally, the pre hooks allow you to shortcircuit the standard behaviour of the diff by returning true from this callback. This will cause the diffApply functions to return prematurely, skipping their standard behaviour.

dd = new diffDOM.DiffDOM({
    // prevent removal of attributes
    preDiffApply: function (info) {
        if (info.diff.action === "removeAttribute") {
            console.log("preventing attribute removal")
            return true
        }
    },
})

Outer and Inner diff hooks

diffDOM also provides a way to filter outer diff

dd = new diffDOM.DiffDOM({
    filterOuterDiff: function (t1, t2, diffs) {
        // can change current outer diffs by returning a new array,
        // or by mutating outerDiffs.
        if (
            !diffs.length &&
            t1.nodeName == "my-component" &&
            t2.nodeName == t1.nodeName
        ) {
            // will not diff childNodes
            t1.innerDone = true
        }
    },
})

Debugging

For debugging you might want to set a max number of diff changes between two elements before diffDOM gives up. To allow for a maximum of 500 differences between elements when diffing, initialize diffDOM like this:

dd = new diffDOM.DiffDOM({
    debug: true,
    diffcap: 500,
})

Disable value diff detection

For forms that have been filled out by a user in ways that have changed which value is associated with an input field or which options are checked/selected without the DOM having been updated, the values are diffed. For use cases in which no changes have been made to any of the form values, one may choose to skip diffing the values. To do this, set valueDiffing to false as a configuration option to diffDOM:

dd = new diffDOM.DiffDOM({
    valueDiffing: false,
})

Interprete strings as case caseSensitive

Strings of HTML can normally be interpreted case-insensitively as HTML tags don't differentiate between uppercase and lowercase. However, in the case of XML (SVGs, XHTML) there is a difference and this should be enabled. To do this, set caseSensitive to true as a configuration option to diffDOM:

dd = new diffDOM.DiffDOM({
    caseSensitive: true,
})

NOTE! If there is an SVG inside of the HTML in the string, diffDOM can automatically determine that it should switch to case sensitivity. It is only if the diff happens entirely within an SVG that it is required to specify this.

diffdom's People

Contributors

304notmodified avatar acusti avatar aquadk avatar ast-etsybin avatar cfsnyder avatar f95johansson avatar gaohuazuo avatar johanneswilm avatar kapouer avatar kranges avatar maronin avatar michaelbenin avatar noxharmonium avatar pomax avatar rahularora12 avatar sanarena avatar sqrtt avatar ssorallen avatar wbond avatar wildhoney avatar yguarata avatar z1ad avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

diffdom's Issues

Simple diff gives 100s of redundant addElement/removeElement diffs

Exellent library. Using it to develop math editor that re-renders only changed nodes.

The following gives 3 diffs for the first diff and 203 for the second diff. Using diffDOM from repository two days ago.

<head>
  <meta charset="utf-8">
  <title>diffDOM</title>
  <script src="diffDOM/diffDOM.js"></script>
 </head>
<body>
  <div id="diffDOM"><span>a</span><span>b</span></div>
  <div id="diffDOM"><span>a</span>b</div>
</body>
<script>
  var el1  = document.querySelectorAll('#diffDOM')[0];
  var el2  = document.querySelectorAll('#diffDOM')[1];
  var dd = new diffDOM();
  var d = dd.diff(el1, el2);
  console.log(d)
  var d = dd.diff(el2, el1);
  console.log(d)
 </script>

adding post-.apply hook

I want to get hold of the affected nodes after applying the patch for some post processing. Right now this can reuse some of the exiting methods.

Would you consider this as something that should be part of the library?

Improve diff to favor Move operations

First up, the library works quite well, already, which is awesome.

I've tested various different versions of this library so far, so I don't know whether this area has already seen improvements, but e.g. with the current interactive demo it's rather easy to trigger a removal and re-insertion of the iframe without touching the corresponding html tag.

Ideally the diff would move the element without destroying it.

Exception thrown in IE10

I’m seeing this error in IE 9 and 10:

image

This is the line (from diffDOM) where the exception is happening:

image

Which seems to be this line in the source:

node.insertBefore(this.objToNode(diff[this._const.element], node.namespaceURI === 'http://www.w3.org/2000/svg'), node.childNodes[c] || null);

Any ideas what’s happening here?

Deep freeze on generating diff from specific data

Looks like it's connected with amount of mixed (moved) ids. And looks like there is a nonlinear dependence somewhere in the algorithm.
This is two fragments (it is different sorting of the same rows):

Element 1
<ul><li id="ob987"></li><li id="ob986"></li><li id="ob988"></li><li id="ob991"></li><li id="ob989"></li><li id="ob992"></li><li id="ob993"></li><li id="ob990"></li><li id="ob742"></li><li id="ob741"></li><li id="ob743"></li><li id="ob744"></li><li id="ob749"></li><li id="ob745"></li><li id="ob751"></li><li id="ob752"></li><li id="ob753"></li><li id="ob755"></li><li id="ob754"></li><li id="ob756"></li><li id="ob757"></li><li id="ob758"></li><li id="ob760"></li><li id="ob759"></li><li id="ob761"></li><li id="ob762"></li><li id="ob763"></li><li id="ob764"></li><li id="ob765"></li><li id="ob766"></li><li id="ob767"></li><li id="ob768"></li><li id="ob770"></li><li id="ob769"></li><li id="ob771"></li><li id="ob773"></li><li id="ob772"></li><li id="ob775"></li><li id="ob774"></li><li id="ob776"></li><li id="ob777"></li><li id="ob779"></li><li id="ob778"></li><li id="ob780"></li><li id="ob781"></li><li id="ob782"></li><li id="ob784"></li><li id="ob783"></li><li id="ob785"></li><li id="ob786"></li><li id="ob787"></li><li id="ob789"></li><li id="ob788"></li><li id="ob790"></li><li id="ob791"></li><li id="ob792"></li><li id="ob793"></li><li id="ob794"></li><li id="ob795"></li><li id="ob802"></li><li id="ob801"></li><li id="ob803"></li><li id="ob804"></li><li id="ob797"></li><li id="ob796"></li><li id="ob798"></li><li id="ob799"></li><li id="ob800"></li><li id="ob805"></li><li id="ob806"></li><li id="ob807"></li><li id="ob809"></li><li id="ob808"></li><li id="ob810"></li><li id="ob811"></li><li id="ob812"></li><li id="ob814"></li><li id="ob813"></li><li id="ob815"></li><li id="ob816"></li><li id="ob817"></li><li id="ob819"></li><li id="ob818"></li><li id="ob820"></li><li id="ob821"></li><li id="ob822"></li><li id="ob823"></li><li id="ob824"></li><li id="ob825"></li><li id="ob827"></li><li id="ob826"></li><li id="ob828"></li><li id="ob829"></li><li id="ob830"></li><li id="ob832"></li><li id="ob831"></li><li id="ob833"></li><li id="ob834"></li><li id="ob836"></li><li id="ob835"></li><li id="ob837"></li><li id="ob838"></li><li id="ob839"></li><li id="ob841"></li><li id="ob840"></li><li id="ob842"></li><li id="ob843"></li><li id="ob844"></li><li id="ob846"></li><li id="ob845"></li><li id="ob847"></li><li id="ob849"></li><li id="ob848"></li><li id="ob850"></li><li id="ob851"></li><li id="ob852"></li><li id="ob854"></li><li id="ob853"></li><li id="ob856"></li><li id="ob857"></li><li id="ob855"></li><li id="ob858"></li><li id="ob860"></li><li id="ob859"></li><li id="ob862"></li><li id="ob861"></li><li id="ob864"></li><li id="ob865"></li><li id="ob866"></li><li id="ob863"></li><li id="ob868"></li><li id="ob867"></li><li id="ob870"></li><li id="ob871"></li><li id="ob872"></li><li id="ob869"></li><li id="ob874"></li><li id="ob873"></li><li id="ob875"></li><li id="ob877"></li><li id="ob876"></li><li id="ob879"></li><li id="ob880"></li><li id="ob881"></li><li id="ob878"></li><li id="ob883"></li><li id="ob882"></li><li id="ob885"></li><li id="ob886"></li><li id="ob887"></li><li id="ob884"></li><li id="ob888"></li><li id="ob889"></li><li id="ob890"></li><li id="ob891"></li><li id="ob892"></li><li id="ob894"></li><li id="ob895"></li><li id="ob893"></li><li id="ob897"></li><li id="ob896"></li><li id="ob900"></li><li id="ob898"></li><li id="ob899"></li><li id="ob901"></li><li id="ob903"></li><li id="ob902"></li><li id="ob905"></li><li id="ob906"></li><li id="ob904"></li><li id="ob907"></li><li id="ob909"></li><li id="ob910"></li><li id="ob908"></li><li id="ob912"></li><li id="ob911"></li><li id="ob914"></li><li id="ob915"></li><li id="ob916"></li><li id="ob913"></li><li id="ob918"></li><li id="ob917"></li><li id="ob920"></li><li id="ob921"></li><li id="ob922"></li><li id="ob919"></li><li id="ob924"></li><li id="ob923"></li><li id="ob926"></li><li id="ob927"></li><li id="ob928"></li><li id="ob925"></li><li id="ob930"></li><li id="ob929"></li><li id="ob932"></li><li id="ob933"></li><li id="ob934"></li><li id="ob931"></li><li id="ob936"></li><li id="ob935"></li><li id="ob937"></li><li id="ob938"></li><li id="ob940"></li><li id="ob939"></li><li id="ob941"></li><li id="ob942"></li><li id="ob944"></li><li id="ob943"></li><li id="ob946"></li><li id="ob947"></li><li id="ob948"></li><li id="ob945"></li><li id="ob950"></li><li id="ob949"></li><li id="ob952"></li><li id="ob953"></li><li id="ob954"></li><li id="ob951"></li><li id="ob956"></li><li id="ob955"></li><li id="ob958"></li><li id="ob959"></li><li id="ob960"></li><li id="ob957"></li><li id="ob961"></li><li id="ob962"></li><li id="ob964"></li><li id="ob963"></li><li id="ob965"></li><li id="ob966"></li><li id="ob967"></li><li id="ob968"></li><li id="ob970"></li><li id="ob969"></li><li id="ob971"></li><li id="ob974"></li><li id="ob972"></li><li id="ob975"></li><li id="ob976"></li><li id="ob973"></li><li id="ob977"></li><li id="ob978"></li><li id="ob979"></li><li id="ob981"></li><li id="ob980"></li><li id="ob982"></li><li id="ob983"></li><li id="ob984"></li><li id="ob985"></li><li id="ob995"></li><li id="ob994"></li><li id="ob996"></li><li id="ob997"></li><li id="ob998"></li><li id="ob999"></li><li id="ob1001"></li><li id="ob1000"></li><li id="ob1002"></li><li id="ob1003"></li><li id="ob1004"></li><li id="ob1005"></li><li id="ob1007"></li><li id="ob1006"></li><li id="ob1008"></li><li id="ob1009"></li><li id="ob1011"></li><li id="ob1010"></li><li id="ob1013"></li><li id="ob1012"></li><li id="ob1015"></li><li id="ob1014"></li><li id="ob1016"></li><li id="ob1017"></li><li id="ob1019"></li><li id="ob1018"></li><li id="ob1021"></li><li id="ob1020"></li><li id="ob1023"></li><li id="ob1022"></li><li id="ob1024"></li><li id="ob1025"></li><li id="ob1026"></li><li id="ob1027"></li><li id="ob1029"></li><li id="ob1028"></li><li id="ob1030"></li><li id="ob1031"></li><li id="ob1032"></li><li id="ob1033"></li><li id="ob1034"></li><li id="ob1035"></li><li id="ob1036"></li><li id="ob1038"></li><li id="ob1037"></li><li id="ob1040"></li><li id="ob1039"></li><li id="ob1041"></li><li id="ob1042"></li><li id="ob1043"></li><li id="ob1045"></li><li id="ob1046"></li><li id="ob1044"></li><li id="ob1048"></li><li id="ob1047"></li><li id="ob1053"></li><li id="ob1049"></li><li id="ob1050"></li><li id="ob1052"></li><li id="ob1051"></li><li id="ob1054"></li><li id="ob1059"></li><li id="ob1055"></li><li id="ob1056"></li><li id="ob1057"></li><li id="ob1058"></li><li id="ob1061"></li><li id="ob1060"></li><li id="ob1068"></li><li id="ob1062"></li><li id="ob1063"></li><li id="ob1064"></li><li id="ob1066"></li><li id="ob1067"></li><li id="ob1065"></li><li id="ob1070"></li><li id="ob1069"></li><li id="ob1076"></li><li id="ob1071"></li><li id="ob1072"></li><li id="ob1073"></li><li id="ob1075"></li><li id="ob1077"></li><li id="ob1074"></li><li id="ob1078"></li><li id="ob1079"></li><li id="ob1080"></li><li id="ob1081"></li><li id="ob1083"></li><li id="ob1084"></li><li id="ob1082"></li><li id="ob1086"></li><li id="ob1085"></li><li id="ob1087"></li><li id="ob1088"></li><li id="ob1089"></li><li id="ob1091"></li><li id="ob1092"></li><li id="ob1090"></li><li id="ob1093"></li><li id="ob1094"></li><li id="ob1095"></li><li id="ob1096"></li><li id="ob1097"></li><li id="ob1099"></li><li id="ob1098"></li><li id="ob1100"></li><li id="ob1101"></li><li id="ob1102"></li><li id="ob1104"></li><li id="ob1105"></li><li id="ob1103"></li><li id="ob1106"></li><li id="ob1107"></li><li id="ob1108"></li><li id="ob1109"></li><li id="ob1111"></li><li id="ob1110"></li></ul>

Element 2
<ul><li id="ob745"></li><li id="ob744"></li><li id="ob758"></li><li id="ob741"></li><li id="ob754"></li><li id="ob756"></li><li id="ob757"></li><li id="ob761"></li><li id="ob755"></li><li id="ob759"></li><li id="ob763"></li><li id="ob767"></li><li id="ob768"></li><li id="ob766"></li><li id="ob774"></li><li id="ob769"></li><li id="ob743"></li><li id="ob762"></li><li id="ob742"></li><li id="ob760"></li><li id="ob778"></li><li id="ob783"></li><li id="ob788"></li><li id="ob764"></li><li id="ob780"></li><li id="ob785"></li><li id="ob753"></li><li id="ob765"></li><li id="ob792"></li><li id="ob796"></li><li id="ob776"></li><li id="ob777"></li><li id="ob775"></li><li id="ob801"></li><li id="ob805"></li><li id="ob771"></li><li id="ob781"></li><li id="ob808"></li><li id="ob790"></li><li id="ob803"></li><li id="ob798"></li><li id="ob810"></li><li id="ob791"></li><li id="ob795"></li><li id="ob751"></li><li id="ob752"></li><li id="ob749"></li><li id="ob773"></li><li id="ob772"></li><li id="ob770"></li><li id="ob813"></li><li id="ob818"></li><li id="ob834"></li><li id="ob831"></li><li id="ob833"></li><li id="ob832"></li><li id="ob823"></li><li id="ob800"></li><li id="ob807"></li><li id="ob789"></li><li id="ob797"></li><li id="ob793"></li><li id="ob804"></li><li id="ob782"></li><li id="ob825"></li><li id="ob779"></li><li id="ob802"></li><li id="ob835"></li><li id="ob784"></li><li id="ob786"></li><li id="ob840"></li><li id="ob787"></li><li id="ob817"></li><li id="ob814"></li><li id="ob845"></li><li id="ob848"></li><li id="ob847"></li><li id="ob846"></li><li id="ob853"></li><li id="ob815"></li><li id="ob820"></li><li id="ob824"></li><li id="ob858"></li><li id="ob816"></li><li id="ob859"></li><li id="ob794"></li><li id="ob799"></li><li id="ob806"></li><li id="ob811"></li><li id="ob828"></li><li id="ob837"></li><li id="ob812"></li><li id="ob809"></li><li id="ob826"></li><li id="ob861"></li><li id="ob842"></li><li id="ob850"></li><li id="ob856"></li><li id="ob860"></li><li id="ob857"></li><li id="ob855"></li><li id="ob854"></li><li id="ob867"></li><li id="ob870"></li><li id="ob882"></li><li id="ob865"></li><li id="ob871"></li><li id="ob886"></li><li id="ob887"></li><li id="ob864"></li><li id="ob885"></li><li id="ob873"></li><li id="ob838"></li><li id="ob839"></li><li id="ob836"></li><li id="ob876"></li><li id="ob829"></li><li id="ob880"></li><li id="ob830"></li><li id="ob827"></li><li id="ob879"></li><li id="ob851"></li><li id="ob852"></li><li id="ob849"></li><li id="ob895"></li><li id="ob893"></li><li id="ob892"></li><li id="ob898"></li><li id="ob896"></li><li id="ob843"></li><li id="ob844"></li><li id="ob894"></li><li id="ob841"></li><li id="ob900"></li><li id="ob875"></li><li id="ob874"></li><li id="ob902"></li><li id="ob907"></li><li id="ob821"></li><li id="ob822"></li><li id="ob904"></li><li id="ob908"></li><li id="ob909"></li><li id="ob819"></li><li id="ob872"></li><li id="ob869"></li><li id="ob868"></li><li id="ob888"></li><li id="ob890"></li><li id="ob891"></li><li id="ob866"></li><li id="ob863"></li><li id="ob862"></li><li id="ob889"></li><li id="ob911"></li><li id="ob917"></li><li id="ob905"></li><li id="ob914"></li><li id="ob920"></li><li id="ob906"></li><li id="ob910"></li><li id="ob915"></li><li id="ob921"></li><li id="ob923"></li><li id="ob926"></li><li id="ob929"></li><li id="ob932"></li><li id="ob901"></li><li id="ob935"></li><li id="ob899"></li><li id="ob927"></li><li id="ob933"></li><li id="ob897"></li><li id="ob883"></li><li id="ob912"></li><li id="ob916"></li><li id="ob884"></li><li id="ob913"></li><li id="ob940"></li><li id="ob939"></li><li id="ob941"></li><li id="ob938"></li><li id="ob946"></li><li id="ob947"></li><li id="ob943"></li><li id="ob953"></li><li id="ob959"></li><li id="ob952"></li><li id="ob958"></li><li id="ob965"></li><li id="ob949"></li><li id="ob955"></li><li id="ob963"></li><li id="ob928"></li><li id="ob925"></li><li id="ob903"></li><li id="ob924"></li><li id="ob881"></li><li id="ob878"></li><li id="ob877"></li><li id="ob961"></li><li id="ob934"></li><li id="ob931"></li><li id="ob930"></li><li id="ob991"></li><li id="ob974"></li><li id="ob972"></li><li id="ob988"></li><li id="ob971"></li><li id="ob969"></li><li id="ob986"></li><li id="ob978"></li><li id="ob979"></li><li id="ob977"></li><li id="ob980"></li><li id="ob997"></li><li id="ob996"></li><li id="ob994"></li><li id="ob983"></li><li id="ob982"></li><li id="ob962"></li><li id="ob1002"></li><li id="ob1000"></li><li id="ob948"></li><li id="ob989"></li><li id="ob945"></li><li id="ob944"></li><li id="ob1006"></li><li id="ob1003"></li><li id="ob1009"></li><li id="ob1017"></li><li id="ob1014"></li><li id="ob1024"></li><li id="ob1022"></li><li id="ob918"></li><li id="ob942"></li><li id="ob1025"></li><li id="ob922"></li><li id="ob919"></li><li id="ob1008"></li><li id="ob1016"></li><li id="ob1028"></li><li id="ob1011"></li><li id="ob957"></li><li id="ob960"></li><li id="ob1031"></li><li id="ob956"></li><li id="ob1034"></li><li id="ob1019"></li><li id="ob1039"></li><li id="ob990"></li><li id="ob973"></li><li id="ob999"></li><li id="ob995"></li><li id="ob1030"></li><li id="ob1035"></li><li id="ob1047"></li><li id="ob1036"></li><li id="ob1041"></li><li id="ob1049"></li><li id="ob1055"></li><li id="ob1054"></li><li id="ob1042"></li><li id="ob1050"></li><li id="ob1056"></li><li id="ob966"></li><li id="ob1057"></li><li id="ob1043"></li><li id="ob1106"></li><li id="ob992"></li><li id="ob998"></li><li id="ob993"></li><li id="ob987"></li><li id="ob1060"></li><li id="ob1069"></li><li id="ob1062"></li><li id="ob1071"></li><li id="ob1063"></li><li id="ob1072"></li><li id="ob1078"></li><li id="ob1085"></li><li id="ob1064"></li><li id="ob1073"></li><li id="ob1079"></li><li id="ob1093"></li><li id="ob1098"></li><li id="ob967"></li><li id="ob968"></li><li id="ob964"></li><li id="ob1087"></li><li id="ob1094"></li><li id="ob1080"></li><li id="ob975"></li><li id="ob976"></li><li id="ob970"></li><li id="ob1095"></li><li id="ob1032"></li><li id="ob1033"></li><li id="ob1029"></li><li id="ob1044"></li><li id="ob937"></li><li id="ob1088"></li><li id="ob1081"></li><li id="ob936"></li><li id="ob1089"></li><li id="ob1096"></li><li id="ob1048"></li><li id="ob1038"></li><li id="ob1107"></li><li id="ob1053"></li><li id="ob1020"></li><li id="ob1051"></li><li id="ob1108"></li><li id="ob954"></li><li id="ob1045"></li><li id="ob951"></li><li id="ob1046"></li><li id="ob950"></li><li id="ob1040"></li><li id="ob1102"></li><li id="ob1100"></li><li id="ob1023"></li><li id="ob1026"></li><li id="ob1090"></li><li id="ob1018"></li><li id="ob1021"></li><li id="ob1027"></li><li id="ob1015"></li><li id="ob1101"></li><li id="ob1037"></li><li id="ob1103"></li><li id="ob1104"></li><li id="ob1105"></li><li id="ob1099"></li><li id="ob1012"></li><li id="ob1091"></li><li id="ob1092"></li><li id="ob1086"></li><li id="ob1067"></li><li id="ob1061"></li><li id="ob1109"></li><li id="ob1007"></li><li id="ob1065"></li><li id="ob1010"></li><li id="ob1013"></li><li id="ob1074"></li><li id="ob1059"></li><li id="ob1068"></li><li id="ob1005"></li><li id="ob1001"></li><li id="ob1004"></li><li id="ob1077"></li><li id="ob1084"></li><li id="ob1070"></li><li id="ob1052"></li><li id="ob1066"></li><li id="ob1075"></li><li id="ob1083"></li><li id="ob1076"></li><li id="ob1058"></li><li id="ob1110"></li><li id="ob1097"></li><li id="ob1082"></li><li id="ob985"></li><li id="ob984"></li><li id="ob981"></li><li id="ob1111"></li></ul>

Modular compatibility

It shouldn't be hard, I'll provide a pull request if you want, but I came across a situation where I'm loading about five separate javascripts and figured it's time for AMD.js to handle it at this point.

jQuery, diffDOM, socket.io, responsive.js ,** patch.js

responsive.js is a script a wrote to handle some ui events.
patch.js listens to a node.js/socket.io server dispatching fs.watch file change events, applying html patches with diffDOM via AJAX/DOMParser.

Anyways, can you add AMD.js support or is that out of the question?

just saying hi

Johannes,

Just letting you know I landed here now. I'm gonna give your lib a try.

Alex

Diff can unintentionally cause newValue to be undefined

JSFiddle that highlights the issue:
https://jsfiddle.net/ka2tvyq7/

JSFiddle with solution:
https://jsfiddle.net/nnzwdswh/

To observe the issue, type/remove some of the text from the input. Then hit the submit button. The first time the submit button is pressed, nothing happens, but the second time, the input value is set to undefined.

Relevant code used to test this:

var dd = new diffDOM();

// create two sets of identical html
var element1 = document.createElement('form');

var input = document.createElement('input');
    input.setAttribute('type', 'text');
    input.setAttribute('value', 'testing');

var submit = document.createElement('input');
    submit.setAttribute('type', 'submit');

element1.appendChild(input);
element1.appendChild(submit);

document.body.appendChild(element1);

// element2 will remain virtual, and we will diff element1 with element2
// to determine if we actually need to update the DOM or not.
var element2 = element1.cloneNode(true);

// event listener for when the input value changes
element1.addEventListener('input', function(e){
    // update the virtual element, then diff and see if there needs to be updates
    element2.firstChild.setAttribute('value', e.target.value);

    var diff = dd.diff(element1, element2);
    dd.apply(element1, diff);

    console.log(diff);
});

// event listener for pressing the submit button
element1.addEventListener('submit', function(e){
    e.preventDefault();

     // update the virtual element, then diff and see if there needs to be updates
    element2.firstChild.setAttribute('value', '');

    var diff = dd.diff(element1, element2);
    dd.apply(element1, diff);

    console.log(diff);
});

It appears that this issue revolves around any calls to node.setAttribute(...) within the library.
Instead of:

node.setAttribute(attribute, objNode.attributes[attribute]);

I changed it to:

node.setAttribute(attribute, objNode.attributes[attribute]);
node[attribute] = objNode.attributes[attribute];

This seems redundant, but it was the only way that seemed to get it to work as intended. Have a look and let me know what you think. I can put up a PR with the changes if you feel it is necessary.

Never ending loop when diffing specific DOM.

When I compare these two DOM elements:

<div class="columns small-9">
<select class="scoring-input scoring-select" onclick="fixSelection()"><option value="a9F6FCB4D-1E9B-493C-A8AA-E30B8B14B4AF">A - <p>Caprica</p>
</option><option value="m2" selected="true">B - <p>Iowa</p>
</option><option value="aA35C82E7-3690-4B8E-9CCB-7F669E98D0E4">C - <p>Mars Colony</p>
</option><option value="aFED1F76D-C161-48E5-91A8-4F633A567BE7">D - <p>Tatooine</p>
</option></select>
</div>

and

<div class="columns small-9">
<select class="scoring-input scoring-select" onclick="fixSelection()"><option value="a9F6FCB4D-1E9B-493C-A8AA-E30B8B14B4AF">A - <p>Caprica</p>
</option><option value="a4900B142-050F-4875-B386-1D61FB1A3A05">B - <p>Mars Colony</p>
</option><option value="m2" selected="true">C - <p>Iowa</p>
</option><option value="aFED1F76D-C161-48E5-91A8-4F633A567BE7">D - <p>Tatooine</p>
</option></select>
</div>

The diff code enters a never ending loop. It somehow has to do something with the changed attributes on the option elements.

When I add the following code:

if (difflist && difflist.length > 0) {
                    for(var looper = 0; looper < difflist.length; looper++) {
                        if (difflist[looper][14] === 10) {
                            difflist.splice(looper, 1);
                            looper--;
                        }
                    }
                }

                if (difflist && difflist.length === 0) {
                    difflist = undefined;
                }

To the bottom of the main loop (do {} while (difflist);), it fixes the issue, apparently even without side effects.... I figured that the diffing / applying of the attribute changes somewhere goes wrong.. and since Im not interested in the diff of attributes, this is a safe solution for me for now.

I know this is not THE solution, but I have neither the expertise nor the time to debug this any further. For now I will use a patched version of this lib. Please include a proper fix for this issue in the next release..

Diff a detached node to an attached node.

This is similar to #11.

My use case is I want to template a custom element. When state changes, I want to take the rendered HTML and diff it to what is currently there. The custom element may or may not be in the document at this point in time. The diff() function is causing errors when DOM mutations happen because it seems to be invoking apply(). From reading the documentation, it seems that it shouldn't be invoking apply() automatically, but instead leaving that up to the user. Heres a JSBin for it.

In #11, you said:

I would suggest you just filter out any changes on the top node.

I'm not sure what you mean by that, but I assume that I'd have to have a diff object in order to do that. Probably easy enough, but you also said:

I don't see this being relevant enough as a feature.

You may be right in that it may not add that much convenience. I can't have much an opinion on the level of difficulty required to filter out any changes on the top node because I can't get there. However, I would think that making the API consumer mutate the diff object would be exposing an implementation detail that they shouldn't be concerned with. If this was a feature, it would probably allow the diffing between an actual DocumentFragment and a node in the DOM, which would potentially add a lot of convenience for some (me for sure).

Thanks for writing this. I'm excited to experiment with it given I can get past this hurdle.

Failed to execute 'setAttribute' on 'Element': '"'

Sometimes I have get this error.
Failed to execute 'setAttribute' on 'Element': '"' is not a valid attribute at diffDOM.applyDiff
But I can't get what element can cause it.

The error appears at this line in diffDOM library:
node.setAttribute(diff.name, diff.value);

Call stack is:

    at http://dev.lc/min/?g=js_main&debug&v=13:8264:27
    at Array.forEach (<anonymous>)
    at diffDOM.apply (http://dev.lc/min/?g=js_main&debug&v=13:8263:19)
    at http://dev.lc/min/?g=js_main&debug&v=13:10675:24
    at Object.success (http://dev.lc/min/?g=js_main&debug&v=13:10565:25)
    at i (http://dev.lc/min/?g=js_main&debug&v=13:2:28017)
    at Object.fireWith [as resolveWith] (http://dev.lc/min/?g=js_main&debug&v=13:2:28783)
    at A (http://dev.lc/min/?g=js_main&debug&v=13:4:14035)
    at XMLHttpRequest.<anonymous> (http://dev.lc/min/?g=js_main&debug&v=13:4:16323)

What kind of situation can cause such error? What I have to check in my html?

release ?

are there plans before next release ?

i'm just eager to remove the github url from my package.json :)

Are dom changes batched?

Say there are two divs, the second one has two more childnodes.

<div></div>

<div><h1>diffDOM demo</h1><h1>diffDOM2 demo</h1></div>

In this case, there are two diffs, one for each node addition. But, when I apply diff.apply(), are the node additions added by a single operation, (say, for eg, using documentfragment) or done each time for each diff ?

My guess is it doesnt batch operations, is it so?
If it doesnt, do you plan to add this in future releases?

Route Incorrect For Removals (Multiple Distinct Elements Have Same Route)

My understanding of the route attribute of a diff object is that it can be used to retrieve the original element that was modified. For removals, this element lives in the first dom, and for additions/modifications, it lives in the second. So given:

const { DOMParser, XMLSerializer } = require('xmldom')
        , p = new DOMParser()
        , domBefore = p.parseFromString(xmlBefore)
        , domAfter = p.parseFromString(xmlAfter)
        , diff = dd.diff(domBefore, domAfter);

We should have the invariant that for every element d of diff, dd.getFromRoute(d.action.includes('remove') ? domBefore : domAfter, d.route) is the same element as d.element.

However sometimes the last element of route is incorrect, breaking this invariant. Given xmlBefore of:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE assessment>
<assessment>
  <title>Test</title>
  <short_title>Test</short_title>
  <page>
    <title>Page 1</title>
    <question>
      <multiple_choice shuffle="true" select="multiple" labels="false">
        <choice value="A">Placeholder</choice>
        <choice value="B">Placeholder</choice>
      </multiple_choice>
      <part id="be03031c79464d08a5bce122fee68ffc">
        <response match="A" score="1">
          <feedback>
            <p id="aa3ab72153c834c8395a0099dff39d434"/>
          </feedback>
        </response>
        <response match="A,B" score="0" name="AUTOGEN_{A,B}">
          <feedback>
            <p id="ab8e552e886ee486baf47d45c778deac4"/>
          </feedback>
        </response>
        <response match="B" score="0" name="AUTOGEN_{B}">
          <feedback>
            <p id="aed5ccfa249794825a9fd6e05233ca5c1"/>
          </feedback>
        </response>
      </part>
    </question>
  </page>
</assessment>

and xmlAfter of:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE assessment>
<assessment>
  <title>Test</title>
  <short_title>Test</short_title>
  <page>
    <title>Page 1</title>
    <question>
      <multiple_choice shuffle="true" select="multiple" labels="false">
        <choice value="A">Placeholder</choice>
        <choice value="B">Placeholder</choice>
      </multiple_choice>
      <part id="be03031c79464d08a5bce122fee68ffc">
        <response match="A" score="1">
          <feedback>
            <p id="aa3ab72153c834c8395a0099dff39d434"/>
          </feedback>
        </response>
      </part>
    </question>
  </page>
</assessment>

diff is:

[ { action: 'removeTextElement',
    route: [ 4, 5, 3, 3, 0 ],
    value: '\r\n        ' },
  { action: 'removeElement',
    route: [ 4, 5, 3, 3, 0 ],
    element:
     { nodeName: 'response',
       attributes: [Object],
       childNodes: [Array] } },
  { action: 'removeTextElement',
    route: [ 4, 5, 3, 3, 0 ],
    value: '\r\n        ' },
  { action: 'removeElement',
    route: [ 4, 5, 3, 3, 0 ],
    element:
     { nodeName: 'response',
       attributes: [Object],
       childNodes: [Array] } },
  { action: 'modifyAttribute',
    route: [ 4, 5, 3, 3, 1 ],
    name: 'match',
    oldValue: 'B',
    newValue: 'A' },
  { action: 'removeAttribute',
    route: [ 4, 5, 3, 3, 1 ],
    name: 'name',
    value: 'AUTOGEN_{B}' },
  { action: 'modifyAttribute',
    route: [ 4, 5, 3, 3, 1 ],
    name: 'score',
    oldValue: '0',
    newValue: '1' },
  { action: 'removeElement',
    route: [ 4, 5, 3, 3, 1, 1, 1 ],
    element: { nodeName: 'p', attributes: [Object] } },
  { action: 'addElement',
    route: [ 4, 5, 3, 3, 1, 1, 1 ],
    element: { nodeName: 'p', attributes: [Object] } } ]

All of the removed elements have the same route [ 4, 5, 3, 3, 0 ]. I think this bug is caused by lines 936 and 945 of diffDOM.js, where

diffs.push(new Diff()
    .setValue(t._const.action, t._const.removeTextElement)
    .setValue(t._const.route, route.concat(index2))
    .setValue(t._const.value, node.data)
);

should actually be doing route.concat(index1). However I feel like this issue may exist elsewhere in the code where remove diff routes are handled. Can this be fixed so that the removal routes can be used to get dom elements from the first dom (domBefore)?

Removing first child results in overly complicated diff

Removing the last child node:
<h1>Foo</h1><h2>Bar</h2><h3>Baz</h3> to <h1>Foo</h1><h2>Bar</h2>

gives a nice clean diff:

{"action":"removeElement","route":[2],"element":{"nodeName":"H3","childNodes":[...]}}

Removing the first child node:
<h1>Foo</h1><h2>Bar</h2><h3>Baz</h3> to <h2>Bar</h2><h3>Baz</h3>

gives a massive diff:

{"action":"replaceElement","oldValue":{"nodeName":"H1","childNodes":[...]},"newValue":{"nodeName":"H2","childNodes":[...]},"route":[0]},
{"action":"replaceElement","oldValue":{"nodeName":"H2","childNodes":[...]},"newValue":{"nodeName":"H3","childNodes":[...]},"route":[1]},
{"action":"removeElement","route":[2],"element":{"nodeName":"H3","childNodes":[...]}}

INPUT modifyAttribute does not affect value

When you apply this type of diff on an INPUT element, the value is not changed in the DOM.

{"action":"modifyAttribute","route":[1],"name":"value","oldValue":"hello","newValue":""}

Funny thing you diff again and apply again and you get this diff instead:

{"action":"modifyValue","oldValue":"hello","newValue":"","route":[1]}

Then the INPUT value changes in the DOM.

Load up this file in your browser and see for yourself.
https://gist.github.com/martindrapeau/097e21bea76362a0976386ad9c0c7326

HTML Comments cause an exception

The error triggers on line 552 because t1.attributes is undefined.

The fix might be as simple as adding a check like

t1.attributes ? slice.call(...) : []

(and same for t2 on next line), but I haven't tested it yet.

vdom issues

Using a "virtual" dom nodes (in the form of JS Objects) instead of "real" DOM nodes (even though they off-screen and disconnected) has speed advantages. In the vdom branch diffDOM has been converted to do that for the building of the diff list. However, it is currently still buggier than the original version. When running random.html, at least two different types of errors show up every now and then. One of these bugs has been added to the top of the basic.html file.The other one was less easily reproducible but results in an error due to a missing parentNode around lines 1192/1210.

These issues are the main thing that prevent the vdom branch from being merged into the main branch.

@unbug is currently looking at the bug in basic.html.

New hook: actual text node diff

Hey there,

currently the text diff hook, is invoked when applying the diff if I read the docs correctly.

I would like to be able to extract a diff at diff time, though, in order to implement transformations / merging for text nodes. (My goal is to have the dom diff contain an actual text diff for all changed text nodes, instead of the new string)

Is this possible?

Do not package the `venv` dir (reduce the installed size 200x)

Your current npm package size is 33 MiB, and unpacked size is 168 MiB (as of [email protected]).

167 MiB of that is inside the venv directory.

It looks like stuff from that directory is not used by the package, so most probably it was packaged by an accident.

You could add it to .npmignore (if you have an .npmignore already) or to .gitignore (if you don't have an .npmignore), or use the files field in your package.json file.

Directory listing with sizes:

4,0K    diff-dom-2.0.0.tgz/bower.json
8,0K    diff-dom-2.0.0.tgz/demo
48K     diff-dom-2.0.0.tgz/diffDOM1.js
48K     diff-dom-2.0.0.tgz/diffDOM2.js
48K     diff-dom-2.0.0.tgz/diffDOM.js
4,0K    diff-dom-2.0.0.tgz/index.html
8,0K    diff-dom-2.0.0.tgz/LICENSE.txt
40K     diff-dom-2.0.0.tgz/newDiffDOM.js
28K     diff-dom-2.0.0.tgz/oldDiffDOM.js
4,0K    diff-dom-2.0.0.tgz/package.json
4,0K    diff-dom-2.0.0.tgz/README.md
40K     diff-dom-2.0.0.tgz/RecentDiffDOM.js
4,0K    diff-dom-2.0.0.tgz/test3.html
156K    diff-dom-2.0.0.tgz/test.html
296K    diff-dom-2.0.0.tgz/tests
167M    diff-dom-2.0.0.tgz/venv

Is it possible to know if a specific diff is an innerHTML diff?

Whenever the diff is innerHTML (or not an attribute diff) I would like to ignore that diff.
Is it possible to determine when a diff is innerHTML? As of now, I have noticed that innerHTML diffs typically show up at the end, after attribute diffs. Would that always be the case?

[Diff, Diff, Diff, Diff]
0: Diff
1: Diff
2: Diff
3: Diff // <= this one is innerHTML diff, I would like to remove it from the diff list

Not playing well with inputs?

If I have an HTML input, type into it, and then some JavaScript runs that updates the dom using diffDOM, even if the input field itself has not changed, my typing disappears and the field loses focus.

span inside paragraph diff with the entire paragraph

Hey @johanneswilm,

Here is the element being diff'ed

<p> some text <span alt='meta text'> some text</span> </p>

The patch that gets generated is pointed to the entire paragraph, which is the unexpected behavior. I have some tricky things going on inside of the span that I prefer the update patch is applied only to the span itself.

Any ideas of how to change this behavior? I would love to submit a PR if your time is constrained.

adjacent text node agnostic merging

Starting this issue here to begin a conversation about text nodes. For the CryptPad use case we are pushing DiffDOM to it's limit by using it to patch changes to the DOM across the wire when people have typed in a contentEditable.
We have discovered an interesting issue that browsers like to split text nodes on occasions which don't always make sense. If we make a text node longer than a given length, the browser will decide to split it into two, if we hit backspace in the middle of a text node, the browser will decide to split it and then shorten the node before, rather than shifting the entire text node.

My gut feeling is that DiffDOM probably ought to be agnostic to adjacent text nodes when it merges. That is to say, it should see ["hel","lo ","world!"] the same as ["hello"," world!"]. Furthermore there is perhaps some performance to be gained by reusing the browser's trick when a few characters are removed from the middle of a text node to call Text.splitText() and then simply shorten the prior text node.

In any case, it would be nice if the adjacent text nodes in one DOM were not shoved into the other DOM which kills our cursor location and makes any sane Operational Transform quite impossible.

What do you think about this? Is considering adjacent text nodes as separate things a valued feature or is this something which you'd be open to a patch for?

pre-diff hooks

I'm using diffDOM for applying patches to a contentEditable section of a page, and it's working really well. Unfortunately, patching a DOM while you're editing has unwanted side effects that often displace the cursor selection.

I'm currently modifying parts of the diff algorithm to execute optional hooks that would expose information about the nodes which are being patched, which would make it much easier to gracefully handle cursor functionality. In general, I'd like to extend the library to address issues which are pertinent to diff/patching a contentEditable node.

I'd prefer to get these changes pushed upstream so I don't have to maintain a fork. Is there an IRC channel I can join to discuss changes more directly, or should I just submit a PR and go from there?

PS: Thanks for the excellent library, I tried a number of alternatives before settling on diffDOM.

Diff spec

I would greatly appreciate documentation describing the structure and content of the calculated diffs, so I can process and manipulate them.

Thanks

Detecting events bound to DOM?

I was thinking about using diffDom to apply DOM changes when my models change, but then I realized that it probably doesn't check for bound events and transfer the events over to the new elements (or if they were removed - then unbind them).

How would you handle something like this?

Elements with matching IDs or classes are not matched more strongly

Although there is special code for creating identifiers based on ID or class, they don't change the behaviour of findCommonSubsets, as roughlyEqual gives the same result (true) for comparing <p id="foo"> with <p id="foo"> as it does <p id="foo"> with <p id="bar">.

The result is changing a document from

<p id="A"></p><p id="B"></p><p id="C"></p> to <p id="A"></p><p id="C"></p>

results in an overly complicated diff of

{"action":"removeElement","route":[0],"element":{"nodeName":"P","attributes":{"id":"A"}}},
{"action":"modifyAttribute","route":[0],"name":"id","oldValue":"B","newValue":"A"}

Preventing a `removeElement` action in `preDiffApply` is messing up diffs of an adjacent element.

Given the following markup:

<div id="a">
    <div class="before"></div>
    <a href="#before">Before</a>
</div>

<div id="b">
    <span class="after"></span>
    <a href="#after">After</a>
</div>

I set up a simple scenario where I want to diff #a against #b and apply the diff to #a, but I have used the preDiffApply function to try to prevent removal of the div element:

const a = document.querySelector('#a');
const b = document.querySelector('#b');

const differ = new diffDOM({
    preDiffApply: function(info){
        if(info.diff.action === 'removeElement' && info.node.nodeName === 'DIV' && info.node.classList.contains('before')){
            return true;
        }
    }
});

const diff = differ.diff(a, b);
differ.apply(a, diff);

I would expect that because I have only prevented the removeElement action, that the addElement action for the span would still take place, as would the modifyAttribute and modifyTextElement actions which would update the a element, and the following output would be the result of what #a would become:

<div id="b">
    <span class="after"></span>
    <div class="before"></div>
    <a href="#after">After</a>
</div>

However, the modifyAttribute and modifyTextElement actions are no longer performing correctly and the following output is the result instead:

<div id="b">
    <span class="after"></span>
    <div class="before"></div>
    <a href="#before">Before</a> <!-- this node has not changed as expected -->
</div>

If I log the info variable in the function, It looks like the modifyAttribute action is still present, but is not referencing the correct node, and the modifyTextElement action is missing altogether.

screen shot 2018-11-11 at 9 27 03 pm

Here is a full example file containing the code referenced in this issue:

diff-test.html.zip

diff excessively replace entire div rather than changing the innerText and then appendChild

Here I have two html strings:

original

<h2>this is a paragraph <code>ha</code>&nbsp;</h2><h2>this is another`h`&nbsp;</h2><ul><li>some stuff</li></ul> 

New

<h2>this is a paragraph <code>ha</code>&nbsp;</h2><h2>this is another<code>h</code>&nbsp;</h2><ul><li>some stuff</li></ul> 

diff generated

[{"14":5,"15":[1],"18":{"26":"H2","28":[{"24":"this is another`h` "}]}},{"14":9,"15":[1],"16":{"26":"UL","28":[{"26":"LI","28":[{"24":"some stuff"}]}]},"17":{"26":"H2","28":[{"24":"this is another"},{"26":"CODE","28":[{"24":"h"}]},{"24":" "}]}},{"14":6,"15":[2],"18":{"26":"UL","28":[{"26":"LI","28":[{"24":"some stuff"}]}]}}] 

I am thinking about ways to fix this. Any input?

Simple text change results in overly complex diff

Change
<p><b>Foo</b> Bar <b>Baz</b></p>
to
<p><b>Foo</b> Car <b>Baz</b></p>

Expected diff:

{"action":"modifyTextElement","route":[0,1],"oldValue":" Bar ","newValue":" Car "}

Actual diff:

{"action":"modifyTextElement","route":[0,1],"oldValue":" Bar ","newValue":" Car "},
{"action":"removeTextElement","route":[0,1],"value":" Bar "},
{"action":"addTextElement","route":[0,1],"value":" Car "}

The second two actions are redundant

Sub diffs

It'd be nice to be able to stop diffing at a node. Would allow for nested diffDOMs / views.

Was thinking of something like sync="false" although perhaps definable?

Below works:

nodeToObj: function(node) {
            var objNode = {}, dobj = this;
            objNode.nodeName = node.nodeName;
            if (objNode.nodeName === '#text' || objNode.nodeName === '#comment') {
                objNode.data = node.data;
            } else {
                if (node.attributes && node.attributes.length > 0) {
                    objNode.attributes = {};
                    Array.prototype.slice.call(node.attributes).forEach(
                        function(attribute) {
                            objNode.attributes[attribute.name] = attribute.value;
                        }
                    );
                    if (objNode.attributes['sync'] === "false") objNode.sync = false;
                }
                if (node.childNodes && node.childNodes.length > 0 && objNode.sync !== false) {
                    objNode.childNodes = [];
                    Array.prototype.slice.call(node.childNodes).forEach(
                        function(childNode) {
                            objNode.childNodes.push(dobj.nodeToObj(childNode));
                        }
                    );
                }
                if (this.valueDiffing) {
                    if (node.value) {
                        objNode.value = node.value;
                    }
                    if (node.checked) {
                        objNode.checked = node.checked;
                    }
                    if (node.selected) {
                        objNode.selected = node.selected;
                    }
                }
            }

            return objNode;
        },

What do you think?

Element filtering example

Can somebody explain how to exclude some elements from source from changing, please? For example, by class name?

In my situation there are new elements on the page (created with js) and there is no such elements in update coming from server via ajax. I need to remain this elements, and update everything else.

Cannot read property 'childNodes' of undefined

With maxChildCount and maxChildCountDiffCount setted to small numeral, sometimes there are errors.
For example, with diffing of this two fragments and child counts = 1:

<div><ul data-gid="10"><li data-id="631"></li></ul><br><br><br></div>
<div><ul data-gid="14"><li data-id="644"></li></ul><br><br><br></div>

Eror Cannot read property 'childNodes' of undefined.
Looks like it is connected with repeated <br> tag.

Without maxChildCount it works good.

What license?

Hey,

I just noticed that you transferred the repo. Here's my two cents :)

  1. In countries where public domain doesn't exist, declaring something to be public domain leaves people in those countries with the raw untouched copyright laws (which aren't too permissive, as you may know). So, I suggest to use a license like MIT or BSD, which are both small and permissive.
  2. @Pomax it might make sense to recreate a repo called "DOM-diff" on your account and point users to this repo, since all stargazers and stuff were lost, when you deleted the old one...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.