Angular 4在IE11中出现TypeError:无法获取apply属性问题排查
首先,这个错误的核心原因很明确:IE11并不支持scrollIntoViewIfNeeded()这个DOM方法,而你的focused指令里直接调用了它,导致Angular的Renderer.invokeElementMethod尝试调用一个不存在的方法的apply时抛出了错误(因为方法本身是undefined,自然没有apply属性)。
为什么移除[focused]="i === activeIndex"就正常?
因为这个绑定一移除,指令里的focused setter就不会被触发,也就不会执行那行调用scrollIntoViewIfNeeded()的代码,自然避开了IE11不支持的API。而移除API调用组件后正常,大概率是那个组件也用到了类似IE11不兼容的DOM方法,但从你给出的信息看,最直接的问题点就是focused指令里的这行代码。
验证这个结论
你可以直接在IE11的开发者工具控制台里,随便选一个DOM元素,执行element.scrollIntoViewIfNeeded(),会发现控制台直接提示这个方法不存在,这就是问题的源头。
修复方案
把scrollIntoViewIfNeeded()替换成IE11支持的scrollIntoView(),并且可以通过参数来实现类似的效果:
修改你的focused指令代码:
import {Directive, Input, Renderer, ElementRef} from '@angular/core' @Directive({ selector: '[focused]' }) export class FocusedDirective { @Input() set focused(value: boolean){ if(value){ // 替换为IE11支持的scrollIntoView,加上参数实现类似scrollIntoViewIfNeeded的行为 this.renderer.invokeElementMethod( this.elementRef.nativeElement, 'scrollIntoView', [{ behavior: 'auto', block: 'nearest' }] // 这个参数会让元素在可视区域时不滚动,和scrollIntoViewIfNeeded效果接近 ); } } constructor(private elementRef: ElementRef, private renderer: Renderer){} }
另外补充一点:你当前的polyfills都是针对ES6/ES7语法的(比如core-js的那些导入),但这类DOM方法的缺失是浏览器API支持问题,语法polyfill解决不了,所以就算你加了再多语法polyfill,也解决不了这个问题,必须针对性地替换兼容的API。
额外严谨兼容方案
如果需要同时兼容现代浏览器和IE11,可以先检查元素是否存在目标方法,再决定调用哪个:
@Input() set focused(value: boolean){ if(value){ const el = this.elementRef.nativeElement; if (el.scrollIntoViewIfNeeded) { this.renderer.invokeElementMethod(el, 'scrollIntoViewIfNeeded'); } else { this.renderer.invokeElementMethod(el, 'scrollIntoView', [{ behavior: 'auto', block: 'nearest' }]); } } }
内容的提问来源于stack exchange,提问作者sashafedsdev

