键盘敲烂,月薪过万作业不做,等于没学
当前系列: Javascript入门 修改讲义

实际开发中,JavaScript不是在F12控制台中玩的,而是要操作HTML和CSS复习。HTML和CSS又被抽象为:

DOM

Document Object Model,文档对象模型,是W3C制定的用于访问结构化(structured)文档的标准。包含:

  • 核心DOM
  • XML DOM
  • HTML DOM

换言之,HTML DOM 是关于如何获取、修改、添加或删除 HTML 元素的标准。(还是增删改查,^_^)

节点和树

在 HTML DOM 中,所有事物都是节点(node),所有的节点共同构成一颗节点“tree,复习

  • 一个HTML页面就是一个文档(document),文档也是一个节点:根节点,下面又有:
  • 元素(element)节点:对应每个HTML元素
  • 文本(text)节点:HTML 元素内的文本是(注意:元素是元素,文本是文本)
  • 属性(attribute)节点:HTML元素中的每个属性
  • 甚至注释(comment)都是节点……

以如下HTML代码:

<!DOCTYPE html>
<html>
<head>
    <title>源栈欢迎您!</title>
    <style>
        .red {
            color: red;
        }
    </style>
</head>
<body>
    <h1>源栈欢迎您!</h1>
    <div>
        <p>HTML DOM结构示范</p>
        <p id='mole'>
            先学习,后付费。见:
            <a name="yz" href="https://17bang.ren" target="_blank">一起帮·源栈课堂</a>
            。
        </p>
        <p class="red">
            但我们认为,将其图示为
            <strong style="color:darkblue">嵌套结构</strong>
            更容易理解。
        </p>
        <img name="yz" src="/images/源栈小九-small.png" alt="温柔大方的小九姐姐" />
        <quote></quote>
    </div>
    <div>
        <a href="#">QQ:2282636022</a>
    </div>
</body>
</html>


实际上就是这样一个“嵌套”结构:

#体会:所有节点组成一颗树!

树中的所有节点均可通过 JavaScript 进行访问、修改、创建或删除:这就是JavaScript的基本功能之一!


DOM树操作

获取元素

一般从document开始,以getElement开头,包括:

  • getElementById(id):根据id查找
    document.getElementById('mole')
    注意:JavaScript中也可以(不推荐/不规范地)直接通过点Id拿到该元素,比如:
    window.mole
    这会造成一些费解的问题,详见后文.submit()不是方法
  • getElementsByTagName():根据标签名查找。
    document.getElementsByTagName('p')
    同样的,一些浏览器也支持点出一些特定的标签,比如form
    document.forms  //注意复数形式
  • getElementsByClassName()
  • getElementsByName()

注意

  • getElementById是唯一一个element单数的方法,后面的都是复数elements,返回的也不再是一个DOM元素,而是元素集合
  • 元素集合结果可以像数组一样用方括号([])获取到元素中某一个子元素

复杂查找

在已获取的元素上可以进一步的使用getElementXXX(),比如:

//不要忘了[0]
document.getElementsByTagName('div')[0].getElementsByTagName('a')

但通常我们利用一些内置的(build-in)方法/属性,通过父/子/兄弟……关系,找到:

  • 所有子元素/节点:children / childNodes
  • 第一个子元素/节点:firstElementChild / firstChild
  • 最后一个子元素/节点: lastElementChild / lastChild
  • 父元素/节点:parentElement/Node
  • 下/上一个兄弟元素/节点:next/previous(Element)Sibling

注意区分:node(节点)和element(元素),节点包含text/comment等类型……

PS:在ES6中还借鉴JQuery选择器(后文详述),引入了querySelector()

修改

在找到DOM元素后,对DOM元素进行操作,获取/修改:

内容

演示区别:

  • innerHTML:标签中包裹的所有内容,含换行、缩进、空白,以及HTML子元素
  • innerText:仅仅是标签中包裹的文本

注意

  1. 如果是getElementsByXXX,返回的是集合,需要先取得其中的某一个元素之后才能进行上述操作
    document.getElementsByTagName('p')[0].innerHTML = "<a href='https://17bang.ren/'>一起帮·源栈课堂</a>" 
  2. DOM元素的属性一般可以直接赋值进行修改

属性

可以使用方法:get/setAttribute():
document.getElementsByTagName('img')[0].getAttribute("src") 
document.getElementsByTagName('img')[0].setAttribute("src", "/images/源栈小九.png" ) 

也可以直接点(.)出:

document.getElementsByTagName('img')[0].src 

但是,这两者的结果有所不同(演示)。我们可以理解为:

  • get/setAttribute()是“字符串”操作,从HTML元素文本中截取
  • .出属性是“对象”操作,从“对象化”后的DOM对象中获取其属性

延伸阅读:JQuery的attr()和prop()

此外,.出的属性还可以再进一步的.出它的子属性,比如:

document.getElementsByTagName('strong')[0].style.fontSize = "50px" 

注意JavaScript中变量/字段/属性等命名不能包含中划线(-),所以CSS中的font-size变成了fontSize。

添加元素

一个固定的套路:

  1. 创建一个指定类型的元素:createElement(elmentType)
  2. 设置它的属性(可选):setAttribute(attributeName)
  3. 设置createTextNode(text)
  4. 将该元素放置到DOM树,可以是:
    • parent.appendChild(node):把node插入到parent元素的最后一个子元素的后面
    • parent.insertBefore(node, beforeWhich):把node插入到beforeWhich元素的前面(#体会:糟糕的方法定义)
let quote = document.createElement('quote');    //<quote></quote>
quote.setAttribute('id', 'lucky-stack');        //<quote id='lucky-stack'></quote>
quote.style.cssFloat = 'right';                 //<quote id='lucky-stack' style='float:right'></quote>
let text = document.createTextNode('一起帮·源栈');
quote.appendChild(text);                        //<quote id='lucky-stack' style='float:right'>一起帮·源栈</quote>
document.getElementsByTagName('div')[0].appendChild(quote);

或者可以用它替换(replace)其他DOM元素

先将div抽取出来:

let div = document.getElementsByTagName('div')[0]; 
演示:

  • replaceWith(string|node)
    div.children[1].replaceWith(quote);
  • replaceChild(newNode,oldNode)
    div.replaceChild(quote, div.children[1] );

删除

remove引导的两个常用方法:

  • remove():
    div.children[1].remove()
  • removeChild(node):#体会#这种糟糕的设计
    div.removeChild(div.children[1])

注意:实际开发中,删除(再也没了,不能再通过document.出)和隐藏是不一样的,不要混淆:

  • 修改CSS样式
    div.style.display="none";
    div.style.visibility = "hidden"; 
  • 添加hide属性
    div.hidden = true 

属性还可以使用removeAttribute()删除,也可以说是元素的“改”了……


表单

不认识下面内容的同学自行复习

<form>
    <label>用户名:<input type="text" name="username" /></label><br />
    <label>密码:<input type="password" name="password" /></label><br />
    <label>确认密码:<input type="password" /></label><br />
    <div>
        <label>学费支付:</label>
        <label><input type="radio" value="order" name="fee"/>先付费</label>
        <label><input type="radio" value="payoff" name="fee"/>后付费</label>
    </div>
    <div>
        <label>学习课程:</label>
        <select name="courses">
            <option value="front">前端</option>
            <option value="backend">后端</option>
            <option value="database">数据库</option>
        </select>
    </div>
    <div>
        <label>学习计划:</label><br />
        <textarea name="study-plan"></textarea>
    </div>
    <input type="submit" value="提交" />
    <input type="reset" value="重置" />
    <label><input type="checkbox" id="remember"/>记住我</label>
</form>

value

使用value属性:

  • 文本类:text / password  / hidden / textarea,可获取/设置用户输入
  • checkbox / radio:只获取该元素本来设置的value值
  • select:能获取用户选择项的值(value而不是文本text)

checked & selected & selectedIndex

checkbox / radio:可以通过其checked值判断它是否被用户选中。演示

document.getElementsByTagName("input")[3].checked 
理解:获取label中的checkbox,O(∩_∩)O哈哈~
document.getElementsByTagName("form")[0].lastElementChild.getElementsByTagName("input")[0].checked 

但对于select而言,就需要使用option的selected属性,判断其是否被选中。

有时候我们需要获取的是选中项的文本(不是value): 

{
    let options = document.getElementsByTagName("select")[0].children;
    for (let i = 0; i < options.length; i++) {
        if (options[i].selected) {
            console.log(options[i].innerHTML);
        }
    }
}

观察课堂演示@想一想@:最外层{}的作用是什么?

还有一种更简单的方法,利用select的selectedIndex获取被选中项的下标/索引,然后利用children[selectedIndex]:

document.getElementsByTagName("select")[0].selectedIndex;

disabled & readOnly

都是禁用。但复习:两者的区别

演示:获取和设置

注意:readOnly的O是大写。

文件

<input type="file" id="icon" multiple />
注意不能直接使用value,否则得到的只能是文件名(路径)

要拿到文件,需要使用files属性:(详见:更多文件内容操作

document.getElementById("icon").files 


性能损耗

利用JavaScript完成DOM操作,实际上分成两个部分:

  1. JavaScript引擎(如webkit)执行,比如:for循环、i++、JavaScript原生函数调用等
  2. DOM渲染,比如:document.getElementsByXXX()、XXX.disabled=true等,

这两者是独立的:所以两者之间的交互天然是有消耗的。

注意:两个独立“模块”之间的消耗是最大的,常常是性能瓶颈。同理的还有数据库连接、HTTP连接……

应尽量避免:

  • 反复的获取同一个DOM元素,而应该将其存储在一个JavaScript变量中,
  • 一边JavaScript运算,一边DOM渲染尤其是在循环中),而应该想办法一次性获取DOM元素对象,在JavaScript中运算完毕之后,再一次性修改DOM


作业

本章作业名为:dom.html

  1. 模拟求助首页(可以适当更改html标签名字或添加class等),并通过在控制台输入代码:
    1. 将状态为“协助中”的求助背景改成灰黑色
    2. 统计有多少个悬赏大于10的求助
    3. 如果总结数为0,将其从DOM树中删除
    4. 写一个函数getKeywordsCount(),可以统计某个求助使用了多少关键字
  2. 参考用户资料页面,控制台显示出用户的:性别 / 出生年月 / 关注(关键字)/ 自我介绍
学习笔记
源栈学历
今天学习不努力,明天努力找工作

作业

觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 Javascript入门 中继续学习:

多快好省!前端后端,线上线下,名师精讲

  • 先学习,后付费;
  • 不满意,不要钱。
  • 编程培训班,我就选源栈

更多了解 加:

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧

公众号:源栈一起帮

二维码