赋予网页健壮的灵魂 —— TypeScript(下)

7 DOM 操作与类型定义:赋予网页真正的交互

回到最初的目标:让网页动起来。在 TypeScript 中,我们如何安全地操作 HTML 元素并响应用户的交互呢?TypeScript 提供了内置的类型定义来描述浏览器环境中的各种对象(如 document, window, DOM 元素,事件对象等),这使得我们在操作 DOM 时也能享受到类型检查带来的便利和安全。

7.1 获取 DOM 元素及其类型

当你使用像 document.getElementById()document.querySelector() 这样的方法获取 DOM 元素时,TypeScript 会根据方法签名返回一个特定的类型。

  • document.getElementById() 返回 HTMLElement | null
  • document.querySelector() 返回 Element | null (更通用的类型)。

它们返回的可能是找到的元素类型(如果是特定标签,会有更精确的类型,比如 HTMLDivElement for <div>),或者在找不到元素时返回 null

// seventh.ts

// 获取一个 div 元素
const myDiv = document.getElementById("myDiv");

// myDiv 的类型是 HTMLElement | null
if (myDiv) {
  // 在这个 if 块中,myDiv 的类型被缩小为 HTMLElement
  myDiv.textContent = "你好,DOM!";
  // HTMLElement 类型有通用的属性和方法
  console.log("Div 元素的标签名:", myDiv.tagName);
} else {
  console.error("找不到 id 为 myDiv 的元素");
}

// 获取一个输入框元素
const myInput = document.getElementById("myInput");

// myInput 的类型是 HTMLElement | null
if (myInput) {
  // 如果确定它是 input 元素,并且你需要访问 input 特有的属性 (如 value),
  // 你需要使用类型断言或者更精确的选择器方法。
  // 直接访问 value 会报错,因为 HTMLElement 上没有 value 属性
  // console.log(myInput.value); // 编译时报错

  // 使用类型断言
  const inputElement = myInput as HTMLInputElement;
  console.log("输入框的值 (断言):", inputElement.value);

  // 或者使用更精确的查询方法(如果存在)或结合类型守卫(后面会讲)
  // const inputElement2 = document.querySelector<HTMLInputElement>("#myInput"); // 泛型方法
  // if (inputElement2) {
  //   console.log("输入框的值 (querySelector 泛型):", inputElement2.value);
  // }
}

7.2 类型断言用于 DOM 元素

当你明确知道获取到的元素的具体类型时(比如你确定一个通过 ID 获取的元素就是 <canvas>),可以使用类型断言将其断言为更精确的类型,以便访问该元素特有的属性和方法。

// seventh.ts (接着上面的代码)

// 假设 HTML 中有一个 <canvas id="myCanvas"></canvas>
const canvas = document.getElementById("myCanvas") as HTMLCanvasElement;

// 现在 TypeScript 知道 canvas 是 HTMLCanvasElement 类型,可以安全地访问 getContext 方法
if (canvas) {
  const ctx = canvas.getContext("2d");
  if (ctx) {
    ctx.fillStyle = 'red';
    ctx.fillRect(10, 10, 50, 50);
  }
}

7.3 事件处理

处理用户交互依赖于事件。TypeScript 为各种事件提供了类型定义,这使得在事件处理函数中访问事件对象 (evente) 的属性时也能获得类型提示和检查。

// seventh.ts (接着上面的代码)

const myButton = document.getElementById("myButton"); // 假设 HTML 中有一个 <button id="myButton">点击我</button>

if (myButton) {
  // 添加点击事件监听器
  myButton.addEventListener("click", (event: MouseEvent) => {
    // event 参数是 MouseEvent 类型,具有 clientX, clientY 等属性
    console.log("按钮被点击了!");
    console.log("点击位置:", event.clientX, event.clientY);
    // event 对象是 Event 或其子类型
    event.preventDefault(); // 阻止默认行为
  });
}

const myTextInput = document.getElementById("myTextInput") as HTMLInputElement; // 假设 HTML 中有一个 <input type="text" id="myTextInput">

if (myTextInput) {
  // 添加键盘按下事件监听器
  myTextInput.addEventListener("keydown", (event: KeyboardEvent) => {
    // event 参数是 KeyboardEvent 类型,具有 key, code 等属性
    console.log("键盘按下:", event.key, event.code);
    if (event.key === 'Enter') {
      console.log("按下了 Enter 键,当前输入框的值:", myTextInput.value);
    }
  });
}

常见的事件类型:Event, MouseEvent, KeyboardEvent, ChangeEvent, SubmitEvent 等。

7.4 获取第三方库的类型定义 (@types)

很多流行的 JavaScript 库(如 jQuery, React, Lodash 等)最初并不是用 TypeScript 编写的。为了让 TypeScript 用户能够享受到类型检查和智能提示,社区维护了大量的类型声明文件 (.d.ts 文件)。这些文件只包含类型信息,不包含具体的实现代码。

这些类型声明文件通常发布在 @types 组织下,你可以使用 npm 或 yarn 安装它们。

例如,如果你想在 TypeScript 项目中使用 Lodash 库:

  1. 安装 Lodash: npm install lodash
  2. 安装 Lodash 的类型声明文件: npm install @types/lodash --save-dev

现在,你就可以在 TypeScript 代码中导入并使用 Lodash 了,TypeScript 会根据 @types/lodash 中的类型信息为你提供强大的类型检查和代码补全。

// seventh.ts (接着上面的代码)

// 我们通过declare const _: any;告诉TypeScript全局lodash变量的存在
declare const _: any;

const numbers = [1, 2, 3, 4, 5];
const sumOfNumbers = _.sum(numbers); // TypeScript 知道 _.sum 接受一个 number 数组并返回一个 number
console.log("数字总和 (lodash):", sumOfNumbers);

const users = [
  { 'user': 'barney', 'age': 36, 'active': true },
  { 'user': 'fred',   'age': 40, 'active': false }
];

const activeUsers = _.filter(users, { 'active': true }); // TypeScript 知道 filter 的用法和返回类型
console.log("活跃用户 (lodash):", activeUsers);

当你安装一个本身就是用 TypeScript 编写的库时(比如 React 的新版本、Vue 3 等),它们通常已经自带了类型声明文件,你无需额外安装 @types 包。

编译 seventh.ts

tsc seventh.ts

会生成 seventh.js 文件。然后在 HTML 中引入 seventh.js,并确保你的 HTML 中有相应的 div, input, button, canvas 元素。

<!DOCTYPE html>
<html>
<head> <title>TS DOM 操作</title> </head>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<body>
  <div id="myDiv">这是一个 div</div>
  <input type="text" id="myInput" value="默认值">
  <input type="text" id="myTextInput">
  <button id="myButton">点击触发事件</button>
  <canvas id="myCanvas" width="200" height="100" style="border: 1px solid black;"></canvas>

  <script src="dist/seventh.js"></script> </body>
</html>

小结: TypeScript 提供了丰富的内置类型来描述 DOM 和事件对象,结合类型断言和 @types 声明文件,可以让我们在进行客户端 DOM 操作和使用第三方库时,编写出类型安全、易于维护的代码。

练习:

  1. 创建一个 HTML 文件,包含一个段落 <p id="myParagraph">原始文本</p> 和一个按钮 <button id="changeTextBtn">改变文本</button>
  2. 编写 TypeScript 代码:获取段落和按钮元素,为按钮添加点击事件监听器。在事件处理函数中,获取段落元素,并将其 textContent 修改为“文本已更新!”。确保在 TypeScript 代码中使用了合适的类型。
  3. 创建一个 HTML 文件,包含一个图像元素 <img id="myImage" src="" alt="图片"> 和一个输入框 <input type="text" id="imageUrlInput" placeholder="输入图片 URL">
  4. 编写 TypeScript 代码:获取图像元素和输入框。为输入框添加 change 事件监听器(当输入框的值改变并失去焦点时触发)。在事件处理函数中,获取输入框的值,并将其设置为图像元素的 src 属性。
  5. (进阶)选择一个你感兴趣的流行 JavaScript 库(如 Moment.js),尝试使用 npm 安装它以及对应的 @types 包,然后在 TypeScript 代码中导入并使用该库的功能,体验类型提示。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

低代码布道师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值