文档对象模型
DOM(Document Object Model),文档对象模型。简单的说就是一套操作文档内容的方法。
文档:D
如果没有document(文档),DOM就无从谈起。当创建了一个网页并把它加载到web浏览器中,DOM就悄然而生,它将根据你编写的网页文档创建一个文档对象。
对象:O
javascript中的对象分为三种:
用户自定义对象
(用户自己创建的对象);
内建对象
(内建在js语言的对象,如Array,Math,Date);
宿主对象
(由浏览器提供的对象,最基础的就是window对象);
Window对象对应浏览器窗口的本身,这个对象的属性和方法通常称为
BOM
(浏览器对象模型)。需要了解的是
浏览器窗口的内部
而不是浏览器窗口本身,特别是如何对网页的内容进行处理,而用来实现这一目标的载体就是
document对象
。
模型:M
操作文档对象的方法。
当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model)
。HTML DOM 模型被构造为对象的树:
选取文档元素
1⃣️ 通过ID选取元素
let section1 = document.getElementById("section1");
2⃣️ 通过名字选取元素
let sections = document.getElementByName("section");
和id不一样的是name属性值是在少数HTML元素中有效,包括表单、 表单元素、<iframe>和<img>元素。
3⃣️ 通过标签名选取元素
/*查找文档的第一个<p>元素里所有<span>元素*/
let firstpara = document.getElementsByTagName("p")[0];
let firstParaSpan = firstpara.getElementsByTagName("span")
4⃣️ 通过CSS类选取元素
getElementByClassName()只需要传入一个字符串参数,字符串参数可以使用空格隔开的标识符。只有当元素的class属性完全包含标识符的元素才会被匹配,但是标识符的顺序无关紧要。
// 查找以log命名且包含有"error"和"fatal"类的元素的所有后代
let log = document.getElementById("log");
let total = log.getElementsByClassName("error fatal");
5⃣️ 通过CSS选取器选取元素
在 CSS 中,选择器是一种模式,用于选择需要添加样式的元素。下表展示了CSS相关选择器,其中"CSS" 列指示该属性是在哪个 CSS 版本中定义的(CSS1、CSS2 还是CSS3):
选择器 | 例子 | 例子描述 | CSS版本 |
---|---|---|---|
.class | .intro | 选择 class="intro" 的所有元素 | 1 |
#id | #firstname | 选择 id="firstname" 的所有元素 | 1 |
* | * | 选择所有元素 | 2 |
element | p | 选择所有 <p> 元素。 | 1 |
element,element | div,p | 选择所有 <div> 元素和所有 <p> 元 素 | 1 |
element element | div p | 选择 <div> 元素内部的所有 <p> 元 素 | 1 |
element>element | div>p | 选择父元素为 <div> 元素的所有 <p> 元素 | 2 |
element+element | div+p | 选择紧接在 <div> 元素之后的所有 <p> 元素 | 2 |
[attribute] | [target] | 选择带有 target 属性所有元素 | 2 |
[attribute=value] | [target=_blank] | 选择 target="_blank" 的所有元素 | 2 |
[attribute~=value] | [title~=flower] | 选择 title 属性包含单词 "flower" 的所有 元素 | 2 |
[attribute|=value] | [lang|=en] | 选择 lang 属性值以 "en" 开头的所有元 素。 | 2 |
:link | a:link | 选择所有未被访问的链接 | 1 |
:visited | a:visited | 选择所有已被访问的链接 | 1 |
:active | a:active | 选择活动链接 | 1 |
:hover | a:hover | 选择鼠标指针位于其上的链接 | 1 |
:focus | input:focus | 选择获得焦点的 input 元素 | 2 |
:first-letter | p:first-letter | 选择每个 <p> 元素的首字母 | 1 |
:first-line | p:first-line | 选择每个 <p> 元素的首行 | 1 |
:first-child | p:first-child | 选择属于父元素的第一个子元素的每个 <p> 元素 | 2 |
:before | p:before | 在每个 <p> 元素的内容之前插入内容 | 2 |
:after | p:after | 在每个 <p> 元素的内容之后插入内容 | 2 |
:lang(language) | p:lang(it) | 选择带有以 "it" 开头的 lang 属性值的每 个 <p> 元素 | 2 |
element1~element2 | p~ul | 选择前面有 <p> 元素的每个 <ul> 元 素 | 3 |
[attribute^=value] | a[src^="https"] | 选择其 src 属性值以 "https" 开头的每个 <a> 元素。 | 3 |
[attribute$=value] | a[src$=".pdf"] | 选择其 src 属性以 ".pdf" 结尾的所有 <a> 元素 | 3 |
[attribute*=value] | a[src*="abc"] | 选择其 src 属性中包含 "abc" 子串的每个 <a> 元素 | 3 |
:first-of-type | p:first-of-type | 选择属于其父元素的首个 <p> 元素的每 个 <p> 元素 | 3 |
:last-of-type | p:last-of-type | 选择属于其父元素的最后 <p> 元素的每 个 <p> 元素 | 3 |
:only-of-type | p:only-of-type | 选择属于其父元素唯一的 <p> 元素的每 个 <p> 元素 | 3 |
:only-child | p:only-child | 选择属于其父元素的唯一子元素的每个 <p> 元素 | 3 |
:nth-child(n) | p:nth-child(2) | 选择属于其父元素的第二个子元素的每个 <p> 元素 | 3 |
:nth-last-child(n) | p:nth-last- child(2) | 同上,从最后一个子元素开始计数 | 3 |
:nth-of-type(n) | p:nth-of- type(2) | 选择属于其父元素第二个 <p> 元素的每 个 <p> 元素 | 3 |
:nth-last-of- type(n) | p:nth-last-of- type(2) | 同上,但是从最后一个子元素开始计数 | 3 |
:last-child | p:last-child | 选择属于其父元素最后一个子元素每个 <p> 元素 | 3 |
:root | :root | 选择文档的根元素 | 3 |
:empty | p:empty | 选择没有子元素的每个 <p> 元素(包括 文本节点) | 3 |
:target | #news:target | 选择当前活动的 #news 元素 | 3 |
:enabled | input:enabled | 选择每个启用的 <input> 元素 | 3 |
:disabled | input:disabled | 选择每个禁用的 <input> 元素 | 3 |
:checked | input:checked | 选择每个被选中的 <input> 元素 | 3 |
:not(selector) | :not(p) | 选择非 <p> 元素的每个元素 | 3 |
::selection | ::selection | 选择被用户选取的元素部分 | 3 |
文档结构和遍历
1⃣️ 作为节点树的文档
document对象,
element
对象和
文档中表示文本的text
对象都是node对象
,并定义了一下属性:
node属性 | 注释 |
---|---|
parentNode | 该节点父节点(或者针对类似Document对象的应该是null,因为它没有父节点) |
childNodes | 只读数组的对象(NodeList对象),它是该节点的实时表示 |
firstChild、 lastChild | 该节点子节点的第一个和最后一个,如果该节点没有字节点则为 null |
nextSibling,previousSibling | 该节点的兄弟节点的前一个和下一个。具有相同父节点的两个节点为兄弟节点。节点的顺序反映了它们在文档中出现的顺序。这两个属性将节点之间以双向链表的形式连接起来。 |
nodeType | 该节点的类型,9代表Document节点,1代表Element节点,3 代表Text节点,8代表Comment节点,11代表 DocumentFragment节点 |
nodeValue | Text节点或Comment(注释)节点的文本内容 |
nodeName | 元素的标签名,以大写形式表示 |
/*使用这些Node属性,可以用下面类似的表达式得到文档的第一个节点下面的第二个子节点的引用*/
document.childNodes[0].childNodes[1];
document.firstChild.firstChild.nextSibling;
2⃣️ 作为元素树的文档
element属性 | 注释 |
---|---|
firstElementChild,lastElementChild | 类似firstChild和lastChild,但只代 表Element |
nextElementSibling,previousElementSibing | 类似nextSibing和 previousSibling,但只代表兄弟 Element |
childElementCount | 子元素的数量。返回的值和 chilren.length值相等 |
注意:
节点和元素不一样
,<p>this is a text</p>中,‘this is a text’是一个text节点,但不是element元素。
属性
1⃣️ HTML属性作为Element的属性
HTML属性名不区分大小写,但是JavaScript属性名则大小写敏感
,从HTML属性名转换到JavaScript属性名应该采用小写,但是,如果属性名包含不止一个单词,则将第一个单词以外的单词的首字母大写
有些HTML属性名在JavaScript中是保留字,至于这些属性,一般的规则是为属性名加上前缀html
,for–>htmlFor; 不过有个例外:class–>className;
let img = document.getElementById("myimage");
let imgsrc = img.src;
img.id === "myimage" //进行判断
2⃣️ 获取和设置非标准的HTML属性
Element类型同样定义了
getAttribute()
和
setAttribute()
方法可以查询和设置这些非标准的HTML属性,当然也可以用来查询和设置XML文档中元素上的属性。
let image = document.images[0];
let width = parseInt(image.getAttribute("width"));
image.setAttribute("class","firstImage");
可以看出,基于属性的API与这种方法有两个重要的区别:
- 属性值被当作是字符串,其返回值是字符串,而不是数值\布尔值对象。
- 方法使用标准属性名,对于HTML来说,属性名不区分大小写。
Element类型还设置了两个相关的方法。
hasAttribute()和removeAttribute()
,他们用来检测是否存在属性和完全删除属性。
3⃣️ 数据集属性
HTML5提供了一个解决方法,
任意以小写data-作为前缀的属性名字都是合法的
。这些“数据集属性”不会影响元素的表现,它们定义了标准的,附加额外数据的方法,并不是在文档合法性上做出让步。
HTML5还在Element对象上定义了dataset属性
。该属性指代一个对象,它的每一个属性相对于去掉前缀的data-属性。也就是说
dataset.x保存的是data-x属性的值
。
4⃣️ 作为Attr节点的属性
attributes属性是只读的类数组对象,它代表元素的所有属性
。
具有实时性
,
可以用数组索引访问,也可以用属性名索引访问
(建议使用属性名)。
document.body.attributes[0]; //<body>元素的第一个属性
document.body.attributes.bgColor //<body>元素的bgColor属性
document.body.attributes["ONLOAD"] //<body>元素的onload属性
元素的内容
1⃣️ 作为HTML的元素内容
使用元素对象的innerHTML
属性作为字符串标记返回其内容:
my_p.innerHTML; //返回 I'm <strong>BIG</strong> panda!!!
html5还标准化了一个outerHTML
属性,它将返回包括元素自身标记的字符串内容:
my_p.outerHTML; //返回 <p id="my_p">I'm <strong>BIG</strong> panda!!!</p>
另一个在html5标准化的方法是insertAdjacentHTML()
,它将任意html标记字符串插入到指定元素的“相邻”位置;
该方法有2个参数,第一个表示插入元素的相对位置,第二个参数就是要插入的标记字符串
。
第一个参数可以具有以下几个值: “beforebegin”,”afterbegin”,”beforeend”和”afterend”,其代表的插入位置如下图:
2⃣️ 作为纯文本的元素内容
使用元素对象的textContent
属性来实现:
my_p.textContent; //返回 "I'm BIG panda!!!"
3⃣️ 作为Text节点的元素内容
使用nodeValue
属性。
创建、插入、删除节点
1⃣️ 创建节点
创建新的Element节点可以使用Document对象的
createElement()
方法。给方法传递元素的标签名:
对HTML文档来说名字不容易区分大小写,对XML文档则区分大小写
。
2⃣️ 插入节点
appendChild(),插入指定节点使其成为那个节点的最后一个子节点,
insertBefore(),
接受两个参数,第一个是待插入的节点,第二个参数是已经存在的节点,新节点将插入该节点的前边
。该方法应该是在新节点的父节点上调用,第二个参数必须是该父节点的子节点。如果传递null作为第二个参数,insertBefore()的行为类似appendChild(),它将节点插入在最后。
如果重复插入同一节点,则会删除之前插入的节点并重新插入
。
//根据指定表格每行第n个单元格的值,对第一个<tbody>中的进行排序
//如果存在comparator函数则使用它,否则按字母表顺序比较
function sortrows(table, n, comparator) {
let tbody = table.tBodies[0]; //第一个<tbody>,可能是隐式窗口的
let rows = tbody.getElementsByTagName("tr"); //tbody中所有行
rows = Array.prototype.slice.call(rows, 0); //真实的数组
//基于第n个<td>元素的值对行排序
rows.sort(function(row1, row2) {
let cell1 = row1.getElementsByTagName("td")[n]; //获得第n个单元格
let cell2 = row2.getElementsByTagName("td")[n]; //两行都是
let val1 = cell1.textContent || cell1.innerText; //获得文本内容
let val2 = cell2.textContent || cell2.innerText; //同上,两单格都是
if (comparator) return comparator(val1, val2); // 进行比较
if (val1 < val2) return -1;
else if (val1 > val2) return 1;
else return 0;
});
//在tobody中按他们的顺序把行添加到最后
//这将自动把它们从当前位置移走,故没必要预先删除它们
//如果<tbody>还包含除了<tr>的任何其他元素,这些节点都将会悬浮到顶部位置
for (let i = 0; i < rows.length; i++) tbody.appendChild(rows[i]);
}
//查找表格的<th>元素,假设只有一行,它们可以单击
//以便单击列标题,按列对行排序。
function makeSortable(table) {
let headers = table.getElementsByTagName("th");
for (let i = 0; i < headers.length; i++) {
(function(n) { //嵌套函数来创建本地域
headers[i].onclick = function() {
sortrows(table, n);};
}(i)); //将i的值赋给局部变量n
}
}
3⃣️ 删除和替换节点
removeChild()
就是
从文档树中删除一个节点
,但是请小心:该方法不是在待删除的节点上调用,而是(就像其名字的一部分“child”所暗示的一样)在其父节点上调用。在父节点上调用该方法,并将需要删除子节点作为方法参数传递给它。
replaceChild()方法删除一个子节点并用一个新的节点取而代之
。在父节点上调用该方法,第一个参数是新节点,第二个参数是要替代的节点。
//用新的元素<b>替换n节点,并将n作为该节点的子节点
function embolden(n){
if(typeof n == "string") n = document.getElementById("n");
let parent = n.parentNode;
let b = document.createElement("b");
parent.replaceChild(b,n);
b.appendChild(n);
}
4⃣️ 使用DocumentFragment
DocumentFragment是一种特殊的node
,作为其他节点的一个临时容器,类似于Document节点,但是DocumentFragment是独立的,并不是任何文档的一部分,它的parentNode总是null,类似于Element,可以有任意数量的子节点,
可以使用appendChild()、inserBrfore()或者replaceChild()传递一个DocumentFragment
。
文档和元素的几何形状和滚动
1⃣️ 文档坐标和视口坐标
向右为+x,向下为+y,有两个不同的原点坐标:相对于文档的左上角或者相对于在其中的显示文档的视口的左上角。如果文档比视口要小,或者说它没有出现滚动条,这个时候文档的左上角就是视口的左上角,视为同一个。一般来说,两个原点的相互转化必须加上滚动条的偏移量。
2⃣️ 查询元素的几何尺寸
判定一个元素尺寸和位置最简单的方法是调用它的
getBoundingClientRect()方法
,它不需要参数,返回一个有left,right,top,bottom的属性对象。
left和top表示左上角的x和y坐标。right和 bottom属性表示元素右下角的x和y坐标。
getBoundingClientRect()对象还包含
width
和
height
属性,但在原始的ie中未实现。还返回元素的边框和内边距,但是不包含元素的外边距。
//计算width和height属性
let e = document.getElementById("my_id");
let box = e.getBoundingClientRect(); //获得视口在坐标中的位置
let w = box.width || (box.right - box.left);
let h = box.height || (box.bottom - box.top);
3⃣️ 滚动scrollTo() scrollBy() scroll()
scrollTo()与scroll()
等价,包含两个参数,
第一个表示x轴的偏移量,第二个表示y轴的偏移量
。让窗口滚动到指定的点,使其出现在视口。相对与视口原点的偏移
scrollBy()
参数与上述相同,不同的是偏移量表示的是在当前滚动条的偏移量基础上的增加。
HTML表单
表单是一个包含表单元素的区域。
表单元素
是允许用户在表单中(比如:文
本域、下拉列表、单选框、复选框等等)输入信息的元素。表单使用
表单标签
(
<form>
)定义。form元素可以使用下表所示的属性来进行设置:
属性 | 值 | 描述 |
---|---|---|
action | URL | 规定当提交表单时向何处发送表单数据 |
enctype | 规定在发送表单数据之前如何对其进行编码。 | |
method | get post | 规定用于发送 form-data 的 HTTP 方法 |
name | form_name | 规定表单的名称 |
target | _blank _self _parent _top framename | 规定在何处打开 action URL |
多数情况下被用到的表单标签是输入标签(<input>), 输入类型是由类型属性(type)定义的,大多数经常被用到的输入类型如下:
HTML元素 | 类型属性 | 事件处理程序 | 描述和事件 |
---|---|---|---|
<input type="button">或 <button type="button"> | “button” | onclick | 按钮 |
<input type="checkbox"> | "checkbox" | onchange | 复选按钮 |
<input type="file"> | "file" | onchange | 载入web服务器文件的文件名输入域;它的value属性是只读的 |
<input type="hidden"> | "hidden" | none | 数据由表单提交,但是对用户 不可见 |
<option> | none | none | select对象的单个选项,事件处理发生在select上面 |
<input type="password"> | "password" | onchange | 密码输入框,输入的字符不可见 |
<input type="radio"> | "radio" | onchange | 单选按钮 |
<input type="reset">或 <button type="reset"> | "reset" | onclick | 重置表单按钮 |
<select> | “select-one” | onchange | 选项只能单选的列表或者下拉菜单 |
<select multiple> | “select- multiple” | onchange | 可以多选的下拉菜单 |
<input type="submit">或 <button type="submit"> | “submit” | onclick | 表单提交按钮 |
<input type="text"> | “text” | onchange | 单行文本输入域,type属性缺少或者无法识别时默认的input 元素 |
<textarea> | “textarea” | onchange | 多行文本输入域 |
HTML表单标签
标签 | 描述 |
---|---|
<form> | 定义供用户输入的表单 |
<input> | 定义输入域 |
<textarea> | 定义文本域 (一个多行的输入控件) |
<label> | 定义了 <input> 元素的标签,一般为输入标题 |
<fieldset> | 定义了一组相关的表单元素,并使用外框包含起来 |
<legend> | 定义了 <fieldset> 元素的标题 |
<select> | 定义了下拉选项列表 |
<optgroup> | 定义选项组 |
<option> | 定义下拉列表中的选项 |
<button> | 定义一个点击按钮 |
<datalist> | 指定一个预先定义的输入控件选项列表 |
<keygen> | 定义了表单的密钥对生成器字段 |
<output> | 定义一个计算结果 |
选取表单和表单元素
getElementById()、getElementsByTagName()、querySelectorAll()
let fields = document.getElementById("addr").getElementsByTagName("input");
/*id为shipping的表单中的所有name为methed的单选按钮*/
document.querySelectorAll('#shipping input[type="radio"] [name="methed"]');