diff --git a/README.md b/README.md index 0f66e3d..965ec49 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,10 @@ Query CSS Selector to find matching node. 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 Get first child node diff --git a/src/nodes/html.ts b/src/nodes/html.ts index 1890224..de909a4 100644 --- a/src/nodes/html.ts +++ b/src/nodes/html.ts @@ -19,6 +19,8 @@ export interface RawAttributes { [key: string]: string; } +export type InsertPosition = 'beforebegin' | 'afterbegin' | 'beforeend' | 'afterend'; + const kBlockElements = { div: true, p: true, @@ -57,7 +59,7 @@ export default class HTMLElement extends Node { * * @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(); this.rawAttrs = rawAttrs || ''; 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. * @return {string} text content */ - get rawText() { + public get rawText() { return this.childNodes.reduce((pre, cur) => { return pre += cur.rawText; }, ''); @@ -106,14 +108,14 @@ export default class HTMLElement extends Node { * Get unescaped text value of current node and its children. * @return {string} text content */ - get text() { + public get text() { return decode(this.rawText); } /** * Get structured Text (with '\n' etc.) * @return {string} structured text */ - get structuredText() { + public get structuredText() { let currentBlock = [] as string[]; const blocks = [currentBlock]; 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 child.toString(); }).join(''); @@ -186,7 +188,7 @@ export default class HTMLElement extends Node { this.childNodes = content; } - get outerHTML() { + public get outerHTML() { return this.toString(); } @@ -215,7 +217,7 @@ export default class HTMLElement extends Node { * Get DOM structure * @return {string} strucutre */ - get structure() { + public get structure() { const res = [] as string[]; let indention = 0; function write(str: string) { @@ -383,7 +385,7 @@ export default class HTMLElement extends Node { * Get first child node * @return {Node} first child node */ - get firstChild() { + public get firstChild() { return this.childNodes[0]; } @@ -391,7 +393,7 @@ export default class HTMLElement extends Node { * Get last child node * @return {Node} last child node */ - get lastChild() { + public get lastChild() { return arr_back(this.childNodes); } @@ -399,7 +401,7 @@ export default class HTMLElement extends Node { * Get attributes * @return {Object} parsed and unescaped attributes */ - get attributes() { + public get attributes() { if (this._attrs) { return this._attrs; } @@ -416,7 +418,7 @@ export default class HTMLElement extends Node { * Get escaped (as-it) attributes * @return {Object} parsed attributes */ - get rawAttributes() { + public get rawAttributes() { if (this._rawAttrs) return this._rawAttrs; const attrs = {} as RawAttributes; @@ -510,4 +512,29 @@ export default class HTMLElement extends Node { } }).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; + } + } } diff --git a/test/html.js b/test/html.js index ebc0d93..0ea581d 100644 --- a/test/html.js +++ b/test/html.js @@ -416,11 +416,13 @@ describe('HTML Parser', function () { }); describe('#hasAttribute', function () { - var root = parseHTML(''); - var input = root.firstChild; - input.hasAttribute('required').should.eql(true); - input.removeAttribute('required'); - input.hasAttribute('required').should.eql(false); + it('should return true or false when has or has not some attribute', function () { + var root = parseHTML(''); + var input = root.firstChild; + input.hasAttribute('required').should.eql(true); + input.removeAttribute('required'); + input.hasAttribute('required').should.eql(false); + }); }); describe('#querySelector()', function () { @@ -514,6 +516,46 @@ describe('HTML Parser', function () { root.firstChild.rawAttributes.alt.should.eql('«Sogno'); }); }); + + describe('#insertAdjacentHTML() should parse and insert childrens', function () { + it('shoud insert children after current node', function () { + const html = ''; + const root = parseHTML(html); + const a = root.firstChild; + const b = a.firstChild; + b.insertAdjacentHTML('afterend', ''); + a.toString().should.eql(''); + }); + + it('shoud insert children before current node', function () { + const html = ''; + const root = parseHTML(html); + const a = root.firstChild; + const b = a.firstChild; + b.insertAdjacentHTML('beforebegin', ''); + a.toString().should.eql(''); + }); + + it('shoud append children in current node', function () { + const html = ''; + const root = parseHTML(html); + const a = root.firstChild; + a.insertAdjacentHTML('beforeend', ''); + a.toString().should.eql(''); + a.insertAdjacentHTML('beforeend', ''); + a.toString().should.eql(''); + }); + + it('shoud insert children at position 0', function () { + const html = ''; + const root = parseHTML(html); + const a = root.firstChild; + a.insertAdjacentHTML('afterbegin', ''); + a.toString().should.eql(''); + a.insertAdjacentHTML('afterbegin', ''); + a.toString().should.eql(''); + }); + }); }); describe('stringify', function () {