I decided to present this as an idea since I started using this method everywhere I need to create/manipulate simple markup.
This is a suggestion for natively implementing a simple method that would allow to deprecate the usage of 'hard to read' code patterns such as:
function createMarkupObject(name, value)
var rootNode = document.createElement('div');
rootNode.className = 'root-node';
var labelNode = document.createElement('label');
labelNode.className = 'label-node';
labelNode.innerHTML = name;
var valueNode = document.createElement('span');
valueNode.className = 'value-node';
valueNode.innerHTML = value;
rootNode.appendChild(labelNode);
rootNode.appendChild(valueNode);
return {
root: rootNode,
name: labelNode,
value: valueNode
}
}
let refs = createMarkupObject('Code generated', 'hard to read');
document.body.appendChild(refs.root);
refs['name'].innerHTML = 'Still code generated';
refs['value'].innerHTML = 'Still hard to read';
In favour of a more elegant approach that would allow the creation of markup using strings as 'easy to read' templates for blocks of code:
const template = `
<div class="root-node">
<label class="label-node" ref="name"></label>
<span class="value-node" ref="value"></span>
</div>
`;
let refs = JSR.build(template);
document.body.appendChild(refs['root']);
refs['name'].innerHTML = 'Template generated';
refs['value'].innerHTML = 'easy to read';
Description
The working principle is very simple:
the template nodes whose references are to be returned in the output object, must contain a 'special' attribute named 'ref';
The value assigned to the node's 'ref' attribute will be the name of the respective reference property in the returned ouptut object.
For this reason, every 'ref' value should be unique to each node in the same template.
When a single root node exists in a HTML template string, one does not need to explicitly assign a 'ref' attribute to the template root node as this is always included in the returned output object, only child nodes of the template root node should be assigned with 'ref' attributes with the following exception:
If the template string contains more then 1 root node, only the first root node is returned as 'root' in the output object. The rest of the root nodes in the template string will require the assignment of 'ref' attributes in order to be returned in the output object.
The returned root reference will be specially usefull to place the resulting markup somewhere in the page body.
The resulting markup won't contain any 'ref' attributes as this would make the resulting markup invalid.
Benefits
The obvious advantage this feature has to offer, is the ability to work with 'easy to read' blocks of code.
HTML template strings can be directly defined in the code, or imported from other JS files or JSONs from server responses, or local html files, making it easier to build and organise multiple sets of templates.
Specially useful to render and update elements on a component based front end architecture.
The method
I originally implemented this as a js module, the principle is very simple yet very effective:
// define the namespace
const JSR = (function() {
// create an element to temporarily place the markup generated from string templates
var elementWrapper = document.createElement('div');
/** PUBLIC JSrefs > build(htmlStr)
* Converts a string containing HTML content into an object containing references to the referenced HTML nodes.
* Nodes can be referenced by adding an attribute named 'ref' and setting its value to the desired name for the referece in the returned object:
* Input HTML string: '<div><ul ref="list"></ul></div>'
* Output returned object: { root: HTMLnodeRef div, list: HTMLnodeRef ul }
* Notice that the 'root' property is always returned, without requiring the addition of ref="root" to the HTML template root node.
* @param {htmlStr} htmlStr a string containing valid html
* @returns an object containing references to the nodes in the template
*/
function _buildHtmlStringTemplate(htmlStr) {
elementWrapper.innerHTML = htmlStr;
var nodeList = elementWrapper.getElementsByTagName('*');
var referencedNodes = {};
for (var i=0; i<nodeList.length; i++) {
for (var a=0; a<nodeList[i].attributes.length; a++) {
if (nodeList[i].attributes[a].nodeName.toLowerCase() === 'ref') {
referencedNodes[nodeList[i].attributes[a].nodeValue] = nodeList[i];
nodeList[i].removeAttribute('ref');
break;
}
}
}
referencedNodes['root'] = elementWrapper.children[0];
elementWrapper.innerHTML = null;
return referencedNodes;
}
// export public methods
return {
build: _buildHtmlStringTemplate
}
})()
Would this be something usefull in your opinion?
The original code can be found on the repo GitHub - diogomoreda/js-refs: lightweight JS module to facilitate the creation and manipulation of HTML nodes in the browser, using strings containing HTML code as input models