65.9K
CodeProject 正在变化。 阅读更多。
Home

TypeScript 100天 (第8天)

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2022 年 4 月 26 日

CPOL

6分钟阅读

viewsIcon

5082

一个简单的基于Web的计算器展示了如何让TypeScript与网页内容交互

在上一篇博客的结尾,我说过我们将开始学习如何在网页上使用 TypeScript。在这篇文章中,我们将创建一个简单的基于 Web 的计算器,展示 TypeScript 如何与网页内容进行交互。在开发代码的过程中,我想展示如何在 TypeScript 中使用“老派”的 JavaScript 风格函数。

一如既往,本文的代码可在GitHub上找到。

设计

我想限制计算器的输入,使其按钮按下操作仅限于一组已知的输入。基本上,我希望每个按钮都能触发一个数字或某种类型的运算符。为了选择我们要使用的运算符,我决定创建一个名为 calculator.ts 的 TypeScript 文件,并在其中添加一个 Operator 枚举。我已经将此文件添加到了解决方案的根目录。

enum Operator {
    add = '+',
    subtract = '-',
    multiply = '*',
    divide = '/',
    period = '.'
}

在我展示计算器 TypeScript 代码之前,先设置好 HTML 页面。

<!DOCTYPE html>
<head>
    <title>100 Days of TypeScript - Calculator</title>
</head>
<body>
    <table border="1">
        <th colspan="4"><input id="display"></input></th>
    </table>
    <script src="scripts/calculator.js"></script>
</body>

这里是一个 HTML 页面,显示了一个表格,带有一个标题行,宽度为 4 列,用于显示任何计算结果。我还添加了一个 script 标签来加载计算器脚本。您会注意到,虽然计算器 TypeScript 文件的源文件在 root 文件夹中,但 src 指向的是 scripts 文件夹。为了让 TypeScript 写入此文件夹,我们需要调整 tsconfig.json 文件。我已将我的 tsconfig 文件精简至此。

{
  "compilerOptions": {
    "lib": [
      "DOM", "ES2015"
    ], 
    "outDir": "./scripts",
    "strict": true,
    "noUnusedLocals": true,
  }
}

这些条目中有两个非常值得关注。outDir 键是我们设置 输出 目录的地方;这就是我将 calculator.js 文件写入 scripts 目录的方式。lib 条目也很有趣,因为我在这里添加了一个 DOM 条目。当我们在 lib 中指定条目时,我们是在告诉 TypeScript 需要引入哪些库,例如 ES2015 条目会引入 ECMA 功能。我们希望计算器能够做的一件事是将输入复制到剪贴板,这是一个在 window.navigator 对象上进行的操作。为了访问 navigator,我们必须引入 DOM 库,它允许我们与浏览器文档对象模型 (Document Object Model) 进行交互。有点令人困惑的是,您不需要 DOM 库即可与窗口文档等其他标准功能进行交互。

在将计算器按钮添加到表格之前,我还可以做一些其他的设置。我知道我的表格有一个名为 display 的输入字段。我稍后想与它交互,因此我将编写一个小辅助函数,让我在处理它时更轻松。我将创建一个变量来存储对该输入的引用。由于我将绑定到此值,因此我将添加一个辅助方法,该方法将在首次访问时惰性填充此引用,并在后续调用中返回存储的版本。

let displayElement: HTMLInputElement | null = null;
function getDisplay(): HTMLInputElement {
    if (!displayElement) {
        displayElement = <HTMLInputElement>document.getElementById('display');
    }
    return displayElement;
}

这段代码的意思是,displayElement 可以是 HTMLInputElement 类型,或者可以是 null,表示我们还没有将其连接到 display 元素。TypeScript 在这方面非常有用,我们可以强类型化我们的网页输入,因此在 JavaScript 中原本是 object 的内容可以被约束为实际类型。这很有用,因为它告诉我们有哪些属性和操作可供我们使用。为了填充 displayElement,我们使用 document.getElementById。这是一个标准的浏览器方法,允许我们根据 ID 选择元素(ID 在网页的实际标签中设置)。现在,document.getElementById 的返回类型是 object,所以我们需要使用一种称为类型转换的技术将其设置为适当的类型。TypeScript 提供了几种不同的对象转换方式,但在我们的情况下,我们使用 <> 来指定适当的类型。

让我们添加表格的其余部分。

<tr>
    <td colspan="2"><input type="button" onclick="clearAll()" value="Clear" /></td>
    <td colspan="2"><input type="button" onclick="copyToClipboard()" value="Mem" /></td>
</tr>
<tr>
    <td><input type="button" value="1" onclick="display(1)"/> </td>
    <td><input type="button" value="2" onclick="display(2)"/> </td>
    <td><input type="button" value="3" onclick="display(3)"/> </td>
    <td><input type="button" value="/" onclick="display(Operator.divide)"/> </td>
 </tr>
 <tr>
    <td><input type="button" value="4" onclick="display(4)"/> </td>
    <td><input type="button" value="5" onclick="display(5)"/> </td>
    <td><input type="button" value="6" onclick="display(6)"/> </td>
    <td><input type="button" value="-" onclick="display(Operator.subtract)"/> </td>
 </tr>
 <tr>
    <td><input type="button" value="7" onclick="display(7)"/> </td>
    <td><input type="button" value="8" onclick="display(8)"/> </td>
    <td><input type="button" value="9" onclick="display(9)"/> </td>
    <td><input type="button" value="+" onclick="display(Operator.add)"/> </td>
 </tr>
 <tr>
    <td><input type="button" value="." onclick="display(Operator.period)"/> </td>
    <td><input type="button" value="0" onclick="display(0)"/> </td>
    <td><input type="button" value="=" onclick="solve()"/> </td>
    <td><input type="button" value="*" onclick="display(Operator.multiply)"/> </td>
 </tr>

每个按钮根据我们想做的事情,都连接到四个函数之一。让我们从显示函数开始,该函数与我们之前添加到枚举中的数字和运算符相关联。

function display(value: number | Operator): void {
    const htmlElement = getDisplay();
    htmlElement.value = htmlElement.value.trim() + value;
}

此函数接受数字或运算符之一。我喜欢 TypeScript 为我们提供了联合运算符,可以使用 | 来表示值可以是其中一种类型。

在函数内部,我们使用上面编写的函数获取对输入元素的引用。一旦我们有了这个元素,我们就会从中获取值,并将数字或运算符添加到其中。我对 htmlElement.value 调用 trim() 操作,以防用户在输入末尾留有空格。

如果我想清除输入,可以使用以下方法:

function clearAll(): void {
    const htmlElement = getDisplay();
    htmlElement.value = '';
}

这与我们的显示函数非常相似,因为它获取 html 输入元素(事实上,我们将看到我们所有的函数都会这样做)。一旦有了引用,它就会直接与 value 交互并将其设置为空字符串。

您可能会认为 solve 函数会很复杂,需要解析我们的输入并对其进行计算。实际上,借助一个名为 eval 的标准 JavaScript 函数,它可以评估输入的结果,这个函数非常简单。

function solve(): void {
    const htmlElement = getDisplay();
    const output = eval(htmlElement.value);
    htmlElement.value = output;
}

最后,我们来看一下将输入复制到剪贴板的代码。如前所述,我们将利用 navigator 对象,这就需要我们导入 DOM 库。

function copyToClipboard(): void {
    const htmlElement = getDisplay();
    navigator.clipboard.writeText(htmlElement.value);
}

您可能还记得我说过 navigator 对象位于 window.navigator 中。为了方便起见,我们的代码通常不需要指定 window 部分,因此我们可以直接访问 navigator

最后说明

您可能会问,为什么我选择使用 input type="button" 而不是直接使用 button 元素。我选择 input 方式是因为人们会熟悉它,而且我不需要设置按钮内的其他内容,例如显示图像。

结论

就是这样。这就是我们第一个由 TypeScript 驱动的网页。我希望您对将 TypeScript 连接到 HTML 的便捷程度感到印象深刻。在第 9 天,我们将继续深入 Web 开发的世界,学习创建一个简单的共享报告应用程序。

© . All rights reserved.