Convert HTML To JavaScript
I created this page a while back and still want to do something with it, but haven;t gotten around to it. The original idea of this page was to convert HTML to client side JavaScript that would not suffer XSS attacks, but I have since figured out the way that I did this was not the best. I now want to create something that will help me generate translations from HTML, something that will transform JSON objects - including nested objects - to golang structs, and maybe something else.
Why HTML To JavaScript
Sometimes, you want to generate HTML on the client because you want to decrease server load, and sometimes you want to generate HTML on the client to prevent the DOM from becoming too big. By generating HTML on the client for the latter reason, you will improve the performance of the application.
Below, you can insert a <html>
string into the <textarea>
and submit the form to generate:
- JavaScript code that can be used to generate the html string as-is on the client.
-
A JSON object that can be used to generate HTML by feeding the JSON into the function below.
- This JSON object was included because sometimes you may want to edit the innerText of some DOM nodes based on the user's preferred language.
type ParsedElementCheerioObject = {
name: string,
type: "tag",
attributes: {[s:string]:string},
children: (ParsedElementCheerioObject|ParsedTextCheerioObject)[]
};
type ParsedTextCheerioObject = {
type: "text",
children: [],
data: string
}
type ParsedScriptCheerioObject = {
type: "script",
children: (ParsedElementCheerioObject|ParsedTextCheerioObject)[],
attributes: {[s:string]:string},
name: string
};
type ParsedStyleCheerioObject = {
name: string,
type: "style",
attributes: {[s:string]:string},
children: (ParsedElementCheerioObject|ParsedTextCheerioObject)[],
}
type ParsedCheerioObject = ParsedElementCheerioObject|ParsedTextCheerioObject|ParsedScriptCheerioObject|ParsedStyleCheerioObject;
const VALID_CHEERIO_OBJECT_TYPE = new Set(['tag','text','script','style'] as const);
function getNextVarName(varName:string) {
if (varName.endsWith('z')) {
return varName.slice(0,varName.length-1).concat('A');
} else if (varName.endsWith('Z')) {
return varName.split('').map((s) => 'a').join('').concat('a');
} else {
var s = varName.split('');
for (let i=0;i<s.length;i++) {
if (s[i]!=='Z') {
s[i] = s[i]==='z' ? 'A' : String.fromCharCode(s[i].charCodeAt(0)+1);
break;
}
}
return s.join('');
}
}
function createNodeStr(el:ParsedCheerioObject,str:string,curVarName:string) {
var newStr = str;
if (el.type==="tag"||el.type==="script"||el.type==="style"){
newStr += `const ${curVarName} = document.createElement("`.concat(el.name).concat('");\n');
Object.keys(el.attributes)
.forEach((key)=>{
newStr+= `${curVarName}.setAttribute("${key}",${JSON.stringify(el.attributes[key])});\n`;
})
const charsToAppend:string[] = [];
var latestVarName = curVarName;
for (let child of el.children) {
const { str:newString, latestVarName:newLatestVarName } = createNodeStr(child,newStr,getNextVarName(latestVarName));
latestVarName = newLatestVarName;
charsToAppend.push(newLatestVarName);
newStr = newString;
}
newStr+=`${curVarName}.append(`.concat(charsToAppend.map((s) => s).join(',')).concat(');\n');
return { str: newStr, latestVarName };
} else if (el.type==="text"){
newStr += `const ${curVarName} = document.createTextNode(\``.concat(el.data.replace(/`/g, '\\`')).concat(`\`);\n`);
return { str: newStr, latestVarName: curVarName };
} else throw new Error('Unrecognized object type.');
}
function creatingHTMLFromJSON(arr:ParsedCheerioObject[]) {
var curVarName = "a";
var str = '';
for (let obj of arr) {
const { str:newString, latestVarName } = createNodeStr(obj,str,curVarName);
curVarName = getNextVarName(latestVarName);
str = newString;
}
return str;
}
function createNode(obj:ParsedCheerioObject) {
if(obj.type==="tag"||obj.type==="script"||obj.type==="style") {
const node = document.createElement(obj.name);
Object.keys(obj.attributes)
.forEach((key) => node.setAttribute(key,JSON.stringify(obj.attributes[key])));
if(obj.children.length) {
node.append(...obj.children.map((obj) => createNode(obj)));
}
return node;
} else if (obj.type==="text") {
const textNode = document.createTextNode(obj.data);
return textNode;
} else throw new Error('Unrecognized Type');
}
function createNodesFromJSON(arr:ParsedCheerioObject[]) {
const retArr = [];
for (let obj of arr) {
retArr.push(createNode(obj));
}
return retArr;
}
This doesn't work well right now due to an issue with the template strings.