一个程序猿和普通电脑用户,当他们浏览到一个效果炫酷的网页的时候,他们的反应是不太相同的:
普通用户会“我靠”一声,随即拿起手中的鼠标,到处点来点去,欣赏网页的精彩内容和炫酷效果;
而程序猿的反应,在“我靠”过后,脑子里即泛出无数的div+css,可能还会有js,并且快速用鼠标右键或键盘快捷键,默默的打开浏览器的开发者工具,边查看网页的代码边点着头“原来如此......”
身为程序猿的我们,无时无刻不想着一探那光鲜表面下真实的存在,这是我们的优良美德,查看别人的程序是怎么实现的,是一种快速学习和成长的途径。
JS全栈开发者所进行的前端开发,主要是以浏览器为载体,利用浏览器开放给我们的能力,组合出我们所期望的功能。所以学习如何控制浏览器,是我们的主要目标。
叠纸片
在前面的文章中我们提到过,一个基于浏览器的网页Web应用,构成它骨架的那部分,是HTML,我们在界面上看得到的内容,基本都是由HTML元素所构建成的,一行文字、一个输入框、连一块空白部分,也都来源于HTML元素。
编写一个HTML页面的过程,在我看来,就像是在一张大纸片上按照我们的设想去叠放小纸片的过程。纸片与纸片之间的摆放关系,有上下级的关系、也有兄弟同级的关系,比如用以下这个例子来理解:
一个HTML文件中的<body>元素,相当于那张铺在桌子上的最大的纸片;然后,我们在这张大纸片上,放了一张叫做container-1的小一点的灰色纸片;接着,又在container-1这张灰色纸片上面,顺序摆放3张分别带有文字的橙色纸片。
从上图右半部分的开发者工具栏中,我们可以清晰的看到这种摆放形态所对应的HTML代码:名为container-1的<div>标签是在<body>标签下一级,3个名为container-2的<div>又是在container-1的下一级,同时这3个container-2互为同级关系。
这种关系,可以用一张图来清晰的把它们的关系展示出来:
这看起来像一颗倒置的树,最上面是唯一的树根,往下是分支树杈和树叶。这张图中的每一个方框代表的HTML标签节点,我们叫它DOM节点(Document Object Model,文档对象模型),所以这一整棵树就叫做DOM树了。
再复杂的页面,归根结底,也都只是由这种简单的堆叠关系组成的,只不过是层次更深一些,DOM节点更多一些罢了。所以当你拿到设计师给你的页面平面设计稿的时候,别急着写代码,你首先需要分析一下页面的结构,在脑海中想象一下如何叠出这个页面的纸片模型或DOM树,这样你在写代码的时候就会比较胸有成竹,更有效率。
上帝之手
当一份HTML代码被写好并在浏览器中运行后,它并不是就固定下来,一成不变了。我们有办法随时感知所有这些HTML元素的状态(位置,尺寸,颜色等等)、随时挪动改变它们的层级关系、删除元素、或者创建新的元素。就如一只上帝之手,在触摸和摆弄着这些纸片。
这只上帝之手,就是浏览器开放给我们的JS API,通过JavaScript编程的方式,我们可以很灵活的去访问和操纵这棵DOM树。我们可以通过一些例子来学习和理解JS API的用法,由于起初的这些代码会比较简单,我们也没必要去专门创建js文件,只要借助浏览器自带的开发者工具,就可以很方便的随手运行我们的js代码片段。
window对象
让我们进入开发者工具的console
这一栏,我们可以在这里直接输入并运行JS代码,并立即可看到执行结果,这真是我们学习实践的好帮手啊!
来,让我们输入以下内容,并按回车键试试:
window
我们立马就看到了控制台上输出一个类型为Window的对象,让我们展开这个对象,查看详细内容,能看到如下图所示的内容:
window对象代表了当前的浏览器窗口,它是我们整个前端编程生涯中,最应该记住的一个对象,因为它包含了所有浏览器开放给JS的API,也就是说,所有的API都可以通过window对象来进行使用。
比如alert()这个函数,我们就可以这样来使用:
window.alert('Hello, world');
所有window对象上的属性和方法,都可以省略window.
,直接使用,如下所示:
alert('Hello, world');
我们来看一下在开发者工具控制台中输入后的效果:
这样一个alert()函数,就是浏览器开放给我们的API,提供给我们弹出一个自定义消息框的能力。
为了巩固加深一下映像,让我们再来一个。随便打开一个网站,然后打开开发者工具,在控制台中输入window.location
或location
,我们就能得到这样一个Location对象,这就是通过调用浏览器API,来查看浏览器当前打开的网页的地址信息的例子。
更进一步,我们还能通过这个得到的Location对象,对当前页面进行刷新、跳转到别的网页等操作。你们可以自己来试试如下的代码吧:
window.location.reload();
window.location.replace('http://www.qq.com');
document对象
从上文我们已经了解了浏览器JS API的结构,所以,document对象它也是附属于window对象的一个对象,我们可以通过window.document
或document
来直接访问到它。它代表的,正是由我们的HTML代码所建立的DOM树,所有需要对我们的网页HTML元素(也称作DOM节点)进行访问或操作的行为,都会直接或间接的通过document对象来实现。
说到要操作这棵DOM树,首先不得不谈的就是如何去定位DOM节点,在这棵节点众多的树上,如何精准的找到我们想访问或操作的DOM节点,是头等重要的事情,就像打靶的话首先得找到正确的靶子才行啊!
我们知道,去寻找和识别一件事物,需要观察它们具有的特征,发现和目标事物有所匹配的线索。而对于DOM节点来说,它们也会有一些独特的特征,最简单和明显的特征,就是DOM标签的名字:
<div></div>
<h1></h1>
<p></p>
<input type="text" />
功能不同的DOM标签拥有不同的标签名,如div
, h1
, p
, input
等等,分辨它们就如分辨大象
,老虎
,老鼠
,河马
一样容易。
那如果DOM标签都是同一个,那怎么办?就如我们现在要去观察一大堆喵,它们可都是喵啊,你怎么去区别它们?没事,我们可以根据它们的大小,颜色,品种,产地等信息,来做详细的区分。
<div class="cat big red"></div>
<div class="cat big blue"></div>
<div class="cat medium red"></div>
<div class="cat medium white"></div>
<div class="cat small orange"></div>
由上面的代码我们可以看出,在我们创建这些div的时候,给他们加了些天赋属性点,虽然它们都是div,但它们已经是不同的div了,我们一眼就能看出它们之间的区别来。
心思缜密的同学看到这里就会有疑问了,如果以标签名字,或者分类信息这些特征来定位区分,也难保有同名同姓,或者长得一模一样的人或事物啊。没错,肯定会有这种情况发生!但是这个问题肯定是难不倒我们的,身份证这种东西,不就是为了避免这样的情况发生的么?DOM标签,我们也可以给它们设置一个唯一的身份证号(ID):
<div id="cat001" class="cat">...</div>
<div id="cat002" class="cat">...</div>
<div id="cat003" class="cat">...</div>
就这样,只要报出身份证号,比如cat002,三只喵里面肯定会站出那只符合身份证号码的喵来。
这套用于查找定位DOM的机制,我们称作选择器(Selector)。
好,我们现在大致了解了这样一种定位机制的原理,那下面来看一下,如何在实际编码中使用这种选择器查找定位我们需要的DOM节点。
在很久之前,浏览器开放的JS API,对这套选择器机制的支持还是不太完善的,没有形成一个统一的API接口,我们通常有一些零散的API来完成这些事情:
//通过标签名来获取所有是该标签名的DOM节点
document.getElementsByTagName('div');
//通过ID来获取一个DOM节点
document.getElementById('cat001');
有些API还是缺失的,比如通过标签的属性名和属性值,通过标签的class等方式来获取DOM节点,需要自己去手动处理。直到像Prototype,jQuery,Mootools之类的前端JS工具库的兴起,它们都自带了一套比较完整的用于执行选择器的API,由于它们太好用了,倒逼了浏览器标准,使得类似的选择器API出现在了现代的浏览器中。我们可以来看一下它们的用法:
// 1)jQuery的选择器API
$('input'); // 查找所有标签名为input的元素
$('#cat001'); // 查找id为cat001的元素,id前加#号
$('.red'); // 查找class属性中包含有red的元素,class名字前加.号
// 2)现代浏览器提供的和jQuery功能相对等价的选择器API
document.querySelectorAll('input');
document.querySelectorAll('#cat001');
document.querySelectorAll('.red');
是不是看起来比老的API清晰也更统一了?而且我相信大多数人会更喜欢jQuery的写法,清新爽洁不紧绷啊!不过浏览器提供的API毕竟是自带的原生的API,是行业标准,不需要引入第三方库也能直接使用(在你的网页不需要兼容老的浏览器的情况下),有它自身的一些优势,所以学习和熟悉它们也是很有必要的。
另外,选择器的语法也不止上面提及的这3种,还有一些如级联、伪类等,这些都留给你们自己去进一步学习了解了。
下面我们来几个例子,看一下怎么选择器API的实际运用。仍然拿我们最前面的HTML页面入手,我们要给三个橙色的div,加上一个白色的边框。在开发者工具栏中,直接输入以下代码:
// 通过选择器API,利用class都为container-2的特征,获取到三个橙色的div
var list = document.querySelectorAll('.container-2');
// 循环,为其中的每一个div设置边框样式
for (var i = 0; i < list.length; i++) {
var el = list[i];
el.style.border = '2px solid #FFFFFF';
}
我们可以看到,选择器精确的获取到了三个class名为container-2的DOM节点,并为其设置了边框样式。
接着,再试试这段代码,目标是要删除第二个橙色的div:
var list = document.querySelectorAll('.container-2');
list[1].remove();
我们可以立即观察到,那个内容为"Section B"的橙色div已经从DOM树中消失了。
通过上面这一系列小小的例子,你是不是已经对怎么来操作DOM有了一个大概的了解了?基本的原则,归结起来其实很简单:
- 第一步:先找到目标DOM
- 第二步:通过调用DOM提供的函数或设置DOM的属性
复杂的事情,其实都是由一些最简单的步骤组合堆砌起来的。
其他
浏览器提供的丰富的API中,有可以用来和服务器端进行通信和数据交互的,有用来处理视频音频的播放的,有用来进行绘图的,也有用来取得地理位置经纬度信息的......通过调用API,我们就能达到操控浏览器,完成我们希望它为我们完成的工作。
用最通俗的语言,讲述最技术的故事。
欢迎关注一斤代码的系列课程《从编程小白到全栈开发》