JavaScript高级程序设计(第三版) 第11 章 DOM 扩展 和 第12章

1### Menu

第11 章 DOM 扩展

11.1.1 querySelector()方法
11.1.2 querySelectorAll()方法
11.1.3 element.matches() element.webkitMatchesSelector()方法
11.2 元素遍历
HTML5 - 11.3.1 与类相关的扩充
HTML5 - 11.3.2 焦点管理
HTML5 - 11.3.3HTMLDocument的变化
HTML5 - 11.3.4 字符集属性
HTML5 - 11.3.5 自定义数据属性
HTML5 - 11.3.6 插入标记
    1. innerHTML 属性
    1. outerHTML 属性
    1. insertAdjacentHTML()方法
    1. 内存与性能问题
11.4 专有扩展
  • 11.4.1 文档模式
  • 11.4.2 children属性
  • 11.4.3 contains()方法 & compareDocumentPosition()方法
  • 11.4.4 插入文本
    - 1. innerText 属性
    - 2. outerText 属性
  • 11.4.5 滚动

第12 章 DOM2 和DOM3

12.2 样式
  • 12.2.1 访问元素的样式
      1. DOM 样式属性和方法
      1. 计算的样式
  • 12.2.2 操作样式表
      1. CSS 规则
      1. 创建规则
      1. 删除规则
  • 12.2.3 元素大小
      1. 偏移量
      1. 客户区大小
      1. 滚动大小
      1. 确定元素大小
  • 12.3 遍历
    • 12.3.1 NodeIterator
    • 12.3.2 TreeWalker
  • 12.4 范围
    • 12.4.1 DOM中的范围
        1. 用 DOM 范围实现简单选择
        1. 用 DOM 范围实现复杂选择
        1. 操作 DOM 范围中的内容
        1. 插入 DOM 范围中的内容
        1. 折叠 DOM 范围
        1. 比较 DOM 范围
        1. 复制 DOM 范围
        1. 清理 DOM 范围

  • 11.1.1 querySelector()方法
  • querySelector()方法接收一个 CSS 选择符,返回与该模式匹配的第一个元素,如果没有找到匹配的元素,返回 null。
//取得 body 元素
var body = document.querySelector("body");
//取得 ID 为"myDiv"的元素
var myDiv = document.querySelector("#myDiv");
//取得类为"selected"的第一个元素
var selected = document.querySelector(".selected");
//取得类为"button"的第一个图像元素
var img = document.body.querySelector("img.button");
  • 通过 Document 类型调用 querySelector()方法时,会在文档元素的范围内查找匹配的元素。而通过 Element 类型调用 querySelector()方法时,只会在该元素后代元素的范围内查找匹配的元素。
  • CSS 选择符可以简单也可以复杂,视情况而定。如果传入了不被支持的选择符, querySelector()会抛出错误。

  • 11.1.2 querySelectorAll()方法
  • querySelectorAll() 方法接收的参数与 querySelector()方法一样,都是一个 CSS 选择符,返回的是一个 NodeList 的实例。
  • 只要传给 querySelectorAll()方法的 CSS 选择符有效,该方法都会返回一个 NodeList 对象,而不管找到多少匹配的元素。如果没有找到匹配的元素, NodeList 就是空的。
  • 与 querySelector()类似,能够调用 querySelectorAll()方法的类型包括 Document、DocumentFragment 和 Element。
//取得某<div>中的所有<em>元素(类似于 getElementsByTagName("em"))
var ems = document.getElementById("myDiv").querySelectorAll("em");
//取得类为"selected"的所有元素
var selecteds = document.querySelectorAll(".selected");
//取得所有<p>元素中的所有<strong>元素
var strongs = document.querySelectorAll("p strong");
  • 要取得返回的 NodeList 中的每一个元素,可以使用 item()方法,也可以使用方括号语法
var i, len, strong;
for (i=0, len=strongs.length; i < len; i++){
strong = strongs[i]; //或者 strongs.item(i)
strong.className = "important";
}

11.1.3 element.matches() element.webkitMatchesSelector()方法
  • 这个方法接收一个参数,即 CSS 选择符,如果调用元素与该选择符匹配,返回 true;否则,返回 false。
  • 在取得某个元素引用的情况下,使用这个方法能够方便地检测它是否会被 querySelector() 或 querySelectorAll()方法返回。
  • webkitMatchesSelector() // 除了IE其它浏览器用这个方法match
  • msMatchesSelector():IE浏览器用这个方法
if (document.body.webkitMatchesSelector("body.page1")){
  //true
}

  • 11.2 元素遍历
  • 在使用 childNodes时会返回除了元素节点以外的所有节点,为了弥补这一差异,而同时又保持 DOM 规范不变, Element Traversal 规范(www.w3.org/TR/ElementTraversal/)新定义了一组属性。
  • Element Traversal API 为 DOM 元素添加了以下 5 个属性。
  • childElementCount:返回子元素(不包括文本节点和注释)的个数。
  • firstElementChild:指向第一个子元素; firstChild 的元素版。
  • lastElementChild:指向最后一个子元素; lastChild 的元素版。
  • previousElementSibling:指向前一个同辈元素; previousSibling 的元素版。
  • nextElementSibling:指向后一个同辈元素; nextSibling 的元素版。
    支持的浏览器为 DOM 元素添加了这些属性,利用这些元素不必担心空白文本节点

  • HTML5 - 11.3.1 与类相关的扩充
  • 1. getElementsByClassName()方法
  • 可以通过 document对象及所有 HTML 元素调用该方法;
  • getElementsByClassName()方法接收一个参数,该参数可以是一个或多个类名字符串,通过空格分隔;
  • 该方法会返回element或document的后代元素中匹配的元素,返回的是NodeList动态集合类型;
//取得所有类中包含"username"和"current"的元素,类名的先后顺序无所谓
var allCurrentUsernames = document.getElementsByClassName("username current");
//取得 ID 为"myDiv"的元素中带有类名"selected"的所有元素
var selected = document.getElementById("myDiv").getElementsByClassName("selected");
  • 2. classList 属性
  • 通过 className 属性为element添加、删除和替换类名。
  • 这个 classList 属性是新集合类型 DOMTokenList 的实例。与其他 DOM 集合类似;
  • DOMTokenList 有一个表示自己包含多少元素的 length 属性,而要取得每个元素可以使用 item()方法,也可以使用方括号语法。此外,这个新类型还定义如下方法。
  • add(value):将给定的字符串值添加到列表中。如果值已经存在,就不添加了。
  • contains(value):表示列表中是否存在给定的值,如果存在则返回 true,否则返回 false。
  • remove(value):从列表中删除给定的字符串。
  • toggle(value):如果列表中已经存在给定的值,删除它;如果列表中没有给定的值,添加它。
//删除"disabled"类
div.classList.remove("disabled");
//添加"current"类
div.classList.add("current");
//切换"user"类
div.classList.toggle("user");
//确定元素中是否包含既定的类名
if (div.classList.contains("bd") && !div.classList.contains("disabled")){
//执行操作
)
//迭代类名
for (var i=0, len=div.classList.length; i < len; i++){
doSomething(div.classList[i]);
}

HTML5 - 11.3.2 焦点管理
  • document.activeElement 属性,这个属性始终会引用 DOM 中当前获得了焦点的元素。
  • 元素获得焦点的方式有页面加载、用户输入(通常是通过按 Tab 键)和在代码中调用 focus()方法。
var button = document.getElementById("myButton");
button.focus();
alert(document.activeElement === button); //true
  • document.hasFocus()方法,这个方法用于确定文档是否获得了焦点。
var button = document.getElementById("myButton");
button.focus();
alert(document.hasFocus());   //true

11.3.3 HTML5 - HTMLDocument的变化
  • 1. readyState 属性
  • Document 的 readyState 属性有两个可能的值:
    • loading,正在加载文档;
    • complete,已经加载完文档。
if (document.readyState == "complete"){
//执行操作
}
  • 2. 兼容模式

  • 检测页面的兼容模式 document.compatMode ;

  • 在标准模式下, document.compatMode 的值等于"CSS1Compat",

  • 而在混杂模式下, document.compatMode 的值等于"BackCompat"

  • 3. head 属性

  • document.head 属性,引用文档的<head>元素。


HTML5 - 11.3.4 字符集属性
  • charset
  • charset属性表示文档中实际使用的字符集,也可以用来指定新字符集。
  • 默认情况下,这个属性的值为"UTF-16",但可以通过<meta>元素、响应头
    部或直接设置 charset 属性修改这个值。来看一个例子。
alert(document.charset); //"UTF-16"
document.charset = "UTF-8";
  • defaultCharset
  • 另一个属性是 defaultCharset,表示根据默认浏览器及操作系统的设置,当前文档默认的字符集应该是什么。如果文档没有使用默认的字符集,那 charset 和 defaultCharset 属性的值可能会不一样;

HTML5 - 11.3.5 自定义数据属性
  • HTML5 规定可以为元素添加非标准的属性,但要添加前缀 data-,目的是为元素提供与渲染无关的信息,或者提供语义信息。这些属性可以任意添加、随便命名,只要以 data-开头即可。
<div id="myDiv" data-appId="12345" data-myname="Nicholas"></div>
  • 添加了自定义属性之后,可以通过元素的 dataset 属性来访问自定义属性的值。 dataset 属性的值是 DOMStringMap 的一个实例,也就是一个名值对儿的映射。
var div = document.getElementById("myDiv");
//取得自定义属性的值
var appId = div.dataset.appId;
var myName = div.dataset.myname;
//设置值
div.dataset.appId = 23456;
div.dataset.myname = "Michael";

HTML5 - 11.3.6 插入标记
  • 1. innerHTML 属性
  • 在读模式下, innerHTML 属性返回与调用元素的所有子节点(包括元素、注释和文本节点)对应的 HTML 标记。
  • 在写模式下, innerHTML 会根据指定的值创建新的 DOM 树,然后用这个 DOM 树完全替换调用元素原先的所有子节点。
  • 在子节点的内容被替换后,被替换的元素节点的属性,事件处理等还会留在属性里,所以需要先删除属性和的事件,在做替换;
div = document.createElement("div");
div.innerHTML = "outside left <b>bold font...</b> out right";
document.body.appendChild(div)
  • 并不是所有元素都支持 innerHTML 属性。不支持 innerHTML 的元素有: <col>、 <colgroup>、<frameset>、 <head>、 <html>、 <style>、 <table>、 <tbody>、 <thead>、 <tfoot>和<tr>。

  • 2. outerHTML 属性
  • 在读模式下, outerHTML 返回调用它的元素及所有子节点的 HTML 标签。在写模式下, outerHTML会根据指定的 HTML 字符串创建新的 DOM 子树,然后用这个 DOM 子树完全替换调用元素。
  • 在子节点的内容被替换后,被替换的元素节点的属性,事件处理等还会留在属性里,所以需要先删除属性和的事件,在做替换;
<div id="content">
    <p>in div paragraph..</p>
</div>
<script>
div = document.getElementById("content");
div.outerHTML = "<p>It's replace content <strong>paragraph111</strong></p>";
// It's replace content paragraph111
</script>
div元素被替换掉了

  • 3. insertAdjacentHTML()方法

  • 插入标记的最后一个新增方式是 insertAdjacentHTML()方法。这个方法最早也是在 IE中出现的,它接收两个参数:插入位置和要插入的 HTML 文本。第一个参数必须是下列值之一:

    • "beforebegin",在当前元素之前插入一个紧邻的同辈元素;
    • "afterbegin",在当前元素之下插入一个新的子元素或在第一个子元素之前再插入新的子元素;
    • "beforeend",在当前元素之下插入一个新的子元素或在最后一个子元素之后再插入新的子元素;
    • "afterend",在当前元素之后插入一个紧邻的同辈元素
//作为前一个同辈元素插入
element.insertAdjacentHTML("beforebegin", "<p>Hello world!</p>");
//作为第一个子元素插入
element.insertAdjacentHTML("afterbegin", "<p>Hello world!</p>");
//作为最后一个子元素插入
element.insertAdjacentHTML("beforeend", "<p>Hello world!</p>");
//作为后一个同辈元素插入
element.insertAdjacentHTML("afterend", "<p>Hello world!</p>");

  • 4. 内存与性能问题
    • 在子节点的内容被替换后,被替换的元素节点的属性,事件处理等还会留在属性里,所以需要先删除属性和的事件,在做替换;
    • 如果在一个循环体里多次调用innerHTML、outerHtml会影响性能,最好把return的内容先赋给一个变量,让后在调用innerHTML。

11.4 专有扩展
  • 11.4.1 文档模式
  • doctype 最好使用标准模式 <!DOCTYPE html>

  • 11.4.2 children属性
  • children属性是 HTMLCollection 的实例,和childNodes一样返回子节点,但是不返回textNode等其他节点,只返回子元素节点;

  • 11.4.3 contains()方法 & compareDocumentPosition()方法
  • 返回元素节点是否被包含;
  • alert(htmlNode.contains(bodyNode)); // true
  • 返回alert(bodyNode.contains(htmlNode)); // false

compareDocumentPosition()方法

  • 后面的给定元素的相对参考元素(前面的调用元素)位置,并返回位置掩码;
    <div id="mydiv">
        <ul>
            <p id="myp"><span id="myspan">spanaaa</span></p>
            <li>line 1</li>
            <li>line 2</li>
            <li>line 3</li>
        </ul>
    </div>
    <script>
        mydiv = document.getElementById("mydiv");
        myspan = document.getElementById("myspan");
        // 包含8 + 居前2 = 10
        document.write(myspan.compareDocumentPosition(mydiv))
        // 被包含16 + 居后4 = 20
        document.write(mydiv.compareDocumentPosition(myspan))
    </script>

  • 11.4.4 插入文本
      1. innerText 属性
      • 设置 innerText 永远只会生成当前节点的一个子文本节点,而为了确保只生成一个子文本节点,
        就必须要对文本进行 HTML 编码。利用这一点,可以通过 innerText 属性过滤掉 HTML 标签。方法是
        将 innerText 设置为等于 innerText,这样就可以去掉所有 HTML 标签,比如:
        div.innerText = div.innerText;
      1. outerText 属性
      • 与innerText 属性作用相同,只是替换会替换整个元素;

第12 章 DOM2 和DOM3

  • OM1 级主要定义的是 HTML 和 XML 文档的底层结构。 DOM2 和 DOM3 级则在这个结构
    的基础上引入了更多的交互能力,也支持了更高级的 XML 特性。为此, DOM2 和 DOM3
    级分为许多模块(模块之间具有某种关联),分别描述了 DOM 的某个非常具体的子集。这些模块
    如下。
  • DOM2 级核心(DOM Level 2 Core):在 1 级核心基础上构建,为节点添加了更多方法和属性。
  • DOM2 级视图(DOM Level 2 Views):为文档定义了基于样式信息的不同视图。
  • DOM2 级事件(DOM Level 2 Events):说明了如何使用事件与 DOM 文档交互。
  • DOM2 级样式(DOM Level 2 Style):定义了如何以编程方式来访问和改变 CSS 样式信息。
  • DOM2 级遍历和范围(DOM Level 2 Traversal and Range):引入了遍历 DOM 文档和选择其特定
    部分的新接口。
  • DOM2 级 HTML(DOM Level 2 HTML):在 1 级 HTML 基础上构建,添加了更多属性、方法和
    新接口。
DOM 变化
  • 可以通过下列代码来确定浏览器是否支持这些 DOM 模块;
var supportsDOM2Core = document.implementation.hasFeature("Core", "2.0");
var supportsDOM3Core = document.implementation.hasFeature("Core", "3.0");
var supportsDOM2HTML = document.implementation.hasFeature("HTML", "2.0");
var supportsDOM2Views = document.implementation.hasFeature("Views", "2.0");
var supportsDOM2XML = document.implementation.hasFeature("XML", "2.0");

12.2 样式
  • 确定浏览器是否支持 DOM3 级定义的 CSS3 能力
alert(document.implementation.hasFeature("CSS3.0", "3.0"));  // true
  • 12.2.1 访问元素的样式
  • 通过元素的style属性来操作样式;
//设置背景颜色
myDiv.style.backgroundColor = "red";
  • 对于使用短划线(分隔不同的词汇,例如 background-image)的 CSS 属性名,必须将其转换成驼峰大小写形式,才能通过 JavaScript 来访问;例如:()background-image > style.backgroundImage)
  • 在标准模式下,所有度量值都必须指定一个度量单位。
myDiv.style.width = "100px";
myDiv.style.height = "200px";

  • 1. DOM 样式属性和方法
    // cssText:get or set element of style;
    mydiv.style.cssText = "width: 25px; height: 100px; background-color: green";
    alert(mydiv.style.cssText);

    // parentRule: The following JavaScript code gets the parent CSS style rule from a CSSStyleDeclaration:
    var declaration = document.styleSheets[0].cssRules[0].style;
    var rule = declaration.parentRule;

    // getPropertyPriority :检查rule的给定的property是否设置了!important,如果是就返回"important"
    var declaration1 = document.styleSheets[0].cssRules[0].style;
    var isImportant = declaration1.getPropertyPriority('margin') === 'important';

    // style.getPropertyValue: 返回css属性的值
    // The following JavaScript code queries the value of the margin property in a CSS selector rule:
    var declaration2 = document.styleSheets[0].cssRules[0].style;
    var value = declaration2.getPropertyValue('margin'); // "1px 2px"

    // style.removeProperty: 删除css属性
    var declaration3 = document.styleSheets[0].cssRules[0].style;
    var oldValue = declaration3.removeProperty('background-color');
    
    // style.setProperty: 添加css属性
    var declaration = document.styleSheets[0].cssRules[0].style;
    declaration.setProperty('border-width', '1px 2px');
    // Equivalent to:
    // declaration.borderWidth = '1px 2px';

2. 计算的样式

  • document.defaultView.getComputedStyle(element, null)
  • 所有计算的样式都是只读的;不能修改计算后样式对象中的 CSS 属性。此外,计算后的样式也包含属于浏览器内部样式表的样式信息,因此任何具有默认值的 CSS 属性都会表现在计算后的样式中。
  • 返回一个CSSStyleDeclaration 对象(与 style 属性的类型相同),其中包含当前元素的所有计算的样式,可以通过这个对象get到element计算后的最终样式;
    <style>
        #myp {
            border:1px solid red;
            color: #ff00d8;
            font-size:30px;
        }
        #mydiv {
            background-color: pink;
        }
    </style>
</head>
<body>
<div id="mydiv">
    <p id="myp" style="color:purple;font-size:30px;background-color: dodgerblue;">in div paragraph..</p>
</div>
<script>
    function notinarray(obj, arr) {
        var i = arr.length;
        while (i--) {
            if (arr[i] === obj) {
                return false;
            }
        }
        return true;
    }
    myp = document.getElementById("myp");
    // return CSSStyleDeclaration对象 of this element
    computedStyle = window.getComputedStyle(myp, null);  
    //  // 找到跟element对应的那条规则的cssText; cssRulesText = 内嵌cssRule + inline cssRule
    var cssRulesText = (document.styleSheets[0].cssRules[0].style.cssText) + " " + myp.style.cssText; 
    // 正则得到css属性名;
    var patt=new RegExp(/([a-z-]+):/g);
    
    // css属性去重;
    dictre = [];
    while (true){
        var re = patt.exec(cssRulesText);
        if (re==null){break}
        if (notinarray(re[1], dictre)){
            dictre.push(re[1])
        }
    }
    // 打印this element的 css属性computed值
    for (let i=0, len=dictre.length; i<len; i++){
        let attr = dictre[i];
        document.write(attr, "  : ", computedStyle[attr], "<br> ")
    }

    // border : 1px solid rgb(255, 0, 0)   // 只有chrome会显示border属性的值
    // color : rgb(128, 0, 128)
    // font-size : 30px
    // background-color : rgb(30, 144, 255)

  • 12.2.2 操作样式表

  • CSSStyleSheet 继承自 StyleSheet,后者可以作为一个基础接口来定义非 CSS 样式表。从StyleSheet 接口继承而来的属性如下。

    • disabled:表示样式表是否被禁用的布尔值。这个属性是可读/写的,将这个值设置为 true 可以禁用样式表。
    • href:如果样式表是通过<link>包含的,则是样式表的 URL;否则,是 null。
    • media:当前样式表支持的所有媒体类型的集合。与所有 DOM 集合一样,这个集合也有一个length 属性和一个 item()方法。也可以使用方括号语法取得集合中特定的项。如果集合是空列表,表示样式表适用于所有媒体。在 IE 中, media 是一个反映<link>和<style>元素 media特性值的字符串。
    • ownerNode:指向拥有当前样式表的节点的指针,样式表可能是在 HTML 中通过<link>或<style/>引入的(在 XML 中可能是通过处理指令引入的)。如果当前样式表是其他样式表通过@import 导入的,则这个属性值为 null。 IE 不支持这个属性。
    • parentStyleSheet:在当前样式表是通过@import 导入的情况下,这个属性是一个指向导入它的样式表的指针。
    • title: ownerNode 中 title 属性的值。
    • type:表示样式表类型的字符串。对 CSS 样式表而言,这个字符串是"type/css"。除 了 disabled 属 性 之 外 , 其 他 属 性 都 是 只 读 的 。 在 支 持 以 上 所 有 这 些 属 性 的 基 础 上 ,CSSStyleSheet 类型还支持下列属性和方法:
      • cssRules:样式表中包含的样式规则的集合。 IE 不支持这个属性,但有一个类似的 rules 属性。
      • ownerRule:如果样式表是通过@import 导入的,这个属性就是一个指针,指向表示导入的规则;否则,值为 null。 IE 不支持这个属性。
      • deleteRule(index):删除 cssRules 集合中指定位置的规则。 IE 不支持这个方法,但支持一个类似的 removeRule()方法。
      • insertRule(rule,index):向 cssRules 集合中指定的位置插入 rule 字符串。 IE 不支持这
        个方法,但支持一个类似的 addRule()方法。
    • 应用于文档的所有样式表是通过 document.styleSheets 集合来表示的。通过这个集合的 length属性可以获知文档中样式表的数量,而通过方括号语法或 item()方法可以访问每一个样式表。
  • 也可以直接通过<link>或<style>元素取得 CSSStyleSheet 对象。 DOM 规定了一个包含CSSStyleSheet 对象的属性,名叫 sheet;除了 IE,其他浏览器都支持这个属性。 IE 支持的是styleSheet 属性。要想在不同浏览器中都能取得样式表对象,可以使用下列代码。

function getStyleSheet(element){
return element.sheet || element.styleSheet;
}
//取得第一个<link/>元素引入的样式表
var link = document.getElementsByTagName("link")[0];
// 这里的 getStyleSheet()返回的样式表对象与 document.styleSheets 集合中的样式表对象相同。
var sheet = getStylesheet(link);  

1. CSS 规则
CSSRule 对象表示样式表中的每一条规则。实际上, CSSRule 是一个供其他多种类型继承的基类型,其中最常见的就是 CSSStyleRule 类型,表示样式信息(其他规则还有@import、 @font-face、@page 和@charset,但这些规则很少有必要通过脚本来访问)。 CSSStyleRule 对象包含下列属性。
- cssText:返回整条规则对应的文本。由于浏览器对样式表的内部处理方式不同,返回的文本可能会与样式表中实际的文本不一样; Safari 始终都会将文本转换成全部小写。 IE 不支持这个属性。
- parentRule:如果当前规则是导入的规则,这个属性引用的就是导入规则;否则,这个值为null。 IE 不支持这个属性。
- parentStyleSheet:当前规则所属的样式表。 IE 不支持这个属性。
- selectorText:返回当前规则的选择符文本。由于浏览器对样式表的内部处理方式不同,返回的文本可能会与样式表中实际的文本不一样(例如, Safari 3 之前的版本始终会将文本转换成全部小写) 。在 Firefox、 Safari、 Chrome 和 IE 中这个属性是只读的。 Opera 允许修改 selectorText。
- style:一个 CSSStyleDeclaration 对象,可以通过它设置和取得规则中特定的样式值。
- type:表示规则类型的常量值。对于样式规则,这个值是 1。 IE 不支持这个属性。

  • 其中三个最常用的属性是 cssText、 selectorText 和 style。 cssText 属性与 style.cssText属性类似,但并不相同。前者包含选择符文本和围绕样式信息的花括号,后者只包含样式信息(类似于元素的 style.cssText)。此外, cssText 是只读的,而 style.cssText 也可以被重写。

  • 大多数情况下,仅使用 style 属性就可以满足所有操作样式规则的需求了。这个对象就像每个元素上的 style 属性一样,可以通过它读取和修改规则中的样式信息。

// 假设这条规则位于页面中的第一个样式表中,而且这个样式表中只有这一条样式规则
div.box {
background-color: blue;
width: 100px;
height: 200px;
}

var sheet = document.styleSheets[0];
var rules = sheet.cssRules || sheet.rules; //取得规则列表
var rule = rules[0]; //取得第一条规则
alert(rule.selectorText); //"div.box"
alert(rule.style.cssText); //完整的 CSS 代码
alert(rule.style.backgroundColor); //"blue"
alert(rule.style.width); //"100px"
alert(rule.style.height); //"200px"
  • 使用这种方式,可以像确定元素的行内样式信息一样,确定与规则相关的样式信息。与使用元素的方式一样,在这种方式下也可以修改样式信息。
var sheet = document.styleSheets[0];
var rules = sheet.cssRules || sheet.rules; //取得规则列表
var rule = rules[0]; //取得第一条规则
rule.style.backgroundColor = "red"
//以这种方式修改规则会影响页面中适用于该规则的所有元素。
//如果有两个带有 box 类的<div>元素, 这两个元素都会应用修改后的样式。

2. 创建规则

function insertRule(sheet, selectorText, cssText, position){
    if (sheet.insertRule){
        sheet.insertRule(selectorText + "{" + cssText + "}", position);
    } else if (sheet.addRule){
        sheet.addRule(selectorText, cssText, position);
    }
}
//下面是调用这个函数的示例代码。
insertRule(document.styleSheets[0], "body", "background-color: silver", 0);

3. 删除规则

var sheet = document.styleSheets[0];
function deleteRule(sheet, index){
    if (sheet.deleteRule){                 // 除IE以外的浏览器的方法
        sheet.deleteRule(index);
    } else if (sheet.removeRule){      // IE浏览器的方法
        sheet.removeRule(index);
    }
}

- 12.2.3 元素大小
1. 偏移量

  • 包括元素在屏幕上占用的所有可见的空间。
  • offsetHeight:元素在垂直方向上占用的空间大小,以像素计。包括元素的高度、(可见的)水平滚动条的高度、上边框高度和下边框高度。
  • offsetWidth:元素在水平方向上占用的空间大小,以像素计。包括元素的宽度、(可见的)垂直滚动条的宽度、左边框宽度和右边框宽度。
  • offsetLeft:元素的左外边框至包含元素的左内边框之间的像素距离。
  • offsetTop:元素的上外边框至包含元素的上内边框之间的像素距离。
  • offsetLeft 和 offsetTop 属性与包含元素有关,包含元素的引用保存在 offsetParent属性中。
  • 要想知道某个元素在页面上的偏移量,将这个元素的 offsetLeft 和 offsetTop 与其 offsetParent的相同属性相加,如此循环直至根元素,就可以得到一个基本准确的值。
// 以下获取x轴上的偏移量
    function getElementLeft(element){
        var actualLeft = element.offsetLeft;
        var current = element.offsetParent;
        while (current !== null){
            actualLeft += current.offsetLeft;
            current = current.offsetParent;
        }
        return actualLeft;
    }
// 以下获取y轴上的偏移量
    function getElementTop(element){
        var actualTop = element.offsetTop;
        var current = element.offsetParent;
        while (current !== null){
            actualTop += current. offsetTop;
            current = current.offsetParent;
        }
        return actualTop;
    }
  • 这两个函数利用 offsetParent 属性在 DOM 层次中逐级向上回溯,将每个层次中的偏移量属性合计到一块。
  • 对于简单的 CSS 布局的页面,这两函数可以得到非常精确的结果。对于使用表格和内嵌框架布局的页面,由于不同浏览器实现这些元素的方式不同,因此得到的值就不太精确了。


    图片.png

2. 客户区大小

  • 元素内容及其内边距所占据的空间大小;

  • clientWidth

    • 元素内容区宽度加上左右内边距宽度;
  • clientHeight

    • 属性是元素内容区高度加上上下内边距高度;


      客户区大小
  • 客户区大小就是元素内部的空间大小,因此滚动条占用的空间不计算在内。

  • 最常用到这些属性的情况,就是确定浏览器视口大小的时候。

    function getViewport(){
        if (document.compatMode == "BackCompat"){     //混杂模式用这个
            return {
                width: document.body.clientWidth,
                height: document.body.clientHeight
            };
        } else {
            return {
                width: document.documentElement.clientWidth,   //标准模式用这个
                height: document.documentElement.clientHeight
            };
        }
    }
//这个函数会返回一个对象,包含两个属性: width和 height;
//表示浏览器视口(<html>或<body>元素)的大小。

3. 滚动大小

  • scrollHeight:在没有滚动条的情况下,元素内容的总高度。
  • scrollWidth:在没有滚动条的情况下,元素内容的总宽度。
  • scrollLeft:被隐藏在内容区域左侧的像素数。通过设置这个属性可以改变元素的滚动位置。
  • scrollTop:被隐藏在内容区域上方的像素数。通过设置这个属性可以改变元素的滚动位置。
  • 带有垂直滚动条的页面总高度就是 document.documentElement.scrollHeight;
  • 对于不包含滚动条的页面而言,scrollWidth 和 clientWidth相等, scrollHeight 和 clientHeight 相等;


    滚动大小
  • 通过 scrollLeft 和 scrollTop 属性确定元素当前滚动的状态
    • 在元素尚未被滚动时,这两个属性的值都等于 0。
    • 如果元素被垂直滚动了,那么 scrollTop 的值会大于 0,且表示元素上方不可见内容的像素高度。
    • 如果元素被水平滚动了,那么 scrollLeft 的值会大于 0,且表示元素左侧不可见内容的像素宽度。
  • 通过 scrollLeft 和 scrollTop 属性设置元素的滚动位置。
    • 因此将元素的scrollLeft 和 scrollTop 设置为 0,就可以重置元素的滚动位置。
       //检测元素是否位于顶部,如果不是就将其回滚到顶部。
       function scrollToTop(element){
           if (element.scrollTop != 0){
               element.scrollTop = 0;
           }
       }

4. 确定元素大小

  • viewport(视口)
    • 等于浏览器窗口,拥有浏览器窗口的宽度和高度,<html>元素使用viewport宽度的100%(在未手动设置html元素宽度情况下)。用document.documentElement.clientWidth获取viewport的宽度。
document.write(div.clientWidth,"<br>")
document.write(div.clientHeight,"<br>")
  • 元素大小
    用element.offsetWidth 和 element.offsetHeight 获取元素大小,元素大小包括
document.write("element", div.offsetWidth,"<br>")
document.write("element", div.offsetHeight,"<br>")
元素大小 page343
  • 1. 偏移量

  • offsetWidth - 返回元素宽,元素宽包括内容区 + 左右内边距 + 左右边框;

  • offsetHeight - 返回元素高,元素宽包括内容区 +上下内边距 + 上下边框;

  • offsetLeft - 表示该元素的border左外边缘与已定位的父容器(offsetParent对象)左padding外边缘的距离(不包括父元素border);

  • offsetTop - 表示该元素的border上外边缘与已定位的父容器(offsetParent对象)上padding外边缘的距离(不包括父元素border);

  • 2. Client 客户区大小

  • clientWidth - 元素内容宽+内边距 大小,不包括边框(IE下实际包括)、外边距、滚动条部分;

  • clientHeight - 元素内容高+内边距 大小,不包括边框(IE下实际包括)、外边距、滚动条部分;

  • clientLeft - 返回内边距的边缘和边框的外边缘之间的水平距离,也就是左边框宽度;

  • clientTop - 返回内边距的边缘和边框的外边缘之间的垂直距离,也就是上边框宽度;

  • 3. 滚动大小

  • scrollWidth - 是只读属性,是元素内容宽度(左padding外边缘 - 右padding外边缘,不包括滚动条),包括溢出而在屏幕上不可见的内容。

  • scrollHeight - 是只读属性,是元素内容高度(上padding外边缘 - 下padding外边缘,不包括滚动条),包括溢出而在屏幕上不可见的内容。

  • scrollLeft - 可以获取 or 设置一个元素的内容水平滚动的像素数(左边被隐藏内容的宽度的px值);

  • scrollTop - 可以获取 or 设置一个元素的内容垂直滚动的像素数(顶部被隐藏内容的高度的px值);

  • 比如元素的scrollHeight是1015,clientHeight是303,那么scrollTop就是712;

  • 4. 确定元素大小

  • 每个元素都提供了一个 getBoundingClientRect()方法。这个方法返回会一个矩形对象,包含 6 个属性: left、 top、 right 和 bottom。还有width和height。这些属性给出了元素在页面中相对于视口(浏览器的视口就是body元素或者html元素的左上角)的位置。

  • getBoundingClientRect()的width属性 == offsetWidth的值;

  • getBoundingClientRect()的Height属性 == offsetHeight的值;

12.3 遍历
  • 12.3.1 NodeIterator
    • 可以使用 document.createNodeIterator()方
      法创建它的新实例。接受4个参数:
      • 1.root:想要作为搜索起点的树中的节点。
      • 2.whatToShow:表示要访问哪些节点的数字代码。
        • NodeFilter.SHOW_ALL:显示所有类型的节点。
        • NodeFilter.SHOW_ELEMENT:显示元素节点。
        • NodeFilter.SHOW_ATTRIBUTE:显示特性节点。由于 DOM 结构原因,实际上不能使用这个值。
        • NodeFilter.SHOW_TEXT:显示文本节点。
        • NodeFilter.SHOW_CDATA_SECTION:显示 CDATA 节点。对 HTML 页面没有用。
        • NodeFilter.SHOW_ENTITY_REFERENCE:显示实体引用节点。对 HTML 页面没有用。
        • NodeFilter.SHOW_ENTITYE:显示实体节点。对 HTML 页面没有用。
        • NodeFilter.SHOW_PROCESSING_INSTRUCTION:显示处理指令节点。对 HTML 页面没有用。
        • NodeFilter.SHOW_COMMENT:显示注释节点。
        • NodeFilter.SHOW_DOCUMENT:显示文档节点。
        • NodeFilter.SHOW_DOCUMENT_TYPE:显示文档类型节点。
        • NodeFilter.SHOW_DOCUMENT_FRAGMENT:显示文档片段节点。对 HTML 页面没有用。
        • NodeFilter.SHOW_NOTATION:显示符号节点。对 HTML 页面没有用。
      • 3.filter:是一个 NodeFilter 对象,或者一个表示应该接受还是拒绝某种特定节点的函数。
        • 每个 NodeFilter 对象只有一个方法,即 acceptNode();如果应该访问给定的节点,该方法返回 NodeFilter.FILTER_ACCEPT,如果不应该访问给定的节点,该方法返回 NodeFilter.FILTER_SKIP。由于 NodeFilter 是一个抽象的类型,因此不能直接创建它的实例。在必要时,只要创建一个包含 acceptNode()方法的对象,然后将这个对象传入createNodeIterator()中即可。
    // 创建一个只显示<p>元素的节点迭代器。
    var filter = {
        acceptNode: function(node){
            return node.tagName.toLowerCase() == "p" ?
                NodeFilter.FILTER_ACCEPT :
                NodeFilter.FILTER_SKIP;
        }
    };
    var iterator = document.createNodeIterator(root, NodeFilter.SHOW_ELEMENT,
        filter, false);

    // 也可以是一个与 acceptNode()方法类似的函数
    var filter = function(node){
        return node.tagName.toLowerCase() == "p" ?
            NodeFilter.FILTER_ACCEPT :
            NodeFilter.FILTER_SKIP;
    };
    var iterator = document.createNodeIterator(root, NodeFilter.SHOW_ELEMENT,
        filter, false);

  • 4.entityReferenceExpansion:布尔值,表示是否要扩展实体引用。这个参数在 HTML 页面中没有用,因为其中的实体引用不能扩展。

  • NodeIterator 方法

    • nextNode() - 向前前进一步
    • previousNode() - 向后后退一步
   <div id="div1">
       <p><b>Hello</b> world!</p>
       <ul>
           <li>List item 1</li>
           <li>List item 2</li>
           <li>List item 3</li>
       </ul>
   </div>
   // 遍历<div>元素中的所有元素
<script>
     var div = document.getElementById("div1");
   var iterator = document.createNodeIterator(div, NodeFilter.SHOW_ELEMENT, null, false);
   var node = iterator.nextNode();
   while (node !== null) {
       alert(node.tagName); //输出标签名
       node = iterator.nextNode();
   }
   // 只想返回遍历中遇到的<li>元素, 把下面的过滤器传入第三个参数即可;
   var filter = function(node){
       return node.tagName.toLowerCase() == "li" ?
           NodeFilter.FILTER_ACCEPT :
           NodeFilter.FILTER_SKIP;
   };

</script>
  • 12.3.2 TreeWalker
    • TreeWalker 是 NodeIterator 的一个更高级的版本。除了包括 nextNode()和 previousNode()在内的相同的功能之外,这个类型还提供了下列用于在不同方向上遍历 DOM 结构的方法。
    • 方法
      • parentNode():遍历到当前节点的父节点;
      • firstChild():遍历到当前节点的第一个子节点;
      • lastChild():遍历到当前节点的最后一个子节点;
      • nextSibling():遍历到当前节点的下一个同辈节点;
      • previousSibling():遍历到当前节点的上一个同辈节点。
    //可以用 TreeWalker来代替 NodeIterator
    var div = document.getElementById("div1");
    var filter = function(node){
        return node.tagName.toLowerCase() == "li"?
            NodeFilter.FILTER_ACCEPT :
            NodeFilter.FILTER_SKIP;
    };
    var walker= document.createTreeWalker(div, NodeFilter.SHOW_ELEMENT,
        filter, false);
    var node = iterator.nextNode();
    while (node !== null) {
        alert(node.tagName); //输出标签名
        node = iterator.nextNode();
    }
  • filter 可以返回的值有所不同。除了 NodeFilter.FILTER_ACCEPT 和 NodeFilter.FILTER_SKIP 之外,还可以使用 NodeFilter.FILTER_REJECT, 会跳过相应节点及该节点的整个子树。例如,将前面例子中的
    NodeFilter.FILTER_SKIP 修改成 NodeFilter.FILTER_REJECT,结果就是不会访问任何节点。这是因为第一个返回的节点是<div>,它的标签名不是"li",于是就会返回 NodeFilter.FILTER_REJECT,这意味着遍历会跳过整个子树。在这个例子中, <div>元素是遍历的根节点,于是结果就会停止遍历。
  • TreeWalker 真正强大的地方在于能够在 DOM 结构中沿任何方向移动。使用 TreeWalker遍历 DOM 树,即使不定义过滤器,也可以取得所有<li>元素。
    var div = document.getElementById("div1");
    var walker = document.createTreeWalker(div, NodeFilter.SHOW_ELEMENT, null, false);
    walker.firstChild(); //转到<p>
    walker.nextSibling(); //转到<ul>
    var node = walker.firstChild(); //转到第一个<li>
    while (node !== null) {
        alert(node.tagName);
        node = walker.nextSibling();
    }
  • TreeWalker 类型还有一个属性:currentNode
    • 表示任何遍历方法在上一次遍历中返回的节点。通过设置这个属性也可以修改遍历继续进行的起点。
    var node = walker.nextNode();
    alert(node === walker.currentNode); //true
    walker.currentNode = document.body; //修改起点
12.4 范围
  • 12.4.1 DOM中的范围
    • document.createRange()来创建 DOM 范围
    • startContainer:range起始节点的父节点(即选区中第一个节点的父节点)。
    • startOffset:范围中选中第一个节点在其父节点的 childNodes 集合中的索引。
    • endContainer:range最终节点的父节点(即选区中最后一个节点的父节点)。
    • endOffset:范围中最后一个子节点(终点)在其父节点的 childNodes 集合中的索引。如果selectNode 选择的只有一个节点,那么endOffset会返回startOffset的值+1
    • commonAncestorContainer: startContainer 和 endContainer 共同的祖先节点在文档树中位置最深的那个(也就是离startContainer 和 endContainer 最近的共同祖先节点 )。
  • 1. 用 DOM 范围实现简单选择
    • selectNode() :选择整个节点,包括其子节点;
    • selectNodeContents(): 只选择节点的子节点。
      • <p>p content<b>bold font</b>p content</p>
<!DOCTYPE html>
<html>
    <body>
        <p id="p1"><b>Hello</b> world!</p>
    </body>
</html>
<script>
    var range1 = document.createRange();  // create range ins
    range2 = document.createRange();      
    p1 = document.getElementById("p1");
    range1.selectNode(p1);          // select element p
    range2.selectNodeContents(p1);  // select element p 的content 不包括element p
</script>
  • 其他方法
    • setStartBefore(Node):将范围的起点设置在 Node 之前,因此 Node 也就是范围选区中的第一个子节点。
    • setStartAfter(Node):将范围的起点设置在 Node 之后,因此 Node 也就不在范围之内了,其下一个同辈节点才是范围选区中的第一个子节点。
    • setEndBefore(Node):将范围的终点设置在 Node 之前,因此 Node 也就不在范围之内了,其上一个同辈节点才是范围选区中的最后一个子节点。
    • setEndAfter(Node):将范围的终点设置在 Node 之后,因此 Node 也就是范围选区中的最后一个子节点。
<div id="mydiv">
    <ol id="myol">
        <li>ol Coffee</li>
        <li>ol Tea</li>
        <li>ol Milk</li>
    </ol>
    <p>a paragraph line...</p>
    <ul id="myul">
        <li>ul Coffee</li>
        <li>ul Tea</li>
        <li>ul Milk</li>
    </ul>
</div>
<script>
    olNode = document.getElementById("myol");
    ulNode = document.getElementById("myul");
    var range1 = document.createRange(); //创建range
    var range2 = document.createRange(); //创建range

    range1.setStartBefore(olNode); // 把range的起点放在olNode前面(olNode是range中的第一个节点)
    range1.setEndAfter(ulNode); //把range的终点放在ulNode后面 (ulNode是range的最后一个节点)
    document.write(range1.endOffset, "<br>")  // 6
</script>

  • 2. 用 DOM 范围实现复杂选择
    • setStart() 方法
      • 根据参数选择range第一个节点, 参数一传入start节点的父节点(startContainer), 参数二传入start节点在父节点中的索引(startOffset)。
    • setEnd() 方法
      • 根据参数选择range最后一个节点, 参数一传入end节点的父节点(endContainer), 参数二传入end节点在父节点中的索引加1(endOffset)。
<body>
ol Coffee ol Tea ol Milk a paragraph line... ul Coffee ul Tea ul Milk
<div id="mydiv">
    <ol id="myol">
        <li>ol Coffee</li>
        <li>ol Tea</li>
        <li>ol Milk</li>
    </ol>
    <p>a paragraph line...</p>
    <ul id="myul">
        <li>ul Coffee</li>
        <li>ul Tea</li>
        <li>ul Milk</li>
    </ul>
</div>
<script>
    // range定在ol到ul
    divNode = document.getElementById("mydiv");
    rangeIns = document.createRange();
    rangeIns.setStart(divNode, 1);
    //用父节点的childNodes的遍历确定setEnd节点的偏移值(跟endOffset一样是childnodes的length从1开始计算的所以结果需要加1)
    divChilds = divNode.childNodes;
    for (i=0, len=divChilds.length; i<len; i++){
        if (divChilds[i].nodeName.toLowerCase() == "ul"){
            rangeIns.setEnd(divNode, i+1)
        }
    }
    document.write(rangeIns)
</script>

  • 3. 操作 DOM 范围中的内容

  • deleteContents()

    • Range.deleteContents() 移除来自 Document的Range 内容。
  • extractContents()

    • Range.extractContents() 也会从文档中移除范围选区。但是会返回一个[object DocumentFragment] ,就是range中的范围;
  • Range.cloneContents()

    • 返回 clone 的 Range。 返回是[object DocumentFragment] ;

  • 4.插入 DOM 范围中的内容

  • Range.insertNode(Node) 是在Range的起始位置插入节点。

  • 该方法将把指定的节点(和它的所有子孙节点)插入文档范围的开始点。当该方法返回时,当前范围将包括新插入的节点。如果 newNode 已经是文档的一部分,那么它将被从当前位置删除,然后重新插入范围的开始点。如果 newNodeDocumentFragment 节点,那么插入的不是它自身,而是它的子孙节点,按顺序插入范围的开始点。

  • 如果包含当前范围的开始点的节点是 Text 节点,那么在发生插入操作前,它将被分割成两个相邻的节点。如果 newNode 是 Text 节点,在插入文档后,它不会与任何相邻的 Text 节点合并。要合并相邻的节点,需要调用nodeObject.normalize()方法。

  • Range.surroundContents()方法将Range对象的内容移动到一个新的节点中, 并将这个新节点放回到range区域.如果选定的Range区域包含仅有一个节点标签的Text. 那标签将不会自动成对生成,操作将失败.

    <div id="div1"><p id="p1"><b id="myb">Hello</b> world!</p></div>

    <script>
        var range1 = document.createRange();
        range2 = document.createRange();
        p1 = document.getElementById("myb");
        range1.selectNode(p1.firstChild);                      //选中范围是<b>标签
        var span = document.createElement("span");  //创建新节点
        span.style.backgroundColor = "yellow";           //给新节点添加样式
        // span.innerText = "i am span";        ////就算给新节点添加文本, 也会被覆盖
        // range1.insertNode(span)
        range1.surroundContents(span);     //1.提取范围内容;2.将新节点插入范围的位置;3.把原来的范围内容插入新节点中;
  • 5. 折叠 DOM 范围
  • Range.collapse(bool) 方法向边界点折叠该 Range 。折叠后的 Range 为空,不包含节点内容。
    • 参数 true 表示折叠到范围的起点,参数 false 表示折叠到范围的终点。
  • 要确定 Range 是否已折叠,使用Range.collapsed 属性。
range.collapse(true); //折叠到起点
alert(range.collapsed); //输出 true
  • 6. 比较 DOM 范围

    • Range.compareBoundaryPoints()
    • compareBoundaryPoints() 方法比较两个范围的位置。
    • compareBoundaryPoints(how,sourceRange)
    • how:声明如何执行比较操作(即比较哪些边界点)。它的合法值是 Range 接口定义的常量。
    • sourceRange 要与当前范围进行比较的范围。
    • 如果当前范围的指定边界点位于 sourceRange 指定的边界点之前,则返回 -1。如果指定的两个边界点相同,则返回 0。如果当前范围的边界点位于 sourceRange 指定的边界点之后,则返回 1。
      描述
    • 该方法将比较当前范围的边界点和指定的 sourceRange 的边界点,并返回一个值,声明它们在源文档中的相对位置。参数 how 指定了比较两个范围的哪个边界点。该参数的合法值和它们的含义如下:
      • Range.START_TO_START - 比较两个 Range 节点的开始点
      • Range.END_TO_END - 比较两个 Range 节点的结束点
      • Range.START_TO_END - 用 sourceRange 的开始点与当前范围的结束点比较
      • Range.END_TO_START - 用 sourceRange 的结束点与当前范围的开始点比较
      • 常量 Range.START_TO_END 指定与当前范围的 end 点和 sourceRange 的 start 点进行比较。同样,常量 Range.END_TO_START 指定比较当前范围的 start 点和指定范围的 end 点
        My总结:也就是前面的range和后面的range比较,START_TO_END,前面range的end和后面range的start比较
  • 7. 复制 DOM 范围

    • 可以使用 cloneRange()方法复制范围。这个方法会创建调用它的范围的一个副本。
    • 新创建的范围与原来的范围包含相同的属性,而修改它的端点不会影响原来的范围。
var newRange = range.cloneRange();
  • 8. 清理 DOM 范围
    • 在使用完范围之后,最好是调用 detach()方法,以便从创建范围的文档中分离出该范围。调用detach()之后,就可以放心地解除对范围的引用,从而让垃圾回收机制回收其内存了。
range.detach(); //从文档中分离
range = null; //解除引用
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,898评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,401评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,058评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,539评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,382评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,319评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,706评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,370评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,664评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,715评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,476评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,326评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,730评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,003评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,275评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,683评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,877评论 2 335

推荐阅读更多精彩内容