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
This commit is contained in:
parent
3093c55e9e
commit
4f37f86433
|
@ -27,7 +27,7 @@ describe('AppComponent', function () {
|
||||||
it('should have expected <h1> text', () => {
|
it('should have expected <h1> text', () => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const h1 = de.nativeElement;
|
const h1 = de.nativeElement;
|
||||||
expect(h1.innerText).toMatch(/angular/i,
|
expect(h1.textContent).toMatch(/angular/i,
|
||||||
'<h1> should say something about "Angular"');
|
'<h1> should say something about "Angular"');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -111,7 +111,7 @@ export class AppComponent implements AfterViewInit, OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
onSave(event: KeyboardEvent) {
|
onSave(event: KeyboardEvent) {
|
||||||
let evtMsg = event ? ' Event target is ' + (<HTMLElement>event.target).innerText : '';
|
let evtMsg = event ? ' Event target is ' + (<HTMLElement>event.target).textContent : '';
|
||||||
this.alert('Saved.' + evtMsg);
|
this.alert('Saved.' + evtMsg);
|
||||||
if (event) { event.stopPropagation(); }
|
if (event) { event.stopPropagation(); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -398,19 +398,19 @@ describe('AppComponent', () => {
|
||||||
it('should display a guide page (guide/pipes)', () => {
|
it('should display a guide page (guide/pipes)', () => {
|
||||||
locationService.go('guide/pipes');
|
locationService.go('guide/pipes');
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(docViewer.innerText).toMatch(/Pipes/i);
|
expect(docViewer.textContent).toMatch(/Pipes/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display the api page', () => {
|
it('should display the api page', () => {
|
||||||
locationService.go('api');
|
locationService.go('api');
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(docViewer.innerText).toMatch(/API/i);
|
expect(docViewer.textContent).toMatch(/API/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display a marketing page', () => {
|
it('should display a marketing page', () => {
|
||||||
locationService.go('features');
|
locationService.go('features');
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(docViewer.innerText).toMatch(/Features/i);
|
expect(docViewer.textContent).toMatch(/Features/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update the document title', () => {
|
it('should update the document title', () => {
|
||||||
|
@ -632,7 +632,7 @@ describe('AppComponent', () => {
|
||||||
describe('footer', () => {
|
describe('footer', () => {
|
||||||
it('should have version number', () => {
|
it('should have version number', () => {
|
||||||
const versionEl: HTMLElement = fixture.debugElement.query(By.css('aio-footer')).nativeElement;
|
const versionEl: HTMLElement = fixture.debugElement.query(By.css('aio-footer')).nativeElement;
|
||||||
expect(versionEl.innerText).toContain(TestHttp.versionFull);
|
expect(versionEl.textContent).toContain(TestHttp.versionFull);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ describe('CodeExampleComponent', () => {
|
||||||
TestBed.overrideComponent(HostComponent, {
|
TestBed.overrideComponent(HostComponent, {
|
||||||
set: {template: '<code-example title="Great Example"></code-example>'}});
|
set: {template: '<code-example title="Great Example"></code-example>'}});
|
||||||
createComponent(oneLineCode);
|
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');
|
expect(actual).toBe('Great Example');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -116,7 +116,7 @@ describe('CodeComponent', () => {
|
||||||
hostComponent.linenums = false;
|
hostComponent.linenums = false;
|
||||||
hostComponent.code = ' abc\n let x = text.split(\'\\n\');\n ghi\n\n jkl\n';
|
hostComponent.code = ' abc\n let x = text.split(\'\\n\');\n ghi\n\n jkl\n';
|
||||||
fixture.detectChanges();
|
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');
|
expect(codeContent).toEqual('abc\n let x = text.split(\'\\n\');\nghi\n\njkl');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ describe('CodeComponent', () => {
|
||||||
hostComponent.linenums = false;
|
hostComponent.linenums = false;
|
||||||
hostComponent.code = '\n\n\n' + smallMultiLineCode + '\n\n\n';
|
hostComponent.code = '\n\n\n' + smallMultiLineCode + '\n\n\n';
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const codeContent = codeComponentDe.nativeElement.querySelector('code').innerText;
|
const codeContent = codeComponentDe.nativeElement.querySelector('code').textContent;
|
||||||
expect(codeContent).toEqual(codeContent.trim());
|
expect(codeContent).toEqual(codeContent.trim());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ describe('CodeComponent', () => {
|
||||||
|
|
||||||
function getErrorMessage() {
|
function getErrorMessage() {
|
||||||
const missing: HTMLElement = codeComponentDe.nativeElement.querySelector('.code-missing');
|
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', () => {
|
it('should not display "code-missing" class when there is some code', () => {
|
||||||
|
|
|
@ -108,7 +108,7 @@ export class CodeComponent implements OnChanges {
|
||||||
|
|
||||||
if (!this.code) {
|
if (!this.code) {
|
||||||
const src = this.path ? this.path + (this.region ? '#' + this.region : '') : '';
|
const src = this.path ? this.path + (this.region ? '#' + this.region : '') : '';
|
||||||
const srcMsg = src ? ` for<br>${src}` : '.';
|
const srcMsg = src ? ` for\n${src}` : '.';
|
||||||
this.setCodeHtml(`<p class="code-missing">The code sample is missing${srcMsg}</p>`);
|
this.setCodeHtml(`<p class="code-missing">The code sample is missing${srcMsg}</p>`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -129,8 +129,8 @@ export class CodeComponent implements OnChanges {
|
||||||
}
|
}
|
||||||
|
|
||||||
doCopy() {
|
doCopy() {
|
||||||
// We take the innerText because we don't want it to be HTML encoded
|
// We take the textContent because we don't want it to be HTML encoded
|
||||||
const code = this.codeContainer.nativeElement.innerText;
|
const code = this.codeContainer.nativeElement.textContent.trim();
|
||||||
if (this.copier.copyText(code)) {
|
if (this.copier.copyText(code)) {
|
||||||
this.logger.log('Copied code to clipboard:', code);
|
this.logger.log('Copied code to clipboard:', code);
|
||||||
// success snackbar alert
|
// success snackbar alert
|
||||||
|
|
|
@ -25,11 +25,11 @@ describe('CurrentLocationComponent', () => {
|
||||||
|
|
||||||
it('should render the current location', () => {
|
it('should render the current location', () => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(element.innerText).toEqual('initial/url');
|
expect(element.textContent).toEqual('initial/url');
|
||||||
|
|
||||||
locationService.urlSubject.next('next/url');
|
locationService.urlSubject.next('next/url');
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(element.innerText).toEqual('next/url');
|
expect(element.textContent).toEqual('next/url');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -183,7 +183,7 @@ describe('LiveExampleComponent', () => {
|
||||||
testComponent(() => {
|
testComponent(() => {
|
||||||
const expectedTitle = 'live example';
|
const expectedTitle = 'live example';
|
||||||
const anchor = getLiveExampleAnchor();
|
const anchor = getLiveExampleAnchor();
|
||||||
expect(anchor.innerText).toBe(expectedTitle, 'anchor content');
|
expect(anchor.textContent).toBe(expectedTitle, 'anchor content');
|
||||||
expect(anchor.getAttribute('title')).toBe(expectedTitle, 'title');
|
expect(anchor.getAttribute('title')).toBe(expectedTitle, 'title');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -193,7 +193,7 @@ describe('LiveExampleComponent', () => {
|
||||||
setHostTemplate(`<live-example title="${expectedTitle}"></live-example>`);
|
setHostTemplate(`<live-example title="${expectedTitle}"></live-example>`);
|
||||||
testComponent(() => {
|
testComponent(() => {
|
||||||
const anchor = getLiveExampleAnchor();
|
const anchor = getLiveExampleAnchor();
|
||||||
expect(anchor.innerText).toBe(expectedTitle, 'anchor content');
|
expect(anchor.textContent).toBe(expectedTitle, 'anchor content');
|
||||||
expect(anchor.getAttribute('title')).toBe(expectedTitle, 'title');
|
expect(anchor.getAttribute('title')).toBe(expectedTitle, 'title');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -203,7 +203,7 @@ describe('LiveExampleComponent', () => {
|
||||||
setHostTemplate('<live-example title="ignore this title"></live-example>');
|
setHostTemplate('<live-example title="ignore this title"></live-example>');
|
||||||
testComponent(() => {
|
testComponent(() => {
|
||||||
const anchor = getLiveExampleAnchor();
|
const anchor = getLiveExampleAnchor();
|
||||||
expect(anchor.innerText).toBe(liveExampleContent, 'anchor content');
|
expect(anchor.textContent).toBe(liveExampleContent, 'anchor content');
|
||||||
expect(anchor.getAttribute('title')).toBe(liveExampleContent, 'title');
|
expect(anchor.getAttribute('title')).toBe(liveExampleContent, 'title');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -83,11 +83,14 @@ describe('TocComponent', () => {
|
||||||
it('should only display H2 and H3 TocItems', () => {
|
it('should only display H2 and H3 TocItems', () => {
|
||||||
tocService.tocList.next([tocItem('Heading A', 'h1'), tocItem('Heading B'), tocItem('Heading C', 'h3')]);
|
tocService.tocList.next([tocItem('Heading A', 'h1'), tocItem('Heading B'), tocItem('Heading C', 'h3')]);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const items = tocComponentDe.queryAllNodes(By.css('li'));
|
|
||||||
expect(items.length).toBe(2);
|
const tocItems = tocComponentDe.queryAllNodes(By.css('li'));
|
||||||
expect(items.find(item => item.nativeNode.innerText === 'Heading A')).toBeFalsy();
|
const textContents = tocItems.map(item => item.nativeNode.textContent.trim());
|
||||||
expect(items.find(item => item.nativeNode.innerText === 'Heading B')).toBeTruthy();
|
|
||||||
expect(items.find(item => item.nativeNode.innerText === 'Heading C')).toBeTruthy();
|
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();
|
expect(setPage().tocH1Heading).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
|
||||||
// Only create TOC for docs with an <h1> title
|
// Only create TOC for docs with an <h1> title
|
||||||
// If you don't want a TOC, add "no-toc" class to <h1>
|
// If you don't want a TOC, add "no-toc" class to <h1>
|
||||||
if (titleEl) {
|
if (titleEl) {
|
||||||
title = titleEl.innerText.trim();
|
title = titleEl.textContent.trim();
|
||||||
if (!/(no-toc|notoc)/i.test(titleEl.className)) {
|
if (!/(no-toc|notoc)/i.test(titleEl.className)) {
|
||||||
this.tocService.genToc(this.hostElement, docId);
|
this.tocService.genToc(this.hostElement, docId);
|
||||||
titleEl.insertAdjacentHTML('afterend', '<aio-toc class="embedded"></aio-toc>');
|
titleEl.insertAdjacentHTML('afterend', '<aio-toc class="embedded"></aio-toc>');
|
||||||
|
|
|
@ -12,7 +12,7 @@ describe('SearchResultsComponent', () => {
|
||||||
let searchResults: Subject<SearchResults>;
|
let searchResults: Subject<SearchResults>;
|
||||||
|
|
||||||
/** Get all text from component element */
|
/** 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 */
|
/** Get a full set of test results. "Take" what you need */
|
||||||
function getTestResults(take?: number) {
|
function getTestResults(take?: number) {
|
||||||
|
|
|
@ -258,10 +258,10 @@ describe('TocService', () => {
|
||||||
expect(tocItem.level).toEqual('h3');
|
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 heading = headings[3];
|
||||||
const tocItem = lastTocList[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 ', () => {
|
it('should have "SafeHtml" content which is heading\'s innerHTML ', () => {
|
||||||
|
|
|
@ -37,7 +37,7 @@ export class TocService {
|
||||||
content: this.extractHeadingSafeHtml(heading),
|
content: this.extractHeadingSafeHtml(heading),
|
||||||
href: `${docId}#${this.getId(heading, idMap)}`,
|
href: `${docId}#${this.getId(heading, idMap)}`,
|
||||||
level: heading.tagName.toLowerCase(),
|
level: heading.tagName.toLowerCase(),
|
||||||
title: heading.innerText.trim(),
|
title: heading.textContent.trim(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.tocList.next(tocList);
|
this.tocList.next(tocList);
|
||||||
|
@ -87,7 +87,7 @@ export class TocService {
|
||||||
if (id) {
|
if (id) {
|
||||||
addToMap(id);
|
addToMap(id);
|
||||||
} else {
|
} else {
|
||||||
id = h.innerText.toLowerCase().replace(/\W+/g, '-');
|
id = h.textContent.trim().toLowerCase().replace(/\W+/g, '-');
|
||||||
id = addToMap(id);
|
id = addToMap(id);
|
||||||
h.id = id;
|
h.id = id;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue