如果只是看解决方法,可以直接跳到第二小节
简述
Angular 1.x 版本是用 JavaScript 编写的,我们在百度 Angular 经常会搜索到 AngularJS,并不是 JavaScript 的什么衍生版本,就是 Angular 1.x。在后续版本中,改用 TypeScript 来重写了 Angular 框架。改动较大,所以做了个区分,Angular v1.x 就叫 AngularJS,v2 及后续版本统称为 Angular。
查资料和解决方案的时候,经常会搜索到大量的 AngularJS 内容,注意区分。
在这里提一下 Angular 的历史,是因为本文是在使用这个框架的时候遇到的,所以啰嗦两句。
问题来了
现在有如下 html 文件:
<!-- 这俩随便挑一个用就行 -->
<input type="text" id="infoInput" name="infoInput">
<textarea id="infoArea" name="infoArea" rows="8" cols="80"></textarea>
<!-- 这俩也随便挑一个用就行 -->
<span id="some">something happen!</span>
<p id="any">anything ok!</p>
我现在要通过 TypeScript 获取上面任意一个 DOM 元素,怎么做?有 JS 基础都知道,操作 DOM 可以通过 document
完成:
// 由于 DOM 元素的 ID 是惟一的,所以这种方式获取的是唯一的 DOM 元素
dom = document.getElementById('infoInput');
// name 属性是不唯一的,所以这种方式获取的是所有 name=infoInput 的 DOM 元素,即一个数组
dom1 = document.getElementsByName('infoInput');
而在 TypeScript 中当然也可以这么做,但是在具体使用的时候除了需要声明变量保存获取到的 DOM 元素之外,还有一点小小的问题。
// Angular 框架中
export class Some implements OnInit {ngOnInit() {let dom = document.getElementById('infoArea');
// 1. 获取输入框中的内容
let html = dom.innerHTML;
let val = dom.value;
// 2. 打印输出
console.log(html);
console.log(val);
}
}
这段代码写完会报一个错:
Property ‘value’ does not exist on type ‘HTMLElement’
不要紧,即使有错误提示,我们依旧可以运行并得到正确的结果。如果想在 ts 文件编译失败时不生成 js 文件,可以通过配置实现。
HTMLElement 是什么?这是一个对象,它包含了所有 HTML 元素公有的属性。
关于 HTMLElement 的详细内容以及浏览器的兼容,可以查看 MDN 的这篇文章
来看一张图:
图源自 nanaistaken 的博客。
如果你恰好有一点面向对象编程的知识,那么这张图就很容易理解,没有也没关系,毕竟无论是 js 还是 ts,现在都增加了 class 关键字,引入了类的思想。
经过上面的分析,我们能够知道:getElementXXX()
返回的是一个 HTMLElement 对象,而这个对象包含了所有 DOM 元素的公有属性。而每种不同类别的 DOM 元素,又有自己的特性,也就是图中的子类。
ts 会做编译检查,所以会有错误提示,而 js 则不检查,所以这也会留下安全隐患。
到这里,其实应该已经明白了现在这种情况该怎么解决以及以后该怎么使用 getElementXXX 函数了。
修改后的代码:
export class Some implements OnInit {ngOnInit() {
// *. 做一次类型转换,或者做类型断言
let dom = <HTMLInputElement>document.getElementById('infoArea');
let dom1 = document.getElementById('infoArea') as HTMLElement;
// 1. 获取输入框中的内容
let html = dom.innerHTML;
let val = dom.value;
// 2. 打印输出
console.log(html);
console.log(val);
}
}
总结
HTMLElement 是 DOM 结点共有的属性,TypeScript 库中抽取该属性作为一个公共接口,类似于其他面向对象语言如 Java 和 c ++ 中所说的基类。这样做可以保证在操作 DOM 结点的时候不会出现访问不存在属性的问题。
HTMLInputElement 是 HTMLElement 的一个子接口(或说子类,但 TypeScript 是支持 class 的,所以说接口更好一些),其内部封装了如 input,textarea 这类 dom 结点的属性。