Skip to content

Commit

Permalink
Better DOM management. Template and its last TextRenderer now referen…
Browse files Browse the repository at this point in the history
…ce the same last node, while first node is found as needed so no longer a requirement to pepper the DOM with first text nodes.
  • Loading branch information
stephband committed Jul 22, 2024
1 parent 88e6f6d commit 4bf52e9
Show file tree
Hide file tree
Showing 19 changed files with 494 additions and 496 deletions.
9 changes: 5 additions & 4 deletions literal-html/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import noop from '../../fn/modules/noop.js';
import Signal from '../../fn/modules/signal.js';
import element, { getInternals as Internals } from '../../dom/modules/element.js';
import requestData from '../modules/request-data.js';
import Template from '../modules/template.js';
import Template from '../modules/template.js';
import print from '../modules/scope/print.js';

const assign = Object.assign;
Expand Down Expand Up @@ -65,17 +65,18 @@ export default element('<template is="literal-html">', {
if (internals.initialised) { return; }
internals.initialised = true;

// Compute signal listens to changs
// Observe signal listens to signal value changes and calls fn() on next
// tick, as well as immediately if signal already has value
Signal.observe(internals.data, () => {
const { data, renderer } = internals;

if (!data.value) return;
renderer.push(data.value);
const fragment = renderer.push(data.value);

// Replace DOM content on first push
if (!internals.pushed) {
internals.pushed = true;
this.replaceWith(renderer.content);
this.replaceWith(fragment);
}
});

Expand Down
141 changes: 132 additions & 9 deletions literal-html/test.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@

<style>
body { columns: 2; column-gap: 2.5rem; margin: 1.25rem; font-family: sans-serif; }

table > * > tr > td,
table > * > tr > th {
text-align: left;
}

table > tbody > tr > td,
table > tbody > tr > th {
font-weight: 400;
}

.test { display: block; break-inside: avoid; }
.test + .test { border-top: 1px solid black; margin-top: 1.5rem; }
</style>
Expand Down Expand Up @@ -54,32 +65,144 @@ <h3>#template-6</h3>
<body>
<h1>Tests</h1>

<div class="test">
<!--table class="striped-table x-bleed test">
<thead>
<tr>
<th>Expression</th>
<th style="width:40%;">Render</th>
</tr>
</thead>
<tbody>
<tr>
<th><code>${ undefined }</code></th>
<td><template is="literal-html">${ undefined }</template></td>
</tr>
<tr>
<th><code>${ null }</code></th>
<td>
<template is="literal-html">${ null }</template>
</td>
</tr>
<tr>
<th><code>${ NaN }</code></th>
<td>
<template is="literal-html">${ NaN }</template>
</td>
</tr>
<tr>
<th><code>${ 'Hello' }</code></th>
<td>
<template is="literal-html">${ 'Hello' }</template>
</td>
</tr>
<tr>
<th><code>${ true }, ${ false }</code></th>
<td>
<template is="literal-html">${ true }, ${ false }</template>
</td>
</tr>
<tr>
<th><code>${ 123.4 }</code></th>
<td>
<template is="literal-html">${ 123.4 }</template>
</td>
</tr>
<tr>
<th><code>${ Infinity }, ${ -Infinity }</code></th>
<td>
<template is="literal-html">${ Infinity }, ${ -Infinity }</template>
</td>
</tr>
<tr>
<th><code>${ function name(param) {} }</code></th>
<td>
<template is="literal-html">${ function name(param) {} }</template>
</td>
</tr>
<tr>
<th><code>${ (param) => {} }</code></th>
<td>
<template is="literal-html">${ (param) => {} }</template>
</td>
</tr>
<tr>
<th><code>${ /^regexp/ }</code></th>
<td>
<template is="literal-html">${ /^regexp/ }</template>
</td>
</tr>
<tr>
<th><code>${ Symbol('name') }</code></th>
<td>
<template is="literal-html">${ Symbol('name') }</template>
</td>
</tr>
<tr>
<th><code>${ [0, 1, 2, 3] }</code></th>
<td>
<template is="literal-html">${ [0, 1, 2, 3] }</template>
</td>
</tr>
<tr>
<th><code>${ { property: 'value' } }</code></th>
<td>
<template is="literal-html">${ { property: 'value' } }</template>
</td>
</tr>
<tr>
<th><code>${ document.createTextNode('Hello') }</code></th>
<td>
<template is="literal-html">${ document.createTextNode('Hello') }</template>
</td>
</tr>
<tr>
<th><code>${ Promise.resolve('yoohoo') }</code></th>
<td>
<template is="literal-html">${ Promise.resolve('yoohoo') }</template>
</td>
</tr>
<tr>
<th><code>${ events('pointermove', body)<br/>
&nbsp;&nbsp;.map((e) => round(e.pageX)) }</code></th>
<td>
<template is="literal-html">${ events('pointermove', body).map((e) => round(e.pageX)) }</template>
</td>
</tr>
</tbody>
</table-->

<!--div class="test">
<template is="literal-html" data-state="1" data-value="Hello">
<h2>is="literal-html"</h2>
<p hidden="${ data.state }">Render counts in ().</p>
<p hidden="${ window.data = data, data.state }">Render counts in ().</p>
<p title="${ data.state }" class="${ data.state }">(${ this.renderCount }) ${ data.state }</p>
${ include('#template-1', data) }
${ include('#template-2', data) }
</template>
</div>
</div-->


<div class="test">
<template is="literal-html" data-state="1" data-value="Hello" data-template="2">
${ window.data = data, include('#template-' + data.template, data) }
</template>
</div>

<!--div class="test">
<template is="literal-html" data-state="1" data-value="Hello" data-template="2">
<h2>is="literal-html"</h2>
<p>Render counts in ().</p>
<!--${ window.data = data, '' }-->
<p class="${ 'hello world' }">${ 'Hello world' }</p>
<pre>(${ this.renderCount }) data = ${ data }</pre>
<pre>(${ this.renderCount }) data = ${ window.data = data, data }</pre>
<pre>(${ this.renderCount }) data.state = ${ data.state }</pre>
<pre>(${ this.renderCount }) data.value = ${ data.value }</pre>
(${ this.renderCount })
${ include('#template-' + data.template, data) }
${ window.data = data, include('#template-' + data.template, data) }
${ include('#template-2', data) }
</template>
</div>
<div class="test">
<!--div class="test">
<template is="literal-html" data-plonk="1" data-value="Hello" data-template="1">
<h2>Cued renders</h2>
<p>Asynchronous stopable stream of mutations to <code>data.template</code>.
Expand Down Expand Up @@ -123,7 +246,7 @@ <h2>is="literal-html"</h2>
.log('click')
.map((e) => request().then(include('#template-' + ((++data.template) % 2 + 1), data)))
}
</template-->
</template>
<div class="test">
<template is="literal-html" data-value="Hello World">
Expand Down Expand Up @@ -174,7 +297,7 @@ <h2>Print data</h2>
to an existing property triggers a fresh render.</p>
${ window.data = data, print(data) }
</template>
</div>
</div-->

<script type="module">
import '../scope.js';
Expand Down
6 changes: 0 additions & 6 deletions modules/compile/compile-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,6 @@ const compileNode = overload((targets, node) => toType(node), {
}

targets.push(target);

// Insert text node. When renderer is created with cloned DOM, clone of
// `node` is assigned to `renderer.first` and the clone of this new text
// node is assigned as `renderer.last`.
node.after(document.createTextNode(''));

return targets;
},

Expand Down
72 changes: 0 additions & 72 deletions modules/compile/compile_.js

This file was deleted.

2 changes: 1 addition & 1 deletion modules/define-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import element, { getInternals } from '../../dom/modules/element.js';
import toPrefetchPromise from '../../dom/modules/element/to-prefetch-promise.js';
import defineProperty from './define-property.js';
import getById from './dom/get-by-id.js';
import Template from './template.js';
import Template from './template.js';

const assign = Object.assign;
const entries = Object.entries;
Expand Down
17 changes: 17 additions & 0 deletions modules/dom/delete-range.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

export default function deleteRange(first, last) {
if (window.DEBUG && first.parentNode !== last.parentNode) {
throw new Error('deleteRange: first and last not children of same parent')
}

if (first === last) {
last.remove();
return;
}

// Select range of nodes and remove
const range = new Range();
range.setStartBefore(first);
range.setEndBefore(last);
range.deleteContents();
}
9 changes: 9 additions & 0 deletions modules/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ import TokensRenderer from './renderer/renderer-tokens.js';
import ValueRenderer from './renderer/renderer-value.js';
import TextRenderer from './renderer/renderer-text.js';

/*
Include bind() in parameters somehow...
AttributeRenderer.prototype.create.call(this, element, assign({
// Parameters
bind: (path, object, to = id, from = id) =>
bindValue(element, object, path, to, from, setValue)
}, parameters));
*/

Renderer.create = function create(signal, literal, parameters, element, n, debug) {
const name = typeof n === 'string' && (names[n] || n) ;

Expand Down
19 changes: 0 additions & 19 deletions modules/renderer/compose-boolean.js

This file was deleted.

14 changes: 0 additions & 14 deletions modules/renderer/compose-number.js

This file was deleted.

Loading

0 comments on commit 4bf52e9

Please sign in to comment.