Помогите улучшить парсер html
Возможно, вопрос задан не совсем корректно, так что поясню. Я написал класс для редактирования html. Он еще не закончен, но не суть. В нем есть метод, который делает из строки html массив объектов с иерархической структурой, как в этом считанном html отрывке. Прошу помощи у более опытных разработчиков в улучшении этого парсера. Приму любые комментарии, отвечу на все вопросы. Пишу чисто в учебных целях, так что пояснения будут большим плюсом. Заранее спасибо всем откликнувшимся.
class HTMLEditor {
constructor(html) {
this.html = html;
this.obj = [];
this.tag = '';
this.paramKey = '';
this.paramVal = '';
this.paramFlag = true;
this.text = '';
this.state = '';
this.depth = 0;
this.elem = {};
this.directory = [
'area',
'base',
'br',
'col',
'hr',
'img',
'input',
'link',
'meta',
'param',
]
}
reset (html) {
this.html = html;
this.tag = '';
this.paramKey = '';
this.paramVal = '';
this.paramFlag = true; // true значит читаем ключ иначе читаем значение
this.text = '';
this.state = '';
this.depth = 0;
this.elem = {};
}
HTMLPrinter () {
const printChild = (child) => {
child.forEach(ch => {
for (let param in ch.params) {
ch.self.setAttribute(param, ch.params[param]);
}
ch.parent.self.append(ch.self);
if (ch.children) {
printChild (ch.children);
}
});
}
try {
if (!document.querySelector('debug')) {
const div = document.createElement('div');
div.classList = 'debug';
document.querySelector('body').append(div);
}
this.obj.forEach((item) => {
for (let param in item.params) {
item.self.setAttribute(param, item.params[param]);
console.log(item.self[param]);
}
if (item.parent) {
item.parent.self.append(item.self);
} else {
document.querySelector('.debug').append(item.self);
}
printChild(item.children);
console.log(item);
});
} catch (e) {
console.log(e);
}
}
HTMLParser () {
let parents = [];
let quote = '';
for (var i = 0; i < this.html.length; i++) {
if (this.html[i] === '<') {
this.text = this.text.replace('\\n').trim();
if (this.text) {
this.elem = {
'parent': parents[this.depth - 1],
'children': null,
'params': {},
'self' : this.text,
};
if (parents[this.depth - 1]) {
parents[this.depth - 1].children.push(this.elem);
}
}
this.state = 'rTag';
this.tag = '';
} else if (this.html[i] === '>') {
if (this.state === 'rTag') {
try {
this.elem = {
'parent': null,
'children': [],
'params': {
},
'self' : null,
};
this.elem.self = document.createElement(this.tag);
parents[this.depth] = this.elem;
if (this.depth === 0) {
this.obj.push(this.elem);
} else {
this.elem.parent = parents[this.depth - 1];
this.elem.parent.children.push(this.elem);
}
let flag = this.directory.some((el) => {
return this.tag === el;
});
if (!flag) this.depth++;
} catch (e) {
console.log("error, maybe the depth was -1", e);
}
}
this.state = 'rTex';
this.text = '';
this.paramVal = this.paramVal || true;
if (this.paramKey) {
this.elem.params[this.paramKey] = this.paramVal;
}
this.paramKey = '';
this.paramVal = '';
this.paramFlag = true;
} else if (this.html[i] === ' ') {
if (this.state === 'rTex') {
this.text += this.html[i];
}
if (this.state === 'rPar') {
if (this.paramFlag) {
this.paramVal = this.paramVal || true;
if (this.paramKey) {
this.elem.params[this.paramKey] = this.paramVal;
}
this.paramKey = '';
this.paramVal = '';
this.paramFlag = true;
} else {
this.paramVal += this.html[i];
}
}
if (this.state === 'rTag') {
try {
this.elem = {
'parent': null,
'children': [],
'params': {
},
'self' : null,
};
this.elem.self = document.createElement(this.tag);
parents[this.depth] = this.elem;
if (this.depth === 0) {
this.obj.push(this.elem);
} else {
this.elem.parent = parents[this.depth - 1];
this.elem.parent.children.push(this.elem);
}
this.state = 'rPar';
this.param = '';
let flag = this.directory.some((el) => {
return this.tag === el;
});
if (!flag) this.depth++;
} catch (e) {
console.log("error, maybe the depth was -1", e);
}
}
} else if (this.paramFlag && this.html[i] === '/') {
this.state = 'cTag';
this.depth--;
parents.pop();
} else {
switch (this.state) {
case 'rTag':
this.tag += this.html[i];
break;
case 'rPar':
try {
if (this.paramFlag && this.html[i] === "=") {
i++;
if (this.html[i] === "\"" || this.html[i] === "\'" || this.html[i] === "\`") {
this.paramFlag = !this.paramFlag;
quote = this.html[i];
} else {
throw "unexpected space, quote was expected";
}
} else if (!this.paramFlag && this.html[i] === quote) {
this.paramFlag = !this.paramFlag;
} else {
if (this.paramFlag) {
this.paramKey += this.html[i];
} else {
this.paramVal += this.html[i];
}
}
} catch (e) {
console.log(e);
}
break;
case 'rTex':
this.text += this.html[i];
break;
}
}
}
}
}
const str = `
<section class="popUp hide put">
<form class="TaskGen f f_col f_c" >
<textarea class="input" name="task" rows="8" cols="80">Разобрать входящие</textarea>
<input class="input" type="text" name="processig" value="Бессрочно">
<input class="input" type="text" name="complite" value="Бессрочно">
<input class="input" type="hidden" name="CreateTime" value="">
<button class="button" type="button" name="sendTask">Создать</button>
</form>
</section>
`;
const editor = new HTMLEditor(str);
editor.HTMLParser();
editor.HTMLPrinter();