第九章

DOM

  1. DOM(文档对象模型)是针对HTML和XML文档的一个API。描绘了一个层次化的节点树,允许开发人员添加,修改和删除页面的某一个部分。

  2. 节点层次

    • DOM可以将任何HTML或XML文档描绘成一个由多层节点构成的结构。
    • 节点分为几种不同的类型。每种类型分别表示文档中不同的信息或标记。
    • 每个节点都有各自的特点,方法和数据。
    • 每个节点之间和其他节点之间存在某种关系。
    • 节点之间的关系构成了层次,而所有页面标记则表现为一个以特定节点为根节点的树形结构。
    • 文档节点是每个文档的根节点。
    • <html>元素称为文档元素。文档元素是文档的最外层元素,文档中的其他所有元素都是包含在文档元素中。
    • 每个文档都只能有一个文档元素。在HTML中,文档元素始终都是<html>元素。在XML中,没有预定义的元素,则任何元素都有可能是文档元素。
    • 每一段标记都可以用树中的一个节点表示。
  3. Node类型

    1. 这个Node接口在JavaScript中是作为Node类型实现的;除了IE外,在其他所有浏览器中都可以访问到这个类型。
    2. JavaScript中的所有节点类型都是继承自Node类型,因此,所有节点类型都共享相同的基本属性和方法。
    3. 每个节点都有一个nodeType属性,用于表示节点类型。节点类型由在Node类型中定义的下列12个数据常量表示,任何节点必居其一。
    常量名 常量值 节点类型 描述
    Node.ELEMENT_NODE 1 Element 代表元素
    Node.ATTRIBUTE_NODE 2 Attr 代表属性
    Node.TEXT_NODE 3 Text 代表元素或属性中的文本内容
    Node.CDATA_SECTION_NODE 4 CDATASection 代表文档中的 CDATA 部(不会由解析器解析的文本)
    Node.ENTITY_REFERENCE_NODE 5 EntityReference 代表实体引用
    Node.ENTITY_NODE 6 Entity 代表实体
    Node.PROCESSING_INSTRUCTION_NODE 7 ProcessingInstruction 代表处理指令
    Node.COMMENT_NODE 8 Comment 代表注释
    Node.DOCUMENT_NODE 9 Document 代表整个文档(DOM 树的根节点)
    Node.DOCUMENT_TYPE_NODE 10 DocumentType 向为文档定义的实体提供接口
    Node.DOCUMENT_FRAGMENT_NODE 11 DocumentFragment 代表轻量级的 Document 对象(文档的某个部分)
    Node.NOTATION_NODE 12 Notation 代表 DTD 中声明的符号
    1. 由于IE没有公开Node类型的构造函数,所以为了确保跨浏览器兼容问题,最好是将nodeType属性与数字值进行比较。
    2. 对于元素节点,nodeName中保存的值始终是元素的标签名(大写),而nodeValue的值始终为null。
    3. 对于属性节点的 nodeName 是属性名称,nodeValue 属性包含属性值。
    4. 对于文本节点的 nodeName 永远是 #text,nodeValue 属性包含文本。
    5. 节点关系
      1. 每个节点都有一个childNodes属性,返回一个NodeList对象。该对象是一个类数组对象,用于保存一组有序的节点,可以通过位置来访问这些节点。且该对象有length属性,但它不是Array的实例。该对象是基于DOM结构动态执行查询的结果,就是说DOM结构的变化会自动反映到该对象中。
      2. 每个节点都有一个parentNode属性,返回该节点的父节点。每个节点的previousSibling和nextSibling属性,分别返回该节点的上一个和下一个节点。
      3. 每个节点都有hasChildNodes()方法,这个方法在该节点包含一个或多个节点的情况下返回true。
      4. 每个节点都有一个ownerDocument属性,返回表示整个文档的文档节点。这种关系表示的是任何节点都属于它所在的文档,任何节点都不能同时存在于两个或多个文档中。
    6. 操作节点
      1. appendChild() 用于向childNodes列表的末尾添加节点。如果传入该方法中的节点是文档的一部分,那么该传入的节点就会被移动到新的位置。
      2. insertBefore() 用于向childNode列表中特定的位置添加节点。接收两个参数:要插入的节点和作为参照的节点,同时该方法被返回。插入的节点会被插入到参照节点的前一个位置。如果参照节点是null,那么效果等同于appendChild。
      3. replaceChild() 用于替换节点,接收两个参数:要插入的节点和替换的节点。要替换的节点将有这个方法返回并从文档树中剔除,同时由要插入的节点占据其位置。使用该方法插入一个节点时,该节点的所有关系指针都会从被他所替换的节点复制过来。替换的节点仍在文档中,但文档中没有其位置了。
      4. removeChild() 用于移除节点,接收一个参数:要移除的节点。被移除的节点成为该方法的返回值。移除的节点仍在文档中,但文档中没有其位置了。
      5. cloneNode() 用于创建调用该方法的节点的一个完全相同的副本。接收一个布尔值:表示是否执行深复制。在参数为true下,执行深复制,就是复制节点以及其整个子节点树,但是不会复制添加到DOM树节点中的JavaScript属性,如事件处理程序等(注意:IE中有一个BUG,它会复制事件处理程序,因此在复制之前最好移除事件处理程序);在参数为false下,执行浅复制,即只复制该节点,复制后的节点归文档所有,但并没有为他指定父节点,所以这个节点在文档中类似于“孤儿”。
  4. Document类型

    1. document对象是HTMLDocument的一个实例,表示整个HTML页面。document对象是window对象的一个属性,因此可以将其作为全局对象来访问。

    2. Document节点有以下特征:

      1. nodeType的值为9
      2. nodeName的值为"#document"
      3. nodeValue的值为null
      4. parentNode的值为null
      5. ownerDocument的值为null
      6. 其子节点可能是一个DocumentType(最多一个),Element(最多一个),ProcessingInstruction或Comment
    3. 文档的子节点

      1. 访问document节点的html子节点内置的访问方法
        • documentElement属性,该属性始终指向HTML页面中的<html>
        • 通过document.childNodes[0]列表来访问
        • 通过document.firstChild来访问
      2. document.body直接指向<body>元素
      3. document.doctype可以访问<!DOCTYPE>元素,不同浏览器有差异
        • IE8及之前的版本:如果存在文档类型声明,会将其错误的解释为一个注释并把它当成Comment节点;而document.doctype的值始终为null。
        • IE9+以及Firefox:如果存在文档类型声明,则将其作为文档的第一个子节点;document.doctype是一个Document节点,也可以通过document.firstChild或document.childNodes[0]来访问同一个节点。
        • Safari,chrome和Opera:如果存在文档类型声明,将其解析,但不作为文档的子节点。document.doctype是一个Document节点,但该节点不会出现在document.childNodes中。
      4. <html>元素外部的注释在不同浏览器中处理不同
        <!-- 第一条注释 -->
        <html></html>
        <!-- 第二条注释 -->
        
        • ie8及之前的版本,Safari 3.1及更高的版本,opera和chrome 只会为第一条注释创建节点,不为第二条注释创建节点。因此第一条注释会成为document.childNodes中的第一个节点、
        • ie9及更高的版本,将第一条注释创建为document.childNodes中的一个注释节点,也将第二条注释创建为document.childNodes中的注释节点。
        • Firefox以及Safari 3.1之前的版本会完全忽略这两条注释。
    4. 文档信息

      1. document.title 包含<title>元素中的文本,会显示在浏览器窗口的标题栏或标签页上。修改title属性值也会改变<title>元素。
      2. document.URL 包含页面完整的URL(即地址栏中显示的URL)。
      3. document.domain 只包含页面的域名。不能将这个属性值设置为URL中不包含的域。
      4. document.referrer 包含链接到当前页面的那个页面的URL。在没有页面来源的情况下,可能为空字符串。
      5. 解决跨域问题的一个方法:
        • 由于跨域安全限制,来自不同子域的页面无法通过JavaScript通信。而将每个页面的document.domain设置为相同的值,那么这些页面就可以互相访问对方包含的JavaScript对象。
        • 例如,一个页面加载自www.wrox.com,其中包含一个内嵌框架,这个内嵌框架的页面加载自p2p.worx.com。如果将document.domain值都设置为"wrox.com",那么他们之间就能相互通信了。
    5. 查找元素

      1. document.getElementById() 接收一个参数:要取得的元素ID。如果找到相应的元素则返回该元素,如果不存在则返回null。注意ID必须严格匹配,包括大小写。如果页面中有多个元素的ID相同,返回第一次出现的元素。
      2. document.getElementByTagName() 接收一个参数:要取得的元素标签名,返回的是包含零个或多个元素的NodesList。在HTML文档中,返回的是HTMLCollection对象,类似于NodeList对象。
        1. HTMLCollection对象中的项访问方式
          1. 使用方括号中使用数值语法或item()
          2. 使用nameItem()方法,该方法接收的参数为要查找的元素的name属性值
          3. 使用方括号中使用查找的元素的name属性值
        2. 如果传入参数为*,则表示要获取全部标签,按照它们出现的先后顺序出现。
      3. document.getElementByName() 接收一个参数:要取得元素的name属性值,返回所有具有该name属性值的元素。
    6. 特殊集合

      1. document.anchors 包含文档中所有带name特性的<a>元素。
      2. document.forms 包含文档中所有的<form>元素。
      3. document.images 包含文档中所有的<img>元素。
      4. document.links 包含文档中所有带href特性的<a>元素。
    7. DOM一致性检测:检测浏览器实现了DOM的哪些部分,可以使用document.implementation.hasFeature()方法,该接收两个参数:要检测的DOM功能的名称和版本号。如果浏览器支持给定名称和版本的功能,则返回true,否则返回false。


      检测的不同的值及版本号
    8. 文档写入

      1. document.write() 接收一个字符串参数,即要写入到输出流中的文本。可动态的向页面中加入内容
      2. document.writeln() 接收一个字符串参数,即要写入到输出流中的文本,但会在字符串的末尾加上一个换行符。可动态的向页面中加入内容
      3. document.open() 打开网页的输出流
      4. document.close() 关闭网页的输出流
  5. Element类型

    1. 该类型用于表现XML或HTML元素,提供了对元素标签名,子节点及特性的访问。
    2. Element节点具有以下特征:
      1. nodeType的值为1
      2. nodeName的值为元素的标签名
      3. nodeValue的值为null
      4. parentNode的值为Document或Element
      5. 其子节点可能是Element,Text,Comment,ProcessiongInstruction,CADTASection或EntityReference
    3. 要访问元素的标签名,可以使用nodeName,也可以使用tagName
    4. HTML元素:任何元素的所有特性,都可以通过DOM元素本身的属性来直接访问。
      1. 所有HTML元素都由HTMLElement类型表示,不是直接通过这个联系,也是通过它的子类型来表示。HTMLElement类型直接继承自Element并添加了一些属性。添加的属性分别对应于每个HTML元素中都存在的下列标准特性。
        1. id,元素在文档中的唯一标识符
        2. title,有关元素的附加说明信息,一般通过工具提示条显示出来
        3. lang,元素的语言代码
        4. dir,语言的方向,值为"ltr"从左到右或"rtl"从右到左
        5. className,为元素指定的CSS类
    5. 取得特性:getAttribute() 接收一个参数:要取得的特性名
      1. 该方法也能获取到自定义特性
      2. 特性的名称是不区分大小写的,即"id"与"ID"是同一特性
      3. 当特性是style时,返回的是CSS文本
      4. 当特性是onclick这样的事件处理程序,返回相应代码的字符串
    6. 设置特性:setAttribute() 接收两个参数:要设置的特性名和值
      1. 如果特性已经存在,则会用指定的值替换现有的值;如果不存在,则新建该属性并赋值。
      2. 通过这个方法设置的特性名会被统一转换为小写形式。
    7. 删除特性:removeAttribute() 接收一个参数:即要删除特性的名称。会清除特性的值,也会将该特性从元素中彻底删除。
    8. attribute属性
      1. Element类型是使用attribute属性的唯一一个DOM节点类型。attribute属性中包含一个NameNodeMap,与NodeList类似。元素的每一个特性都由一个Attr节点表示,每个节点都保存在NameNodeMap对象中。该对象有以下方法:
        1. getNameItem(name):返回nodeName属性等于name的节点
        2. removeNameItem(name):从列表中删除nodeName属性等于name的节点,并返回被删除特性的attr节点
        3. setNameItem(node):向列表中添加节点,以节点的nodeName属性为索引
        4. item(pos):返回位于数字pos位置处的节点
      2. attribute属性中包含一系列节点,每个节点的nodeName就是特性的名称,而节点的nodeValue就是特性的值。
      3. 对于attribute对象中的属性,不同浏览器返回的顺序不同。这些特性在XML或HTML代码中的先后顺序,不一定与它们出现在attribute对象中的顺序一致。
    9. 创建元素:document.createElement() 接收一个参数:即要创建元素的标签名。这个标签名在HTML文档中不分大小写,但在XML文档中区分。
      1. 在IE中,可以在该方法中传入完整的元素标签,也可以包含属性。
      2. 一些问题可以通过传入完整的元素标签来解决:
        1. 不能设置动态创建的<iframe>元素的name特性
        2. 不能通过表单的reset()方法重设动态创建的<input>元素
        3. 动态创建的type特性值为"reset"的<button>元素重设不了表单
        4. 动态创建的一批name相同的单选按钮彼此毫无关系
  6. Text类型

    1. 文本节点由Text类型表示,包含的是可以照字面解释的纯文本内容。纯文本内容可以包含转义后的HTML字符,但不能包含HTML代码。
    2. Text节点具有以下特征:
      1. nodeType的值为3
      2. nodeName的值为"#text"
      3. nodeValue的值为节点所包含的文本
      4. parentNode的值Element
      5. 不支持(没有)子节点
    3. 可以通过nodeValue属性或data属性来访问Text节点包含的文本,这两个属性中包含的值相同。
    4. 操作节点中的文本方法:
      1. appendData(text):将text添加到文本的末尾
      2. deleteData(offset,count):从offset指定的位置开始删除count个字符
      3. insertData(offset,text):在offset指定的位置插入text
      4. replaceData(offset,count,text):用text替换从offset指定的位置开始到offset+count为止处的文本
      5. spliceText(offset):从offset指定为止将当前文本节点分成两个文本节点
      6. substringData(offset,count):提取从offset指定的位置开始到offset+count为止处的字符串
    5. 文本节点还有length属性,保存节点中字符的数目。nodeValue.length和data.length保存相同的值。
    6. 在默认下,每个可以包含内容的元素最多只能有一个文本节点,而且必须有内容存在。
    7. 创建文本节点:document.createTextNode() 接收一个参数:要出入节点的文本或完整的HTML代码(含文本)。
    8. 如果两个文本节点是相邻的同胞节点,那么这两个节点的文本就会连接起来,中间不会有空格。
    9. 规范文本节点:如果一个父元素包含两个或多个文本节点,可以调用normalize()方法,将所有节点合并成一个节点,结果节点的nodeValue等于合并前每个文本节点的nodeValue值拼接的值。
    10. 分隔文本节点:spliceText(offset)将一个文本节点分割成两个文本节点,按照指定位置分隔nodeValue值。原来的文本节点将包含从开始到指定位置之前的内容,新节点包含剩下的文本。该方法返回一个新文本节点,该节点与原节点的parentNode相同。
  7. Comment类型

    1. 注释在DOM中是通过Comment类型表示的。
    2. Comment节点具有以下特征:
      1. nodeType的值为8
      2. nodeName的值为"#comment"
      3. nodeValue的值为注释的内容
      4. parentNode的值Element或Document
      5. 不支持(没有)子节点
    3. Comment类型与Text类型继承自相同的类,因此它也拥有除splitText之外的所有字符串操作方法。
    4. 可以通过nodeValue或data属性来取得注释的内容。
    5. 使用document.createComment()并为其传递注释文本可以创建注释节点。
  8. CDATASection类型

    1. 该类型只针对XML文档,表示的是CDATA区域。
    2. 与Comment类似,拥有除了拥有除splitText之外的所有字符串操作方法。
    3. 具有以下特征:
      1. nodeType的值为4
      2. nodeName的值为"#cdata-section"
      3. nodeValue的值为CDATA区域中的内容
      4. parentNode的值Element或Document
      5. 不支持(没有)子节点
    4. 在真正的XML文档中,可以使用document.createCDataSection()来创建CDATA区域,只需传入节点的内容即可。
  9. DocumentType类型

    1. 在web浏览器中并不常用,仅有Firefox,safari和opera支持。
    2. 包含着与文档的doctype有关的所有信息,具有以下特征:
      1. nodeType的值为10
      2. nodeName的值为doctype的名称
      3. nodeValue的值为null
      4. parentNode的值Document
      5. 不支持(没有)子节点
    3. 在DOM1级中,DocumentType对象不能动态创建,而只能通过解析文档代码的方式创建。支持它的浏览器会把DocumentType对象保存在document.doctype中。
    4. DOM1级描述了DocumentType对象的三个属性:
      1. name 表示文档类型的名称
      2. entities 由文档类型描述的实体的NameNodeMap对象
      3. notations 由文档类型描述的符号的NameNodeMap对象
    5. 通常,浏览器中的文档使用的都是HTML或XHTML文档类型,所以entities和notations一般为空列表。
  10. DocumentFragment类型

    1. 在所有节点类型中,只有DocumentFragment在文档中没有对应的标记。DOM规定文档片段是一种轻量级的文档,可以包含和控制节点,但不会像完整文档那样占用额外的资源。
    2. 具有以下特征:
      1. nodeType的值为11
      2. nodeName的值为"document-fragment"
      3. nodeValue的值为null
      4. parentNode的值null
      5. 其子节点可能是Element,Text,Comment,ProcessiongInstruction,CADTASection或EntityReference
    3. 创建文档片段,可以使用document.createDocumentFragment()方法,不需要传参。
    4. 文档片段继承了Node的所有方法,通常用于执行那些用于针对文档的DOM操作。
    5. 如果将文档中的节点添加到文档片段中,就会从文档树中删除该节点,也不会在浏览器中看到该节点。添加到文档片段中的新节点同样也不属于文档树。可以通过appendChild()或insertBefore()将文档片段中内容添加到文档树中。在将文档片段作为参数传递给这两个方法时,实际上只会将文档片段的所有子节点添加到相应位置上;文档片段永远不会成为文档树的一部分。
    6. 假设要为一个<ul>元素添加列表项,如果逐个添加列表项,将会导致浏览器反复渲染新信息。为避免这个问题,可以先将列表项添加到文档片段中,然后一次性添加到文档中。
  11. Attr类型

    1. 元素的特性在DOM以Attr类型表示。在所有浏览器中(包含IE8),都可以访问Attr类型的构造函数和原型。
    2. 从技术角度看,特性就是存在于元素的attributes属性中的节点。特性节点有以下特征:
      1. nodeType的值为2
      2. nodeName的值为特性的名称
      3. nodeValue的值为特性的值
      4. parentNode的值null
      5. 在HTML中不支持(无)子节点
      6. 在XML中子节点可以是text或EntityReference
    3. Attr对象的三个属性:
      1. name 特性名称,与nodeName值相同
      2. value 特性的值,与nodeValue值相同
      3. specified 布尔值,区别特性是在代码中指定的还是默认的
    4. 使用document.createAttribute()并传入特性名称可以创建新的特性节点。
    5. 将新创建的特性节点添加到元素中,必须使用元素的setAttributeNode()方法。添加特性后,可以通过以下方法来访问特性:attributes属性,getAttributeNode()方法或getAttribute()方法。其中,attributes属性,getAttributeNode()方法都会返回对应特性的节点,getAttribute()方法只会返回特性的值。
  12. 动态脚本是指页面加载时不存在,但将来的某一时刻通过修改DOM动态添加的脚本。创建的两种方式:插入外部文件和直接插入JavaScript代码。

  13. 动态样式是指页面加载时不存在的样式,页面加载完成后动态添加到页面中的。加载外部样式文件的过程时异步的,也就是加载样式和执行JavaScript代码的过程没有固定的次序。

  14. 操作表格

    1. <table>添加的属性和方法如下
    属性和方法 说明
    caption 保存着对<caption>元素(如果有)的指针
    tBodies 是一个<tbody>元素的HTMLCollection
    tFoot 保存着对<tfoot>元素(如果有)的指针
    tHead 保存着对<thead>元素(如果有)的指针
    rows 是一个表格中所有行的HTMLCollection
    createTHead 创建<thead>元素,并将其放入到表格中,返回引用
    createTFoot 创建<tfoot>元素,将其放到表格中,返回引用
    createCaption 创建<caption>元素,将其放到表格中,返回引用
    deleteTHead 删除<thead>元素
    deleteTFoot 删除<tfoot>元素
    deleteCaption 删除<caption>元素
    deleteRow(pos) 删除指定位置的行
    insertRow(pos) 向rows集合中的指定位置插入一行
    1. <tbody>添加的属性和方法如下
    属性和方法 说明
    rows 保存着<tbody>元素中行的HTMLCollection
    deleteRow(pos) 删除指定位置的行
    insertRow(pos) 向rows集合中的指定位置插入一行,返回对新插入行的引用
    1. <tr>添加的属性和方法如下
    属性和方法 说明
    cells 保存着<tr>元素中单元格的HTMLCollection
    deleteCell(pos) 删除指定位置的单元格
    insertCell(pos) 向cells集合中的指定位置插入一个单元格,返回对新插入单元格的
  15. 使用NodeList

    • NodeList,NameNodeMap以及HTMLCollection这三个集合都是动态的,即每当文档结构发生变化时,它们都会得到更新。因此,它们始终会保存着最新,最准确的信息。
    • 本质上,所有NodeList对象都是在访问DOM文档时实时运行的查询。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容