issue: #24 Add method insertAdjacentHTML

This commit is contained in:
taoqf 2020-02-16 17:43:01 +08:00
parent 45e77f3771
commit a72c0da9ee
3 changed files with 89 additions and 16 deletions

View file

@ -126,6 +126,10 @@ Query CSS Selector to find matching node.
Append a child node to childNodes Append a child node to childNodes
### HTMLElement#insertAdjacentHTML(where, html)
parses the specified text as HTML and inserts the resulting nodes into the DOM tree at a specified position.
### HTMLElement#firstChild ### HTMLElement#firstChild
Get first child node Get first child node

View file

@ -19,6 +19,8 @@ export interface RawAttributes {
[key: string]: string; [key: string]: string;
} }
export type InsertPosition = 'beforebegin' | 'afterbegin' | 'beforeend' | 'afterend';
const kBlockElements = { const kBlockElements = {
div: true, div: true,
p: true, p: true,
@ -57,7 +59,7 @@ export default class HTMLElement extends Node {
* *
* @memberof HTMLElement * @memberof HTMLElement
*/ */
constructor(public tagName: string, keyAttrs: KeyAttributes, private rawAttrs = '', public parentNode = null as Node) { public constructor(public tagName: string, keyAttrs: KeyAttributes, private rawAttrs = '', public parentNode = null as Node) {
super(); super();
this.rawAttrs = rawAttrs || ''; this.rawAttrs = rawAttrs || '';
this.parentNode = parentNode || null; this.parentNode = parentNode || null;
@ -97,7 +99,7 @@ export default class HTMLElement extends Node {
* Get escpaed (as-it) text value of current node and its children. * Get escpaed (as-it) text value of current node and its children.
* @return {string} text content * @return {string} text content
*/ */
get rawText() { public get rawText() {
return this.childNodes.reduce((pre, cur) => { return this.childNodes.reduce((pre, cur) => {
return pre += cur.rawText; return pre += cur.rawText;
}, ''); }, '');
@ -106,14 +108,14 @@ export default class HTMLElement extends Node {
* Get unescaped text value of current node and its children. * Get unescaped text value of current node and its children.
* @return {string} text content * @return {string} text content
*/ */
get text() { public get text() {
return decode(this.rawText); return decode(this.rawText);
} }
/** /**
* Get structured Text (with '\n' etc.) * Get structured Text (with '\n' etc.)
* @return {string} structured text * @return {string} structured text
*/ */
get structuredText() { public get structuredText() {
let currentBlock = [] as string[]; let currentBlock = [] as string[];
const blocks = [currentBlock]; const blocks = [currentBlock];
function dfs(node: Node) { function dfs(node: Node) {
@ -170,7 +172,7 @@ export default class HTMLElement extends Node {
} }
} }
get innerHTML() { public get innerHTML() {
return this.childNodes.map((child) => { return this.childNodes.map((child) => {
return child.toString(); return child.toString();
}).join(''); }).join('');
@ -186,7 +188,7 @@ export default class HTMLElement extends Node {
this.childNodes = content; this.childNodes = content;
} }
get outerHTML() { public get outerHTML() {
return this.toString(); return this.toString();
} }
@ -215,7 +217,7 @@ export default class HTMLElement extends Node {
* Get DOM structure * Get DOM structure
* @return {string} strucutre * @return {string} strucutre
*/ */
get structure() { public get structure() {
const res = [] as string[]; const res = [] as string[];
let indention = 0; let indention = 0;
function write(str: string) { function write(str: string) {
@ -383,7 +385,7 @@ export default class HTMLElement extends Node {
* Get first child node * Get first child node
* @return {Node} first child node * @return {Node} first child node
*/ */
get firstChild() { public get firstChild() {
return this.childNodes[0]; return this.childNodes[0];
} }
@ -391,7 +393,7 @@ export default class HTMLElement extends Node {
* Get last child node * Get last child node
* @return {Node} last child node * @return {Node} last child node
*/ */
get lastChild() { public get lastChild() {
return arr_back(this.childNodes); return arr_back(this.childNodes);
} }
@ -399,7 +401,7 @@ export default class HTMLElement extends Node {
* Get attributes * Get attributes
* @return {Object} parsed and unescaped attributes * @return {Object} parsed and unescaped attributes
*/ */
get attributes() { public get attributes() {
if (this._attrs) { if (this._attrs) {
return this._attrs; return this._attrs;
} }
@ -416,7 +418,7 @@ export default class HTMLElement extends Node {
* Get escaped (as-it) attributes * Get escaped (as-it) attributes
* @return {Object} parsed attributes * @return {Object} parsed attributes
*/ */
get rawAttributes() { public get rawAttributes() {
if (this._rawAttrs) if (this._rawAttrs)
return this._rawAttrs; return this._rawAttrs;
const attrs = {} as RawAttributes; const attrs = {} as RawAttributes;
@ -510,4 +512,29 @@ export default class HTMLElement extends Node {
} }
}).join(' '); }).join(' ');
} }
public insertAdjacentHTML(where: InsertPosition, html: string) {
if (arguments.length < 2) {
throw new Error('2 arguments required');
}
const p = parse(html) as HTMLElement;
if (where === 'afterend') {
p.childNodes.forEach((n) => {
(this.parentNode as HTMLElement).appendChild(n);
});
} else if (where === 'afterbegin') {
this.childNodes.unshift(...p.childNodes);
} else if (where === 'beforeend') {
p.childNodes.forEach((n) => {
this.appendChild(n);
});
} else if (where === 'beforebegin') {
(this.parentNode as HTMLElement).childNodes.unshift(...p.childNodes);
} else {
throw new Error(`The value provided ('${where}') is not one of 'beforebegin', 'afterbegin', 'beforeend', or 'afterend'`);
}
if (!where || html === undefined || html === null) {
return;
}
}
} }

View file

@ -416,11 +416,13 @@ describe('HTML Parser', function () {
}); });
describe('#hasAttribute', function () { describe('#hasAttribute', function () {
var root = parseHTML('<input required>'); it('should return true or false when has or has not some attribute', function () {
var input = root.firstChild; var root = parseHTML('<input required>');
input.hasAttribute('required').should.eql(true); var input = root.firstChild;
input.removeAttribute('required'); input.hasAttribute('required').should.eql(true);
input.hasAttribute('required').should.eql(false); input.removeAttribute('required');
input.hasAttribute('required').should.eql(false);
});
}); });
describe('#querySelector()', function () { describe('#querySelector()', function () {
@ -514,6 +516,46 @@ describe('HTML Parser', function () {
root.firstChild.rawAttributes.alt.should.eql('&laquo;Sogno'); root.firstChild.rawAttributes.alt.should.eql('&laquo;Sogno');
}); });
}); });
describe('#insertAdjacentHTML() should parse and insert childrens', function () {
it('shoud insert children after current node', function () {
const html = '<a><b></b></a>';
const root = parseHTML(html);
const a = root.firstChild;
const b = a.firstChild;
b.insertAdjacentHTML('afterend', '<c><d></d></c>');
a.toString().should.eql('<a><b></b><c><d></d></c></a>');
});
it('shoud insert children before current node', function () {
const html = '<a><b></b></a>';
const root = parseHTML(html);
const a = root.firstChild;
const b = a.firstChild;
b.insertAdjacentHTML('beforebegin', '<c></c>');
a.toString().should.eql('<a><c></c><b></b></a>');
});
it('shoud append children in current node', function () {
const html = '<a></a>';
const root = parseHTML(html);
const a = root.firstChild;
a.insertAdjacentHTML('beforeend', '<b></b>');
a.toString().should.eql('<a><b></b></a>');
a.insertAdjacentHTML('beforeend', '<c></c>');
a.toString().should.eql('<a><b></b><c></c></a>');
});
it('shoud insert children at position 0', function () {
const html = '<a></a>';
const root = parseHTML(html);
const a = root.firstChild;
a.insertAdjacentHTML('afterbegin', '<b></b>');
a.toString().should.eql('<a><b></b></a>');
a.insertAdjacentHTML('afterbegin', '<c></c>');
a.toString().should.eql('<a><c></c><b></b></a>');
});
});
}); });
describe('stringify', function () { describe('stringify', function () {