From 4f37f86433dc9abe5d1932bcce93ff0dcd857973 Mon Sep 17 00:00:00 2001 From: Georgios Kalpakas Date: Sun, 18 Jun 2017 12:29:48 +0300 Subject: [PATCH] fix(aio): switch from `innerText` to `textContent` to support older browsers `innerText` is not supported in Firefox prior to v45. In most cases (at least the ones we are interested in), `innerText` and `textContent` work equally well, but `textContent` is more performant (as it doesn't require a reflow). From [MDN][1] on the differences of `innerText` vs `textContent`: > - [...] > - `innerText` is aware of style and will not return the text of hidden > elements, whereas `textContent` will. > - As `innerText` is aware of CSS styling, it will trigger a reflow, whereas > `textContent` will not. > - [...] [1]: https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent#Differences_from_innerText Fixes #17585 --- .../examples/setup/src/app/app.component.spec.ts | 2 +- .../template-syntax/src/app/app.component.ts | 2 +- aio/src/app/app.component.spec.ts | 8 ++++---- .../embedded/code/code-example.component.spec.ts | 2 +- aio/src/app/embedded/code/code.component.spec.ts | 6 +++--- aio/src/app/embedded/code/code.component.ts | 6 +++--- .../app/embedded/current-location.component.spec.ts | 4 ++-- .../live-example/live-example.component.spec.ts | 6 +++--- aio/src/app/embedded/toc/toc.component.spec.ts | 13 ++++++++----- .../app/layout/doc-viewer/doc-viewer.component.ts | 2 +- .../search-results/search-results.component.spec.ts | 2 +- aio/src/app/shared/toc.service.spec.ts | 4 ++-- aio/src/app/shared/toc.service.ts | 4 ++-- 13 files changed, 32 insertions(+), 29 deletions(-) diff --git a/aio/content/examples/setup/src/app/app.component.spec.ts b/aio/content/examples/setup/src/app/app.component.spec.ts index 7769024464..4998dec904 100644 --- a/aio/content/examples/setup/src/app/app.component.spec.ts +++ b/aio/content/examples/setup/src/app/app.component.spec.ts @@ -27,7 +27,7 @@ describe('AppComponent', function () { it('should have expected

text', () => { fixture.detectChanges(); const h1 = de.nativeElement; - expect(h1.innerText).toMatch(/angular/i, + expect(h1.textContent).toMatch(/angular/i, '

should say something about "Angular"'); }); }); diff --git a/aio/content/examples/template-syntax/src/app/app.component.ts b/aio/content/examples/template-syntax/src/app/app.component.ts index 8211e563c2..2c65ef1c20 100644 --- a/aio/content/examples/template-syntax/src/app/app.component.ts +++ b/aio/content/examples/template-syntax/src/app/app.component.ts @@ -111,7 +111,7 @@ export class AppComponent implements AfterViewInit, OnInit { } onSave(event: KeyboardEvent) { - let evtMsg = event ? ' Event target is ' + (event.target).innerText : ''; + let evtMsg = event ? ' Event target is ' + (event.target).textContent : ''; this.alert('Saved.' + evtMsg); if (event) { event.stopPropagation(); } } diff --git a/aio/src/app/app.component.spec.ts b/aio/src/app/app.component.spec.ts index 86e9dac62a..6f45d2cc5b 100644 --- a/aio/src/app/app.component.spec.ts +++ b/aio/src/app/app.component.spec.ts @@ -398,19 +398,19 @@ describe('AppComponent', () => { it('should display a guide page (guide/pipes)', () => { locationService.go('guide/pipes'); fixture.detectChanges(); - expect(docViewer.innerText).toMatch(/Pipes/i); + expect(docViewer.textContent).toMatch(/Pipes/i); }); it('should display the api page', () => { locationService.go('api'); fixture.detectChanges(); - expect(docViewer.innerText).toMatch(/API/i); + expect(docViewer.textContent).toMatch(/API/i); }); it('should display a marketing page', () => { locationService.go('features'); fixture.detectChanges(); - expect(docViewer.innerText).toMatch(/Features/i); + expect(docViewer.textContent).toMatch(/Features/i); }); it('should update the document title', () => { @@ -632,7 +632,7 @@ describe('AppComponent', () => { describe('footer', () => { it('should have version number', () => { const versionEl: HTMLElement = fixture.debugElement.query(By.css('aio-footer')).nativeElement; - expect(versionEl.innerText).toContain(TestHttp.versionFull); + expect(versionEl.textContent).toContain(TestHttp.versionFull); }); }); diff --git a/aio/src/app/embedded/code/code-example.component.spec.ts b/aio/src/app/embedded/code/code-example.component.spec.ts index ce57a96106..5dc9b9ddf6 100644 --- a/aio/src/app/embedded/code/code-example.component.spec.ts +++ b/aio/src/app/embedded/code/code-example.component.spec.ts @@ -62,7 +62,7 @@ describe('CodeExampleComponent', () => { TestBed.overrideComponent(HostComponent, { set: {template: ''}}); createComponent(oneLineCode); - const actual = codeExampleDe.query(By.css('header')).nativeElement.innerText; + const actual = codeExampleDe.query(By.css('header')).nativeElement.textContent; expect(actual).toBe('Great Example'); }); diff --git a/aio/src/app/embedded/code/code.component.spec.ts b/aio/src/app/embedded/code/code.component.spec.ts index 51fffd6278..56609dcbf3 100644 --- a/aio/src/app/embedded/code/code.component.spec.ts +++ b/aio/src/app/embedded/code/code.component.spec.ts @@ -116,7 +116,7 @@ describe('CodeComponent', () => { hostComponent.linenums = false; hostComponent.code = ' abc\n let x = text.split(\'\\n\');\n ghi\n\n jkl\n'; fixture.detectChanges(); - const codeContent = codeComponentDe.nativeElement.querySelector('code').innerText; + const codeContent = codeComponentDe.nativeElement.querySelector('code').textContent; expect(codeContent).toEqual('abc\n let x = text.split(\'\\n\');\nghi\n\njkl'); }); @@ -124,7 +124,7 @@ describe('CodeComponent', () => { hostComponent.linenums = false; hostComponent.code = '\n\n\n' + smallMultiLineCode + '\n\n\n'; fixture.detectChanges(); - const codeContent = codeComponentDe.nativeElement.querySelector('code').innerText; + const codeContent = codeComponentDe.nativeElement.querySelector('code').textContent; expect(codeContent).toEqual(codeContent.trim()); }); @@ -141,7 +141,7 @@ describe('CodeComponent', () => { function getErrorMessage() { const missing: HTMLElement = codeComponentDe.nativeElement.querySelector('.code-missing'); - return missing ? missing.innerText : null; + return missing ? missing.textContent : null; } it('should not display "code-missing" class when there is some code', () => { diff --git a/aio/src/app/embedded/code/code.component.ts b/aio/src/app/embedded/code/code.component.ts index 280fc0a33c..98e3315799 100644 --- a/aio/src/app/embedded/code/code.component.ts +++ b/aio/src/app/embedded/code/code.component.ts @@ -108,7 +108,7 @@ export class CodeComponent implements OnChanges { if (!this.code) { const src = this.path ? this.path + (this.region ? '#' + this.region : '') : ''; - const srcMsg = src ? ` for
${src}` : '.'; + const srcMsg = src ? ` for\n${src}` : '.'; this.setCodeHtml(`

The code sample is missing${srcMsg}

`); return; } @@ -129,8 +129,8 @@ export class CodeComponent implements OnChanges { } doCopy() { - // We take the innerText because we don't want it to be HTML encoded - const code = this.codeContainer.nativeElement.innerText; + // We take the textContent because we don't want it to be HTML encoded + const code = this.codeContainer.nativeElement.textContent.trim(); if (this.copier.copyText(code)) { this.logger.log('Copied code to clipboard:', code); // success snackbar alert diff --git a/aio/src/app/embedded/current-location.component.spec.ts b/aio/src/app/embedded/current-location.component.spec.ts index 1dfc1a0f65..e6142b4cd6 100644 --- a/aio/src/app/embedded/current-location.component.spec.ts +++ b/aio/src/app/embedded/current-location.component.spec.ts @@ -25,11 +25,11 @@ describe('CurrentLocationComponent', () => { it('should render the current location', () => { fixture.detectChanges(); - expect(element.innerText).toEqual('initial/url'); + expect(element.textContent).toEqual('initial/url'); locationService.urlSubject.next('next/url'); fixture.detectChanges(); - expect(element.innerText).toEqual('next/url'); + expect(element.textContent).toEqual('next/url'); }); }); diff --git a/aio/src/app/embedded/live-example/live-example.component.spec.ts b/aio/src/app/embedded/live-example/live-example.component.spec.ts index d67d288fcc..87a8a4c08b 100644 --- a/aio/src/app/embedded/live-example/live-example.component.spec.ts +++ b/aio/src/app/embedded/live-example/live-example.component.spec.ts @@ -183,7 +183,7 @@ describe('LiveExampleComponent', () => { testComponent(() => { const expectedTitle = 'live example'; const anchor = getLiveExampleAnchor(); - expect(anchor.innerText).toBe(expectedTitle, 'anchor content'); + expect(anchor.textContent).toBe(expectedTitle, 'anchor content'); expect(anchor.getAttribute('title')).toBe(expectedTitle, 'title'); }); }); @@ -193,7 +193,7 @@ describe('LiveExampleComponent', () => { setHostTemplate(``); testComponent(() => { const anchor = getLiveExampleAnchor(); - expect(anchor.innerText).toBe(expectedTitle, 'anchor content'); + expect(anchor.textContent).toBe(expectedTitle, 'anchor content'); expect(anchor.getAttribute('title')).toBe(expectedTitle, 'title'); }); }); @@ -203,7 +203,7 @@ describe('LiveExampleComponent', () => { setHostTemplate(''); testComponent(() => { const anchor = getLiveExampleAnchor(); - expect(anchor.innerText).toBe(liveExampleContent, 'anchor content'); + expect(anchor.textContent).toBe(liveExampleContent, 'anchor content'); expect(anchor.getAttribute('title')).toBe(liveExampleContent, 'title'); }); }); diff --git a/aio/src/app/embedded/toc/toc.component.spec.ts b/aio/src/app/embedded/toc/toc.component.spec.ts index 06cfc4eaf2..f6a5c36f9a 100644 --- a/aio/src/app/embedded/toc/toc.component.spec.ts +++ b/aio/src/app/embedded/toc/toc.component.spec.ts @@ -83,11 +83,14 @@ describe('TocComponent', () => { it('should only display H2 and H3 TocItems', () => { tocService.tocList.next([tocItem('Heading A', 'h1'), tocItem('Heading B'), tocItem('Heading C', 'h3')]); fixture.detectChanges(); - const items = tocComponentDe.queryAllNodes(By.css('li')); - expect(items.length).toBe(2); - expect(items.find(item => item.nativeNode.innerText === 'Heading A')).toBeFalsy(); - expect(items.find(item => item.nativeNode.innerText === 'Heading B')).toBeTruthy(); - expect(items.find(item => item.nativeNode.innerText === 'Heading C')).toBeTruthy(); + + const tocItems = tocComponentDe.queryAllNodes(By.css('li')); + const textContents = tocItems.map(item => item.nativeNode.textContent.trim()); + + expect(tocItems.length).toBe(2); + expect(textContents.find(text => text === 'Heading A')).toBeFalsy(); + expect(textContents.find(text => text === 'Heading B')).toBeTruthy(); + expect(textContents.find(text => text === 'Heading C')).toBeTruthy(); expect(setPage().tocH1Heading).toBeFalsy(); }); diff --git a/aio/src/app/layout/doc-viewer/doc-viewer.component.ts b/aio/src/app/layout/doc-viewer/doc-viewer.component.ts index 97e8eb41c5..39678cbd0f 100644 --- a/aio/src/app/layout/doc-viewer/doc-viewer.component.ts +++ b/aio/src/app/layout/doc-viewer/doc-viewer.component.ts @@ -97,7 +97,7 @@ export class DocViewerComponent implements DoCheck, OnDestroy { // Only create TOC for docs with an

title // If you don't want a TOC, add "no-toc" class to

if (titleEl) { - title = titleEl.innerText.trim(); + title = titleEl.textContent.trim(); if (!/(no-toc|notoc)/i.test(titleEl.className)) { this.tocService.genToc(this.hostElement, docId); titleEl.insertAdjacentHTML('afterend', ''); diff --git a/aio/src/app/search/search-results/search-results.component.spec.ts b/aio/src/app/search/search-results/search-results.component.spec.ts index cc128bda56..5a82255a03 100644 --- a/aio/src/app/search/search-results/search-results.component.spec.ts +++ b/aio/src/app/search/search-results/search-results.component.spec.ts @@ -12,7 +12,7 @@ describe('SearchResultsComponent', () => { let searchResults: Subject; /** Get all text from component element */ - function getText() { return fixture.debugElement.nativeElement.innerText; } + function getText() { return fixture.debugElement.nativeElement.textContent; } /** Get a full set of test results. "Take" what you need */ function getTestResults(take?: number) { diff --git a/aio/src/app/shared/toc.service.spec.ts b/aio/src/app/shared/toc.service.spec.ts index 2e79b881c6..a40385f4e1 100644 --- a/aio/src/app/shared/toc.service.spec.ts +++ b/aio/src/app/shared/toc.service.spec.ts @@ -258,10 +258,10 @@ describe('TocService', () => { expect(tocItem.level).toEqual('h3'); }); - it('should have title which is heading\'s innerText ', () => { + it('should have title which is heading\'s textContent ', () => { const heading = headings[3]; const tocItem = lastTocList[3]; - expect(heading.innerText).toEqual(tocItem.title); + expect(heading.textContent).toEqual(tocItem.title); }); it('should have "SafeHtml" content which is heading\'s innerHTML ', () => { diff --git a/aio/src/app/shared/toc.service.ts b/aio/src/app/shared/toc.service.ts index 53c71bab16..45dd96c2c3 100644 --- a/aio/src/app/shared/toc.service.ts +++ b/aio/src/app/shared/toc.service.ts @@ -37,7 +37,7 @@ export class TocService { content: this.extractHeadingSafeHtml(heading), href: `${docId}#${this.getId(heading, idMap)}`, level: heading.tagName.toLowerCase(), - title: heading.innerText.trim(), + title: heading.textContent.trim(), })); this.tocList.next(tocList); @@ -87,7 +87,7 @@ export class TocService { if (id) { addToMap(id); } else { - id = h.innerText.toLowerCase().replace(/\W+/g, '-'); + id = h.textContent.trim().toLowerCase().replace(/\W+/g, '-'); id = addToMap(id); h.id = id; }