Nothing happens when I call diff.
redbastie opened this issue · 25 comments
I'm just trying to diff the fetched response of a PHP script via javascript.
I have an index.php
script which contains a uniqid()
call inside the h1
tag:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Hello, world!</title>
</head>
<body>
<h1><?php echo uniqid(); ?></h1>
<button onclick="morph()">Morph</button>
<script src="diffDOM.js"></script>
<script src="morph.js"></script>
</body>
</html>
The diffDOM.js
is from here: https://github.com/fiduswriter/diffDOM/blob/gh-pages/browser/diffDOM.js
Here are the contents of my morph.js
file:
function morph() {
const domParser = new DOMParser();
const currentHtml = document.documentElement.innerHTML
const currentDom = domParser.parseFromString(currentHtml, 'text/html')
fetch('index.php').then(response => {
return response.text()
}).then(newHtml => {
const newDom = domParser.parseFromString(newHtml, 'text/html')
const dd = new diffDOM.DiffDOM();
const diff = dd.diff(currentDom, newDom)
dd.apply(currentDom, diff)
console.log('should be morphed')
})
}
When the button on the page is pressed, I want just want it to change the h1
value. I've console.log
on the currentDom
and newDom
variables and they WERE different. Unfortunately, nothing happens on the actual page itself, and I receive no console errors.
Why isn't this working? What am I doing wrong?
Hey, could you make a minisite with all those things included? For example on a site such as https://jsfiddle.net/ .
What is the output of
console.log(diff)
after the line that goes const diff = dd.diff(currentDom, newDom)
?
So I added this to my morph.js
:
console.log(currentDom)
console.log(newDom)
console.log(diff)
Here is what I get in the console:
[Log] #document (morph.js, line 14)
<html>
<head>…</head>
<body>
<h1>5ff35126b376c</h1>
<button onclick="morph()">Morph</button>
<script src="diffDOM.js"></script>
<script src="morph.js"></script>
</body>
</html>
[Log] #document (morph.js, line 15)
<!DOCTYPE html>
<html lang="en">
<head>…</head>
<body>
<h1>5ff35127daf24</h1>
<button onclick="morph()">Morph</button>
<script src="diffDOM.js"></script>
<script src="morph.js"></script>
</body>
</html>
[Log] [] (0) (morph.js, line 16)
Note the difference between <h1>5ff35126b376c</h1>
and <h1>5ff35127daf24</h1>
which isn't being applied :(
And why do you do:
const newDom = domParser.parseFromString(newHtml, 'text/html')
const dd = new diffDOM.DiffDOM();
const diff = dd.diff(currentDom, newDom)
rather than just
const dd = new diffDOM.DiffDOM()
const diff = dd.diff(currentDom, newHtml)
Please see my reply above for the log output.
Yes, I saw that. But why do you create dom nodes rather than just diffing the HTML string itself? diffDOM will convert it into a virtual dom anyway, so it is not helped by you giving it dom nodes rather than HTML to work with.
This:
const diff = dd.diff(currentHtml, newHtml)
Now throws this error in the console:
Unhandled Promise Rejection: Error: Top level nodes have to be of the same kind.
Same error for this:
const diff = dd.diff(currentDom, newHtml)
Yes, your currentHtml is the child of the document whereas the newDom is a document instance. The document is not a dom node. The child is one. So you need to diff the HTML nodes or the body nodes or the equivalent HTML, not the document instances.
https://developer.mozilla.org/en-US/docs/Web/API/DOMParser shows you that you get a document instance in return, not a dom node.
If this doesn't help and you need more help, please create a jsfiddle for me to inspect.
Can you tell me what library would diff an entire document if this one can't?
No, I don't think you understood. You basically try to diff what is available under window.document rather than window.document.firstElementChild. It's like asking for the difference between my garage and my neighbor's car - one is a car and the other one is a garage, so therefore they are not comparable. What you'll likely want to do is look at the difference between your car and your neighbor's car. I doubt you'll find some specialist that will tell you what the difference between both car and garage are simultaneously.
I have a garage with a car in it.
My car was blue, but in the blink of an eye someone painted it red.
When blink my eyes and look at the garage, I want to see that the car is now red, without changing the whole garage.
Right, so you are comparing the cars inside the garage, not the garages. That's also what you need to do here.
If you compare the garages instead, they may be very different - for example one could have bikes inside of it rather than cars. Or Steve Jobs may be starting Apple in there. And then how do you compare that?
The document could be XHTML rather than HTML, for example. But I doubt that's what you are interested in, right?
What you want to compare are the HTML nodes or the body nodes.
The problem is that fetch
is grabbing the whole garage, but I only want to diff the changes to the car and not the whole garage on the page.
This is for server side rendering...maybe I'm approaching this wrong.
How about exchanging:
const diff = dd.diff(currentDom, newDom)
dd.apply(currentDom, diff)
console.log('should be morphed')
with
const diff = dd.diff(currentDom.firstElementChild, newDom.firstElementChild)
dd.apply(currentDom.firstElementChild, diff)
console.log('should be morphed')
Unless you are switching back and forth between XHTML, HTML and SVG as the window.document, this is likely what you really should be doing.
If I do that, is it going to replace the entire element, or only the changes within that element?
The changes within that element and the attribute changes on that element. But the element we are talking about is the <HTML>
-element, so this is top-most element. What changes are you afraid of not catching that way?
I just want it to change anything that changed within the element, so that the entire page doesn't reload.
Hope this makes sense.
yes, so exactly that is what this library is for. The entire page does not reload. The point is just that window.document is not a dom node, so you cannot diff it either. Not with this library and likely not with any other library either.
If the <head>
-element always stays the same, you might even just diff currentDom.body
with newDom.body
.
I think we are getting super close. This is the new code:
function morph() {
const domParser = new DOMParser();
const currentHtml = document.documentElement.innerHTML
const currentDom = domParser.parseFromString(currentHtml, 'text/html')
const currentBody = currentDom.body
fetch('index.php').then(response => {
return response.text()
}).then(newHtml => {
const newDom = domParser.parseFromString(newHtml, 'text/html')
const newBody = newDom.body
const dd = new diffDOM.DiffDOM();
const diff = dd.diff(currentBody, newBody)
dd.apply(currentBody, diff)
console.log(diff)
})
}
This is the result of console.log(diff)
:
[Log] Array (1) (morph.js, line 17)
0 a {action: "modifyTextElement", route: [1, 0], oldValue: "5ff35dc8795d4", newValue: "5ff35dcff38f7", toString: function, …}
Array Prototype
However, the page still did not change...
Change
const currentHtml = document.documentElement.innerHTML
const currentDom = domParser.parseFromString(currentHtml, 'text/html')
const currentBody = currentDom.body
to
const currentBody = document.body
It's probably easier to just write document.body
in all the places where you used currentBody
.
It worked. <3
congrats!