Last active
November 6, 2022 07:35
-
-
Save gordonbrander/6288ee006d218ff7574ac325e7ca7f50 to your computer and use it in GitHub Desktop.
select — sugarfree D3-style enter/update/exit DOM manipulation, using a simple function instead of method chaining
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// D3-style select-baed DOM updating. | |
// | |
// Rather than relying on jQuery-style method-chaining to update the DOM, | |
// we use a "view" object, an ordinary object that contains the following | |
// functions: | |
// | |
// - `enter(datum) -> Element` returns a DOM node for new elements. | |
// Element will be appended to parent. | |
// - `update(el, datum, old)` handles mutating an element in response to | |
// changes in data. `old` is the last-known data for this element. | |
// - `exit(el)` handles removing an element. | |
const enterWith = (enter, datum) => { | |
const el = enter(datum) | |
el.__data__ = datum | |
return el | |
} | |
export const updateWith = (update, el, datum) => { | |
update(el, datum, el.__data__) | |
el.__data__ = datum | |
} | |
export const select = ({update}, el, datum) => { | |
updateWith(update, el, datum) | |
} | |
export const selectAll = ({enter, update, exit}, query, parent, data) => { | |
const els = parent.querySelectorAll(':scope ' + query) | |
if (els.length === data.length) { | |
for (let i = 0; i < els.length; i++) { | |
updateWith(update, els[i], data[i]) | |
} | |
} else if (els.length < data.length) { | |
for (let i = 0; i < els.length; i++) { | |
updateWith(update, els[i], data[i]) | |
} | |
for (let i = els.length; i < data.length; i++) { | |
parent.appendChild(enterWith(enter, data[i])) | |
} | |
} else if (els.length > data.length) { | |
for (let i = 0; i < data.length; i++) { | |
updateWith(update, els[i], data[i]) | |
} | |
for (let i = data.length; i < els.length; i++) { | |
exit(els[i]) | |
} | |
} | |
} | |
export const Renderer = (view, query) => (parent, data) => | |
selectAll(view, query, parent, data) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Example</title> | |
<script type="module"> | |
import {Renderer} from './select.js' | |
const Li = { | |
enter: datum => { | |
const el = document.createElement('li') | |
el.innerText = datum.text | |
return el | |
}, | |
update: (el, datum, old) => { | |
if (datum !== old) { | |
el.innerText = datum.text | |
} | |
}, | |
exit: el => { | |
el.remove() | |
} | |
} | |
const renderUl = Renderer(Li, 'li') | |
const data = [ | |
{text: 'foo'}, | |
{text: 'bar'} | |
] | |
const ul = document.querySelector('ul') | |
// Reflect data to dom | |
renderUl(ul, data) | |
</script> | |
</head> | |
<body> | |
<ul id="parent"> | |
<li>Some static content that will be updated by script above</li> | |
</ul> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment