基于文档对象模型(DOM)的综合实验项目设计
作者: 刘金华 郭珂言 俞群爱 汪彦龙
摘要:基于文档对象模型(DOM) 的JavaScript代码能够实现Web页面的动态控制,是Web开发技术课程的重点内容,其应用灵活涉及知识点较多,是学生掌握的难点。为了使学生有效的理解各个知识点,提高其利用所学知识解决实际问题的工程能力,设计了类似于开盲盒的DOM综合实验项目。该实验项目融合了DOM事件、DOM操作、函数this指针和对象通信等知识点。实践证明,通过项目的运行和调试有助于学生理解和掌握相关知识点。实验项目的趣味性有效地调动了学生丰富系项目能积极性,使其软件开发能力得到锻炼。
关键词:实验设计;文档对象模型;this指针;Web编程;类图
中图分类号:TP311 文献标识码:A
文章编号:1009-3044(2023)26-0115-05
开放科学(资源服务)标识码(OSID)
0 引言
Web前端开发涉及超文本标记语言(HTML) 、级联样式表(CSS) 和JavaScript编程三大技术。其中,HTML文档利用标记定义Web页面的结构和内容;CSS利用CSS规则定义HTML各标记的显示风格,决定Web页面的显示方式,CSS技术实现了内容与表现的分离;JavaScript是基于对象和事件驱动并具有相对安全性的客户端脚本语言。在HTML基础上,利用JavaScript可以开发交互式Web页面,使网页包含活动元素和更加精彩的内容。JavaScript脚本和HTML 文档对象模型(DOM) 共同控制Web页面的行为[1]。JavaScript通过事件处理程序来响应用户的事件(如单击鼠标等)和对Web页面的控制,是Web前端开发课程的重点内容,也是学生掌握的难点[1-5]。结合单个知识点演示实验来介绍DOM基本概念和用法的教学方式不利于学生从整体上把握基于DOM的动态页面开发。通过合理设计综合性实验有助于学生理解各个知识点,提高学生利用所学知识解决实际问题的工程能力[4,6]。
本文设计了一个DOM综合实验,实验涉及DOM基本概念、DOM结点访问、DOM事件及处理程序、对象(函数)的this指针及绑定和类定义及类间通信等内容。学生通过调试、分析和解决实验中遇到的问题,有效的加深了对JavaScript、DOM相关知识点和面向对象设计思想的理解。
1 DOM综合实验的相关知识
文档对象模型 (DOM) 是HTML和XML文档的编程接口。它提供了对HTML文档的结构化描述,将HTML文档解析为一个由节点对象(包含属性和方法的对象)组成的结构集合,即Web页面的完全的面向对象表述,提供了同一份HTML文档的另一种表现、存储和操作的方式。使用JavaScript等脚本语言能对它进行修改,修改后的影响会直接在Web页面上得到反映[7]。
1.1 DOM结点树
DOM 将 HTML文档表示成以HMTL元素对应对象作为节点的树形结构,称之为节点树。浏览器在加载HTML文档的同时构建其对应的DOM节点树。通常,HTML元素的每个属性在其对应DOM节点对象都有相对应的属性,通过 DOM节点对象的方法与属性,可以访问页面中的任何元素,并进行元素的修改、删除以及添加等操作。以下HTML文档对应的DOM节点树如图1所示。
<html lang="en">
<head>
<title>文档标题</title>
</head>
<body>
<a href="http://baidu.com">我的链接</a>
<h1>我的标题</h1>
</body>
</html>
图1中,文档(document) 节点表示HTML文档对应DOM节点树的根节点。HTML文档中的每个元素在节点树中都有其对应的DOM节点,节点树中节点的层次关系与对应HTML文档中文档元素的层次关系一致。
1.2 DOM结点对象获取
DOM结点树的document根节点对象提供了多个getElement和querySelector方法,分别根据HTML元素属性和CSS选择器来获取HTML元素对应的DOM节点对象[8],利用获取的节点对象可对HTML进行各种操作。以下代码为返回id属性值为id的HTML元素对应节点对象的两种方法。
let name = document.getElementById('id');
let name = document.querySelector('#id');
1.3 DOM添加HTML元素
文档对象模型中,document对象的createElement()方法用来创建新元素,该方法的参数为需要创建元素的标记名。DOM元素对象的appendChild方法用来将其参数对应的元素添加为该元素对象的最后一个孩子结点。
创建img元素的代码如下:
const img = document.createElement('img');
img.src = 'apple.jpg';
以上代码创建了一个img元素,但因为该DOM元素尚未添加到DOM节点树中,所以不会影响Web页面的显示。以下代码将所创建的img元素添加到文档的<body>元素中:
document.body.appendChild(img);
以上代码执行后,img元素节点就插入到了DOM节点树中,浏览器立即将该图片渲染出来并在Web页面上显示。
1.4 DOM事件
DOM事件是对用户输入,点击等行为的响应。JavaScript 与 HTML 页面的交互是通过事件实现的。DOM事件有三个要素:事件源(如:按钮)、事件名(如:鼠标单击)和事件处理程序(如:自定义的函数)。文档对象模型将许多DOM元素定义成为可以接收和处理事件的对象,这些DOM元素对象的addEventListener和removeEventListener方法为指定的事件类型添加和删除事件处理程序。这两个方法接收三个参数:事件名、事件处理函数和一个布尔值,true 表示在捕获阶段调用事件处理程序,false(默认值)表示在冒泡阶段调用事件处理函数。
DOM 中某个事件触发时,所有与该事件相关的信息都保存在一个名为 event 的事件对象中。正常情况下,event 对象是传给事件处理函数的唯一参数,事件处理函数从中获取与事件相关的各种信息[9]。
1.5 this关键字
JavaScript中的this是代表函数运行时自动生成的一个内部对象,其只能在函数内部使用,随着函数使用场合的不同,this的值会发生变化。通常有以下五种情况[10]:
1) 若函数前面没有对象去调用,那么this指向对象Window。在严格模式下,this指向undefined;
2) 若函数作为对象的属性调用时,this指向该对象;
3) 若函数由 new 运算符调用时,函数里的this就指向new 运算符返回的这个对象。
4) 事件处理函数中的this指向监听该事件的对象;
5) 箭头函数内部的this指向是固定的,为定义时的上层作用域。
JavaScript中的函数也是对象,该对象的bind方法可改变this的指向。
1.6 JavaScript对象通信
通常情况下,对象之间通信有以下三种方式:
1) 利用通信对象的双向包含来实现对象的通信。但从软件工程角度看,这是的处理方式是非常不好的。因为很多情况下这种包含关系有悖常理。例如,房间有门很正常,但门中有房间就不可思议了。
2) 利用定制事件来实现对象的通信。对象注册定制事件的监听器,事件触发后对象就能接收到事件并做相应处理。
3) 利用回调函数实现对象通信。包含对象定义回调函数,被包含对象在指定事件的处理程序中调用该回调函数。
2 DOM综合实验项目设计与实现
2.1 DOM综合实验项目功能
该综合实验项目实现了简单的开盲盒功能,用户可以设置盲盒的个数,程序生成设定数目的盲盒并将礼物随机放入各个盲盒之中。用户点击所选择的盲盒,盲盒中的礼物及其价值会显示出来。
2.2 DOM综合实验界面设计
根据项目功能,系统的用户界面应包括:输入盲盒个数的文本输入框;重新加载盲盒的按钮;代表盲盒的图片以及必要的提示文本。具体的界面如下图2所示。
2.3 DOM综合实验类设计
综合实验项目的主要目的是加深学生对关键知识点的理解,培养学生综合运用所学知识解决问题的能力。因此,类设计方面要尽可能简单,避免出现复杂类设计影响学生理解关键知识点的问题。根据综合实验项目的功能设计了App和Present两个类。类App负责创建用户界面,类Present负责盲盒创建及盲盒点击事件的处理。系统的类图[11-12]如图3所示。
图3中类App的构造方法(constructor)涉及盲盒创建和绑定函数this指针等知识点。_onPresentOpened方法是盲盒打开后的回调函数,涉及DOM操作HTML元素、DOM事件和JavaScript对象通信等知识点。类Present的构造方法(constructor)涉及DOM添加HTML元素、DOM事件和绑定函数this指针等知识点。_openPresent方法实现盲盒中礼物显示和盲盒打开后回调函调用等功能,涉及DOM操作HTML元素、DOM事件和JavaScript对象通信等知识点。以上类的方法中,各个关键知识点重复出现,有利于学生理解和掌握。
2.4 DOM综合实验主要方法和界面的实现
类Present构造函数的实现代码如下:
constructor(containerElement, present, onOpenCallback) {
this.containerElement = containerElement;
this.present = present;
this.onOpenCallback = onOpenCallback;
// 绑定事件处理函数的this指针.
this._openPresent = this._openPresent.bind(this);
// 创建img元素并将其加入容器.
this.image = document.createElement('img');
this.image.src = './img/index.png';
//添加单击(click)事件处理程序
this.image.addEventListener('click', this._openPresent);
this.containerElement.append(this.image);
}
构造函数的参数containerElement是DOM对象,为盲盒(Present类实例)的容器;参数present是包含代表盲盒中礼物的图片路径(name)和礼物的价值属性(value)的对象;参数onOpenCallback为回调函数对象,盲盒打开事件发生时被调用。