add example: transform custom element / xml to html
milahu opened this issue · 2 comments
milahu commented
function customElementTransformer(nameCustom, options = {}) {
const defaultOptions = {
name: 'div',
class: nameCustom,
};
options = Object.assign(defaultOptions, options);
const domTransformer = {
selector: nameCustom,
transform: ({ elements, document }) => {
for (let e = 0; e < elements.length; e++) {
const element = elements[e];
const elementNew = document.createElement(options.name);
for (let a = 0; a < element.attributes.length; a++) {
const attr = element.attributes[a];
elementNew.setAttribute(attr.name, attr.value); // copy attribute
}
elementNew.innerHTML = element.innerHTML; // copy content
if (options.class) elementNew.classList.add(options.class);
//element.parentNode.replaceChild(elementNew, element);
element.replaceWith(elementNew);
}
},
};
return domTransformer;
}
eleventyConfig.addPlugin(transformDomPlugin, [
customElementTransformer('page'),
customElementTransformer('nw', { name: 'span', class: 'nowrap-element' }),
]);
sample input
<page>
hello world, here are <nw>four non wrapping words</nw>
</page>
sample output
<div class="page">
hello world, here are <span class="nowrap-element">four non wrapping words</span>
</div>
benefits:
- the class is also visible in the closing tag
- shorter open-tags
milahu commented
more general
function getElementTransformer(selector, options = {}) {
const defaultOptions = {
name: 'div',
attributes: (e => Object.fromEntries(Array.from(e.attributes).map(a => [a.name, a.value]))),
class: (e => e.localName),
};
options = Object.assign(defaultOptions, options);
const getName = (typeof options.name == 'function') ? (e => options.name(e)) : (() => options.name);
const getAttributes = (
(typeof options.attributes == 'function') ? (e => options.attributes(e)) :
(typeof options.attributes == 'object') ? (() => options.attributes) :
(() => false)
);
const getExtraClass = (
(typeof options.class == 'function') ? (e => options.class(e)) :
(typeof options.class == 'string') ? (() => options.class) :
(() => false)
);
const domTransformer = {
selector,
transform: ({ elements, document }) => {
for (let e = 0; e < elements.length; e++) {
const element = elements[e];
const nameNew = getName(element);
if (!nameNew) continue; // no replace
const attributesNew = getAttributes(element) || {};
const extraClassNew = getExtraClass(element);
const elementNew = document.createElement(nameNew);
for (const [name, value] of Object.entries(attributesNew)) {
elementNew.setAttribute(name, value);
}
elementNew.innerHTML = element.innerHTML;
if (extraClassNew) elementNew.classList.add(extraClassNew);
//element.parentNode.replaceChild(elementNew, element);
element.replaceWith(elementNew);
}
},
};
return domTransformer;
}
eleventyConfig.addPlugin(transformDomPlugin, [
getElementTransformer('page', { class: 'page-element' }),
getElementTransformer('nw', { name: 'span', class: 'nowrap-element' }),
...(['de', 'en']).map(langKey =>
getElementTransformer(langKey,
{ name: 'span', attributes: (e => ({ lang: e.localName })), class: false })
)
]);
liamfiddler commented
Wow! I hadn't considered the plugin being used in this way, but it's very cool!
Thanks for the detailed code too! I think this would be a really interesting example of how the plugin can be used 😃
I'm flat out at the moment - any chance you'd be willing to fork the repo, add your example to the "examples" directory (feel free to use one of the existing example directories as a template), and submit a PR?