Growing mirrored list
can-mirror shares direct child additions and removals. Put it on the list,
then change that list with normal DOM methods.
Test it with another person
- Keep this page open in your normal window.
- Copy the private-window link, then paste it into a private or incognito window.
- Interact in either window and watch the other one update.
Copy the code
The live playground and this source use the same Vanilla HTML recipe.
Save this as index.html, or open it in the playground to
test and change it.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Growing mirrored list</title>
<style>
:root {
color: #1c1c1c;
background: #f4efe5;
font-family: ui-sans-serif, system-ui, sans-serif;
}
* { box-sizing: border-box; }
body { display: grid; min-height: 100vh; margin: 0; padding: 1.5rem; place-items: center; }
main { width: min(34rem, 100%); }
h1 { margin: 0 0 0.35rem; font-size: clamp(2rem, 8vw, 3.5rem); line-height: 1; }
.intro { margin: 0 0 1.25rem; line-height: 1.5; }
.panel {
padding: 1rem;
border: 2px solid #1c1c1c;
background: #ebe4d5;
box-shadow: 5px 5px 0 #1c1c1c;
}
ul {
min-height: 7rem;
margin: 0 0 1rem;
padding: 0.75rem 0.75rem 0.75rem 2rem;
border: 1px solid #1c1c1c;
background: #fffdf8;
}
li + li { margin-top: 0.35rem; }
button {
padding: 0.65rem 0.9rem;
border: 2px solid #1c1c1c;
background: #f3cf58;
box-shadow: 3px 3px 0 #1c1c1c;
cursor: pointer;
font: 700 1rem/1 ui-sans-serif, system-ui, sans-serif;
}
button:active { translate: 3px 3px; box-shadow: none; }
</style>
</head>
<body>
<main>
<h1>Shared list</h1>
<p class="intro">Add an item. The direct child appears for everyone.</p>
<section class="panel">
<ul id="growing-list" can-mirror>
<li>first</li>
</ul>
<button id="add-list-item" type="button">add entry</button>
</section>
</main>
<script type="module">
import { playhtml } from "playhtml";
const list = document.getElementById("growing-list");
const addButton = document.getElementById("add-list-item");
addButton.addEventListener("click", () => {
const item = document.createElement("li");
item.textContent = new Date().toLocaleTimeString();
list.appendChild(item);
});
await playhtml.init({ developmentMode: true });
</script>
</body>
</html> Append a direct child
Section titled “Append a direct child”Create an element and append it to the mirrored list:
const item = document.createElement("li");
item.textContent = new Date().toLocaleTimeString();
list.appendChild(item);
The list owns the child change because the new li is a direct child.
Mutations deeper inside a child need their own can-mirror boundary.
Test the list
Section titled “Test the list”Add entries from both windows. Each new item should appear in both lists and remain after reload.
See the can-mirror playground for nested elements, forms, contenteditable, focus, and hover behavior.