<html><head><base href="https://websim.ai/html-merger/"><title>HTML Merger Tool: Interactive Visual Merger</title><style>
body {
font-family: 'Roboto', sans-serif;
background: #f5e6d3; /* Creme background */
color: #ff4136; /* Red text */
margin: 0;
padding: 20px;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.container {
background: #ffffff; /* White container background */
border-radius: 10px;
padding: 30px;
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
border: 1px solid #add8e6; /* Light blue border */
width: 90%;
max-width: 1200px;
}
h1, h2 {
text-align: center;
color: #ff4136; /* Red header */
margin-bottom: 20px;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
}
h1 {
font-size: 2.5em;
}
h2 {
font-size: 1.8em;
}
.code-container {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
.code-area {
flex: 1;
}
textarea {
width: 100%;
height: 300px;
background: #f0f8ff; /* Light blue background for textareas */
border: 1px solid #add8e6; /* Light blue border */
border-radius: 5px;
padding: 10px;
color: #333; /* Dark gray text for better readability */
font-family: 'Fira Code', monospace;
resize: vertical;
font-size: 14px;
}
button {
display: block;
width: 200px;
margin: 20px auto;
padding: 10px;
background: #ff4136; /* Red button */
color: #fff;
border: none;
border-radius: 5px;
font-size: 1em;
cursor: pointer;
transition: background 0.3s ease, transform 0.1s ease;
}
button:hover {
background: #ff725c; /* Lighter red on hover */
transform: translateY(-2px);
}
button:active {
transform: translateY(1px);
}
#mergedHTML {
width: 100%;
height: 300px;
background: #f0f8ff; /* Light blue background */
border: 1px solid #add8e6; /* Light blue border */
border-radius: 5px;
padding: 10px;
color: #333; /* Dark gray text */
font-family: 'Fira Code', monospace;
margin-top: 20px;
font-size: 14px;
}
#visualMerger {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
.visual-area {
width: 48%;
border: 1px solid #add8e6;
border-radius: 5px;
padding: 10px;
background: #f0f8ff;
}
.highlight-add {
background-color: #ccffcc;
}
.highlight-remove {
background-color: #ffcccc;
text-decoration: line-through;
}
.highlight-change {
background-color: #ffffcc;
}
</style></head><body>
<div class="container">
<h1>HTML Merger Tool: Interactive Visual Merger</h1>
<div class="code-container">
<div class="code-area">
<h2>Original HTML</h2>
<textarea id="oldHTML" placeholder="Paste the original HTML here..."></textarea>
</div>
<div class="code-area">
<h2>New HTML</h2>
<textarea id="newHTML" placeholder="Paste the new HTML here..."></textarea>
</div>
</div>
<button id="mergeButton">Merge HTML</button>
<h2>Merged HTML</h2>
<textarea id="mergedHTML" readonly></textarea>
<div id="visualMerger">
<div class="visual-area" id="visualOld">
<h3>Original HTML Structure</h3>
</div>
<div class="visual-area" id="visualNew">
<h3>New HTML Structure</h3>
</div>
</div>
</div>
<script>
const oldHTMLArea = document.getElementById('oldHTML');
const newHTMLArea = document.getElementById('newHTML');
const mergedHTMLArea = document.getElementById('mergedHTML');
const mergeButton = document.getElementById('mergeButton');
const visualOld = document.getElementById('visualOld');
const visualNew = document.getElementById('visualNew');
mergeButton.addEventListener('click', mergeHTML);
function mergeHTML() {
const oldHTML = oldHTMLArea.value;
const newHTML = newHTMLArea.value;
// Parse HTML strings into DOM objects
const oldDOM = new DOMParser().parseFromString(oldHTML, 'text/html');
const newDOM = new DOMParser().parseFromString(newHTML, 'text/html');
// Merge the DOMs
const mergedDOM = mergeDOMs(oldDOM.body, newDOM.body);
// Serialize the merged DOM back into a string
const mergedHTML = new XMLSerializer().serializeToString(mergedDOM);
// Display the merged HTML
mergedHTMLArea.value = mergedHTML;
// Update visual representation
updateVisualMerger(oldDOM.body, newDOM.body);
}
function mergeDOMs(oldNode, newNode) {
const mergedNode = oldNode.cloneNode(false);
const oldChildren = Array.from(oldNode.childNodes);
const newChildren = Array.from(newNode.childNodes);
// Simple merge strategy: Keep all nodes from both old and new
oldChildren.forEach(child => mergedNode.appendChild(child.cloneNode(true)));
newChildren.forEach(child => {
if (!oldChildren.some(oldChild => oldChild.isEqualNode(child))) {
mergedNode.appendChild(child.cloneNode(true));
}
});
return mergedNode;
}
function updateVisualMerger(oldDOM, newDOM) {
visualOld.innerHTML = '<h3>Original HTML Structure</h3>' + visualizeDOM(oldDOM);
visualNew.innerHTML = '<h3>New HTML Structure</h3>' + visualizeDOM(newDOM, oldDOM);
}
function visualizeDOM(node, compareNode = null) {
let html = '';
if (node.nodeType === Node.ELEMENT_NODE) {
const tagName = node.tagName.toLowerCase();
let className = '';
if (compareNode) {
if (!compareNode.querySelector(tagName)) {
className = 'highlight-add';
} else if (node.innerHTML !== compareNode.querySelector(tagName).innerHTML) {
className = 'highlight-change';
}
}
html += `<div class="${className}"><span><${tagName}></span>`;
for (let child of node.childNodes) {
html += visualizeDOM(child, compareNode);
}
html += `<span></${tagName}></span></div>`;
} else if (node.nodeType === Node.TEXT_NODE) {
const text = node.textContent.trim();
if (text) {
html += `<span>${text}</span>`;
}
}
return html;
}
</script>
</body></html>