1-JS-浏览器-Document

Document
浏览器环境,规格

JavaScript 语言最初是为 Web 浏览器创建的。此后,它已经发展成为一种具有多种用途和平台的语言。

平台可以是一个浏览器,一个 Web 服务器,或其他 主机(host),甚至可以是一个“智能”咖啡机,如果它能运行 JavaScript 的话。它们每个都提供了特定于平台的功能。JavaScript 规范将其称为 主机环境

主机环境提供了自己的对象和语言核心以外的函数。Web 浏览器提供了一种控制网页的方法。Node.JS 提供了服务器端功能,等等。

有一个叫做 window 的“根”对象。它有两个角色:

  1. 首先,它是 JavaScript 代码的全局对象,如 全局对象 一章所述。
  2. 其次,它代表“浏览器窗口”,并提供了控制它的方法。

举例:用作全局对象的方法

function sayHi() {
  alert("Hello");
}

// 全局函数是全局对象的方法:
window.sayHi();

// 可以作为浏览器窗口来查看窗口高度
alert(window.innnerHeight);
文档对象模型(DOM)

文档对象模型(Document Object Model),简称 DOM,将所有页面内容表示为可以修改的对象。

document 对象是页面的主要“入口点”。我们可以使用它来更改或创建页面上的任何内容。

比如:

// 将背景颜色改成红色
document.body.style.background = "red";

// 在 1 秒后将其修改回来
setTimeout(() => document.body.style.background="",1000);

注意:DOM 不仅仅用于浏览器,DOM 规范解释了文档的结构,并提供了操作文档的对象。

浏览器对象模型(BOM)

浏览器对象模型(Browser Object Model),简称 BOM,表示由浏览器(主机环境)提供的用于处理文档(document)之外的所有内容的其他对象。

  • navigator 对象提供了有关浏览器和操作系统的背景信息。navigator 有许多属性,但是最广为人知的两个属性是:navigator.userAgent — 关于当前浏览器,navigator.platform — 关于平台(可以帮助区分 Windows/Linux/Mac 等)。
  • location 对象允许我们读取当前 URL,并且可以将浏览器重定向到新的 URL。

如何使用 location 对象方法呢:

alert(location.href); // 显示当前 URL
if (confirm("Go to Wikipedia?")) {
  location.href = "https://wikipedia.org"; // 将浏览器重定向到另一个 URL
}
DOM 树

HTML 文档的主干是标签(tag)。

根据文档对象模型(DOM),每个 HTML 标签都是一个对象。嵌套的标签是闭合标签的“子标签(children)”。标签内的文本也是一个对象。

所有这些对象都可以通过 JavaScript 来访问,我们可以使用它们来修改页面。

例如,document.body 是表示 `` 标签的对象。

运行这段代码会使 `` 保持 3 秒红色状态:

document.body.background = 'red'; // 将背景设为红色
setTimeout(() => document.body.style.background = '',3000);

在这,我们使用了 style.background 来修改 document.body 的背景颜色,但是还有很多其他的属性,例如:

  • innerHTML — 节点的 HTML 内容。
  • offsetWidth — 节点宽度(以像素度量)
  • ……等。
遍历 DOM

DOM 让我们可以对元素和它们中的内容做任何事,但是首先我们需要获取到对应的 DOM 对象。

对 DOM 的所有操作都是以 document 对象开始。它是 DOM 的主“入口点”。从它我们可以访问任何节点。

这里是一张描述对象间链接的图片,通过这些链接我们可以在 DOM 节点之间移动。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LHAd69Cl-1614672125972)(/home/gao/.config/Typora/typora-user-images/image-20210224114853630.png)]

在最顶层:documentElement 和 body
<html> = document.documentElement

// 最顶层的 document 节点是 document.documentElement。这是对应 <html> 标签的 DOM 节点。

<body> = document.body
// 另一个被广泛使用的 DOM 节点是 <body> 元素 — document.body。


<head> = document.head
// <head> 标签可以通过 document.head 访问。

注意: document.body 的值可能是 null

脚本无法访问在运行时不存在的元素。

尤其是,如果一个脚本是在 head 中,那么脚本是访问不到 document.body 元素的,因为浏览器还没有读到它。

<html>

<head>
  <script>
    alert( "From HEAD: " + document.body ); // null,这里目前还没有 <body>
  </script>
</head>

<body>

  <script>
    alert( "From BODY: " + document.body ); // HTMLBodyElement,现在存在了
  </script>

</body>
</html>
子节点:childNodes,firstChild,lastChild

子节点:对应的是直系的子元素。换句话说,它们被完全嵌套在给定的元素中。例如,head, body 就是 html 元素的子元素。

子孙元素:嵌套在给定元素中的所有元素,包括子元素,以及子元素的子元素等。

<html>
<body>
  <div>Begin</div>

  <ul>
    <li>
      <b>Information</b>
    </li>
  </ul>
</body>
</html>

firstChildlastChild 属性是访问第一个和最后一个子元素的快捷方式。

elem.childNodes[0] === elem.firstChild
elem.childNodes[elem.childNodes.length - 1] === elem.lastChild
DOM 集合

childNodes 看起来像一个数组,实际上它并不是一个数组,而是一个集合,一个类数组的可迭代对象。

导致两个结果:

  1. 使用 for…of 来迭代它。
for (let node of document.body.childNodes) {
    alert(node); // 显示集合中的所有节点
}
  1. 无法使用数组的方法,因为不是一个数组
alert(document.body.childNodes.filter); //undefined

集合的性质所得到的第一个结果是不错的。第二个结果也还可以忍受。我们可以使用 Array.from 方法来从集合创建一个“真”数组:

alert(Array.from(document.body.childNodes).filter); // function

注意:**DOM 集合是只读的。**DOM 集合,我们不能通过类似 childNodes[i] = … 的操作来替换一个子节点。修改子节点需要其它方法。

DOM 集合是实时的。

不要用 for…in 来遍历集合。

for..in 循环遍历的是所有可枚举的(enumerable)属性。集合还有一些“额外的”很少被用到的属性,通常这些属性也是我们不期望得到的:

<body>
<script>
  // 显示 0,1,length,item,values 及其他。
  for (let prop in document.body.childNodes) alert(prop);
</script>
</body>
兄弟节点和父节点

兄弟节点是指有同一个父节点的节点。

举例:

<html>
  <head>...</head><body>...</body>
</html>


// <body> 可以说是 <head> 的“下一个”或者“右边”兄弟节点。
// <head> 可以说是 <body> 的“前一个”或者“左边”兄弟节点。

下一个兄弟节点在 nextSibling 属性中,上一个是在 previousSibling 属性中。

可以通过 parentNode 来访问父节点。

/ <body> 的父节点是 <html>
alert( document.body.parentNode === document.documentElement ); // true

// <head> 的后一个是 <body>
alert( document.head.nextSibling ); // HTMLBodyElement

// <body> 的前一个是 <head>
alert( document.body.previousSibling ); // HTMLHeadElement
纯元素导航

例如,在 childNodes 中我们可以看到文本节点,元素节点,甚至如果注释节点存在的话,也能访问到。

但是对于很多任务来说,我们并不想要文本节点或注释节点。我们希望操纵的是代表标签的和形成页面结构的元素节点。

所以,让我们看看更多只考虑 元素节点 的导航链接(navigation link):

  • children — 仅那些作为元素节点的子代的节点。
  • firstElementChildlastElementChild — 第一个和最后一个子元素。
  • previousElementSiblingnextElementSibling — 兄弟元素。
  • parentElement — 父元素。
表格

方便起见,某些类型的 DOM 元素可能会提供特定于其类型的其他属性。

表格(Table)是一个很好的例子,它代表了一个特别重要的情况:

元素支持 (除了上面给出的,之外) 以下这些属性: table.rows — 元素的集合。table.caption/tHead/tFoot — 引用元素 ,。table.tBodies — 元素的集合(根据标准还有很多元素,但是这里至少会有一个 — 即使没有被写在 HTML 源文件中,浏览器也会将其放入 DOM 中)。 ,, 元素提供了 rows 属性: tbody.rows — 表格内部 元素的集合。
: tr.cells — 在给定 中的 和 单元格的集合。tr.sectionRowIndex — 给定的 在封闭的 // 中的位置(索引)。tr.rowIndex — 在整个表格中 的编号(包括表格的所有行)。 和 : td.cellIndex — 在封闭的 中单元格的编号。
<table id="table">
  <tr>
    <td>one</td><td>two</td>
  </tr>
  <tr>
    <td>three</td><td>four</td>
  </tr>
</table>

<script>
  // 获取带有 "two" 的 td(第一行,第二列)
  let td = table.rows[0].cells[1];
  td.style.backgroundColor = "red"; // highlight it
</script>
搜索 getElement*,querySelector
document.getElementById 或者只使用 id

如果一个元素有 id 特性(attribute),那我们就可以使用 document.getElementById(id) 方法获取该元素,无论它在哪里。

举例:

<div id="elem">
  <div id="elem-content">Element</div>
</div>

<script>
  // 获取该元素
  let elem = document.getElementById('elem');

  // 将该元素背景改为红色
  elem.style.background = 'red';
</script>

此外,可以通过 id 命名的全局变量,引用了元素。

<div id="elem">
  <div id="elem-content">Element</div>
</div>

<script>
  // elem 是对带有 id="elem" 的 DOM 元素的引用
  elem.style.background = 'red';

  // id="elem-content" 内有连字符,所以它不能成为一个变量
  // ...但是我们可以通过使用方括号 window['elem-content'] 来访问它
</script>

除非我们申明一个相同名称的 JS 变量,否则它拥有优先权。

<div id="elem"></div>

<script>
  let elem = 5; // 现在 elem 是 5,而不是对 <div id="elem"> 的引用

  alert(elem); // 5
</script>

注意:id 必须是惟一的。

id 必须是唯一的。在文档中,只能有一个元素带有给定的 id

如果有多个元素都带有同一个 id,那么使用它的方法的行为是不可预测的,例如 document.getElementById 可能会随机返回其中一个元素。因此,请遵守规则,保持 id 的唯一性。

querySelectorAll

到目前为止,最通用的方法是 elem.querySelectorAll(css),它返回 elem 中与给定 CSS 选择器匹配的所有元素。

举例:我们想查找所有为最后一个子元素的

  • 元素
  • <ul>
      <li>The</li>
      <li>test</li>
    </ul>
    <ul>
      <li>has</li>
      <li>passed</li>
    </ul>
    <script>
      let elements = document.querySelectorAll('ul > li:last-child');
    
      for (let elem of elements) {
        alert(elem.innerHTML); // "test", "passed"
      }
    </script>
    
    querySelector

    elem.querySelector(css) 调用会返回给定 CSS 选择器的第一个元素。

    换句话说,结果与 elem.querySelectorAll(css)[0] 相同,但是后者会查找 所有 元素,并从中选取一个,而 elem.querySelector 只会查找一个。因此它在速度上更快,并且写起来更短。

    matches

    之前的方法是搜索 DOM。

    elem.matches(css) 不会查找任何内容,它只会检查 elem 是否与给定的 CSS 选择器匹配。它返回 truefalse

    当我们遍历元素(例如数组或其他内容)并试图过滤那些我们感兴趣的元素时,这个方法会很有用。

    举例:

    <a href="http://example.com/file.zip">...</a>
    <a href="http://ya.ru">...</a>
    
    <script>
      // 不一定是 document.body.children,还可以是任何集合
      for (let elem of document.body.children) {
        if (elem.matches('a[href$="zip"]')) {
          alert("The archive reference: " + elem.href );
        }
      }
    </script>
    
    closest

    元素的祖先(ancestor)是:父级,父级的父级,它的父级等。祖先们一起组成了从元素到顶端的父级链。

    elem.closest(css) 方法会查找与 CSS 选择器匹配的最近的祖先。elem 自己也会被搜索。

    换句话说,方法 closest 在元素中得到了提升,并检查每个父级。如果它与选择器匹配,则停止搜索并返回该祖先。

    举例:

    <h1>Contents</h1>
    
    <div class="contents">
      <ul class="book">
        <li class="chapter">Chapter 1</li>
        <li class="chapter">Chapter 1</li>
      </ul>
    </div>
    
    <script>
      let chapter = document.querySelector('.chapter'); // LI
    
      alert(chapter.closest('.book')); // UL
      alert(chapter.closest('.contents')); // DIV
    
      alert(chapter.closest('h1')); // null(因为 h1 不是祖先)
    </script>
    
    getElementsBy*

    完整的是

    • elem.getElementsByTagName(tag) 查找具有给定标签的元素,并返回它们的集合。tag 参数也可以是对于“任何标签”的星号 "*"
    • elem.getElementsByClassName(className) 返回具有给定CSS类的元素。
    • document.getElementsByName(name) 返回在文档范围内具有给定 name 特性的元素。很少使用。

    举例

    // 获取文档中的所有 div
    let divs = document.getElementsByTagName('div');
    
    
    
    //查找 table 中的所有 input 标签
    <table id="table">
      <tr>
        <td>Your age:</td>
    
        <td>
          <label>
            <input type="radio" name="age" value="young" checked> less than 18
          </label>
          <label>
            <input type="radio" name="age" value="mature"> from 18 to 50
          </label>
          <label>
            <input type="radio" name="age" value="senior"> more than 60
          </label>
        </td>
      </tr>
    </table>
    
    <script>
      let inputs = table.getElementsByTagName('input');
    
      for (let input of inputs) {
        alert( input.value + ': ' + input.checked );
      }
    </script>
    

    注意:不要忘记字母 s

    如果没有加 s,只返回单个元素。有 s 返回元素的集合。

    查找 .article 元素

    <form name="my-form">
      <div class="article">Article</div>
      <div class="long article">Long article</div>
    </form>
    
    <script>
      // 按 name 特性查找
      let form = document.getElementsByName('my-form')[0];
    
      // 在 form 中按 class 查找
      let articles = form.getElementsByClassName('article');
      alert(articles.length); // 2, found two elements with class "article"
    </script>
    
    实时的集合

    所有的 "getElementsBy*" 方法都会返回一个 实时的(live) 集合。这样的集合始终反映的是文档的当前状态,并且在文档发生更改时会“自动更新”。

    在下面的例子中,有两个脚本。

    1. 第一个创建了对 div 的集合的引用。截至目前,它的长度是 1。
    2. 第二个脚本在浏览器再遇到一个 div 时运行,所以它的长度是 2。
    <div>First div</div>
    
    <script>
      let divs = document.getElementsByTagName('div');
      alert(divs.length); // 1
    </script>
    
    <div>Second div</div>
    
    <script>
      alert(divs.length); // 2
    </script>
    
    特性和属性(Attributes 和 properties)

    当浏览器加载页面时,它会“读取”(或者称之为:“解析”)HTML 并从中生成 DOM 对象。对于元素节点,大多数标准的 HTML 特性(attributes)会自动变成 DOM 对象的属性(properties)。(译注:attribute 和 property 两词意思相近,为作区分,全文将 attribute 译为“特性”,property 译为“属性”,请读者注意区分。)

    例如,如果标签是body id=“page”,那么 DOM 对象就会有 body.id=“page”。

    DOM属性

    DOM 节点是常规的 JavaScript 对象。我们可以 alert 它们。

    // 在 document.body 中创建一个新的属性
    document.body.myData = {
      name: 'Caesar',
      title: 'Imperator'
    };
    
    alert(document.body.myData.title); // Imperator
    

    也可以添加一个方法

    document.body.sayTagName = function() {
      alert(this.tagName);
    };
    
    document.body.sayTagName(); // BODY(这个方法中的 "this" 的值是 document.body)
    

    还可以修改内建属性的原型

    Element.prototype.sayHi = function() {
      alert(`Hello, I'm ${this.tagName}`);
    };
    
    document.documentElement.sayHi(); // Hello, I'm HTML
    document.body.sayHi(); // Hello, I'm BODY
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值