JS高程:读书摘要(七)DOM

一、 DOM

DOM(文档对象模型)是针对HTMLXML文档的一个API(应用程序编程接口)。DOM描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。DOM脱胎于Netscape 及微软公司创始的DHTML(动态HTML),但现在它已经成为表现和操作页面标记的真正的跨平台、语言中立的方式。

Node类型

DOM1级定义了一个Node接口,该接口将由DOM 中的所有节点类型实现。每个节点都有一个nodeType属性,用于表明节点的类型。节点类型由在Node 类型中定义的下列12 个数值常量来表示,任何节点类型必居其一:

节点类型nodeType

节点的nodeName和nodeValue

  • 节点关系

每个节点都有一个childNodes属性,其中保存着一个NodeList 对象。NodeList是一种类数组对象(可以通过下标访问,有length属性,但它并不是Array的实例),用于保存一组有序的节点,可以通过位置(索引 / 方括号写法)来访问这些节点。NodeList对象的独特之处在于,它实际上是基于DOM 结构动态执行查询的结果,因此DOM结构的变化能够自动反映在NodeList 对象中。我们常说,NodeList是有生命、有呼吸的对象,而不是在我们第一次访问它们的某个瞬间拍摄下来的一张快照。

如何访问保存在NodeList中的节点——可以通过方括号,也可以使用item()方法。

var firstChild = someNode.childNodes[0];
var secondChild = someNode.childNodes.item(1);
var count = someNode.childNodes.length;

使用Array.prototype.slice()方法可以将NodeList对象转换为数。

var arrayOfNodes = Array.prototype.slice.call(someNode.childNodes,0);
  • 节点关系的引用属性

    • parentNode:该属性指向文档树中的父节点
    • previousSibling:该属性指向文档书中的前一个兄弟节点,第一个节点的previousSibling 属性值为null
    • nextSibling:该属性指向文档书中的前一个兄弟节点,最后一个节点的nextSibling属性值为null
    • 父节点的firstChildlastChild属性分别指向其childNodes 列表中的第一个和最后一个节点 中,someNode.firstChild 的值始终等于someNode.childNodes[0] , 而someNode.lastChild 的值始终等于someNode.childNodes [someNode.childNodes.length-1]
  • 操作子节点

    • appendChild():用于向childNodes列表的末尾添加一个节点。添加节点后,childNodes 的新增节点、父节点及以前的最后一个子节点的关系指针都会相应地得到更新。更新完成后,appendChild()返回新增的节点。如果传入到appendChild()中的节点已经是文档的一部分了,那么该节点就会成为父节点的最后一个子节点。
    • insertBefore():把节点放在childNodes列表中某个特定的位置上,这个方法接受两个参数:要插入的节点和作为参照的节点。插入节点后,被插入的节点会变成参照节点的前一个同胞节点(previousSibling),同时被方法返回。如果参照节点是null,则insertBefore()appendChild()执行相同的操作。
    • replaceChild():这个方法接受两个参数,要插入的节点和要替换的节点。要替换的节点将由这个方法返回并从文档树中被移除,同时由要插入的节点占据其位置。在使用replaceChild()插入一个节点时,该节点的所有关系指针都会从被它替换的节点复制过来。尽管从技术上讲,被替换的节点仍然还在文档中,但它在文档中已经没有了自己的位置。
    • removeChild():这个方法接受一个参数,即要移除的节点。被移除的节点将成为方法的返回值。
  • 操作节点的其他方法

    • cloneNode(),用于创建调用这个方法的节点的一个完全相同的副本。cloneNode()方法接受一个布尔值参数,表示是否执行深复制。在参数为true的情况下,执行深复制,也就是复制节点及其整个子节点树;在参数为false的情况下,执行浅复制,即只复制节点本身。复制后返回的节点副本属于文档所有,但并没有为它指定父节点。因此,这个节点副本就成为了一个“孤儿”,除非通过appendChild()insertBefore()replaceChild()将它添加到文档中。
    • normalize(),由于解析器的实现或DOM操作等原因,可能会出现文本节点不包含文本,或者接连出现两个文本节点的情况。当在某个节点上调用这个方法时,就会在该节点的后代节点中查找上述两种情况。如果找到了空文本节点,则删除它;如果找到相邻的文本节点,则将它们合并为一个文本节点。
var deepList = myList.cloneNode(true);
alert(deepList.childNodes.length); //3(IE < 9)或7(其他浏览器)

var shallowList = myList.cloneNode(false);
alert(shallowList.childNodes.length); //0

cloneNode()方法不会复制添加到DOM节点中的JavaScript属性,例如事件处理程序等。这个方法只复制特性、(在明确指定的情况下也复制)子节点,其他一切都不会复制。IE 在此存在一个bug,即它会复制事件处理程序,所以我们建议在复制之前最好先移除事件处理程序。

二、 Document 类型

document对象:在浏览器中,document对象是HTMLDocument(继承自Document类型)的一个实例,表示整个HTML页面。而且,document 对象是window 对象的一个属性,因此可以将其作为全局对象来访问。通过这个文档对象,不仅可以取得与页面有关的信息,而且还能操作页面的外观及其底层结构。Document 节点具有下列特征: nodeType 的值为9;nodeName 的值为"#document"nodeValue 的值为nullparentNode 的值为null

document对象的属性

var html = document.documentElement; //取得对<html>的引用

var body = document.body; //取得对<body>的引用

var doctype = document.doctype; //取得对<DOCTYPE>文档类型的引用

//取得文档标题
var originalTitle = document.title;
//设置文档标题
document.title = "New page title";

//取得完整的URL,不可设置
var url = document.URL;

//取得域名,可设置,但是不能随便设置
var domain = document.domain;

//假设页面来自p2p.wrox.com 域
document.domain = "wrox.com"; // 成功
document.domain = "nczonline.net"; // 出错! 只能省略域名

//取得来源页面的URL,不可设置
var referrer = document.referrer;
查找元素

1、getElementById():接收一个参数:要取得的元素的ID。如果找到相应的元素则返回该元素,如果不存在带有相应ID 的元素,则返回null。注意,这里的ID必须与页面中元素的id特性(attribute)严格匹配,包括大小写。IE8 及较低版本不区分ID的大小写,因此"myDiv""mydiv"会被当作相同的元素ID。如果页面中多个元素的ID 值相同,getElementById()只返回文档中第一次出现的元素。

<input type="text" name="myElement" value="Text field">
<div id="myElement">A div</div>

基于这段HTML 代码,在IE7 中调用document.getElementById("myElement "),结果会返回<input>元素;而在其他所有浏览器中,都会返回对<div>元素的引用。为了避免IE中存在的这个问题,最好的办法是不让表单(<input><textarea><button><select>)字段的name特性与其他元素的ID相同。

2、getElementsByTagName()。这个方法接受一个参数,即要取得元素的标签名,返回一个HTMLCollection对象,作为一个“动态”集合,返回所有的标签元素组成的类数组。HTMLCollection 对象还有一个方法,叫做namedItem(),使用这个方法可以通过元素的name特性取得集合中的项。例如,

<img src="myimage.gif" name="myImage">
 // 那么就可以通过如下方式从images几个中取得对应name的<img>元素:
var images = document.getElementsByTagName("img");
var myImage = images.namedItem("myImage");

// HTMLCollection 还支持按名称访问项
var myImage = images["myImage"];

在通过元素调用这个方法时,除了搜索起点是当前元素之外,其他方面都跟通过document调用这个方法相同,因此结果只会返回当前元素的后代。

var ul = document.getElementById("myList");
var items = ul.getElementsByTagName("li");

3、getElementsByClassName():这个方法接受一个参数,即要取得元素的类名,返回一个HTMLCollection对象,作为一个“动态”集合,返回所有拥有该类的元素组成的类数组。

4、为访问文档常用的部分提供了快捷方式:

  • document.anchors,包含文档中所有带name特性的<a>元素;
  • document.forms,包含文档中所有的<form>元素
  • document.images,包含文档中所有的<img>元素
  • document.links,包含文档中所有带href 特性的<a>元素。

这些特殊集合始终都可以通过HTMLDocument 对象访问到,而且,与HTMLCollection 对象类似,集合中的项也会随着当前文档内容的更新而更新。

文档写入

write()writeln()方法都接受一个字符串参数,即要写入到输出流中的文本。write()会原样写入,而writeln()则会在字符串的末尾添加一个换行符(\n)。在页面被加载的过程中,可以使用这两个方法向页面中动态地加入内容。方法open()close()分别用于打开和关闭网页的输出流。

<html>
    <head>
        <title>document.write() Example</title>
    </head>
    <body>
        <p>The current date and time is:
        <script type="text/javascript">
            document.write("<strong>" + (new Date()).toString() + "</strong>");
        </script>
        </p>
    </body>
</html>

这样做会创建一个DOM元素,而且可以在将来访问该元素。通过write()writeln()输出的任何HTML代码都将如此处理。

还可以使用write()writeln()方法动态地包含外部资源,例如JavaScript文件等。在包含JavaScript文件时,必须注意不能像下面的例子那样直接包含字符串"</script>",因为这会导致该字符串被解释为脚本块的结束,它后面的代码将无法执行。应该使用转义符做处理"<\/script>"

window.onload事件处理程序,等到页面完全加载之后延迟执行函数。函数执行之后,调用document.write()会重写整个页面内容。

三、Element 类型

Element 类型用于表现XMLHTML元素,提供了对元素标签名、子节点及特性的访问。Element节点具有以下特征:nodeType 的值为1;nodeName 的值为元素的标签名;nodeValue 的值为null

要访问元素的标签名,可以使用nodeName属性,也可以使用tagName属性;这两个属性会返回相同的值。

所有HTML元素都由HTMLElement 类型表示,不是直接通过这个类型,也是通过它的子类型来表示。HTMLElement类型直接继承自Element并添加了一些属性。添加的这些属性分别对应于每个HTML元素中都存在的下列标准特性。

id,元素在文档中的唯一标识符
title,有关元素的附加说明信息
lang,元素内容的语言代码,很少使用。
dir,语言的方向,值有ltr从左到右  rtl从右到做 ,很少使用

className,与元素的class 特性对应,即为元素指定的CSS类。
没有将这个属性命名为class,是因为class 是ECMAScript 的保留字。
通过className访问而不是class,**但是在使用操作元素属性的方法时需要使用class,与html上的属性保持一致**

<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>
操作元素属性

每个元素都有一或多个特性,这些特性的用途是给出相应元素或其内容的附加信息。操作特性的DOM方法主要有三个,分别是getAttribute()setAttribute()removeAttribute(),接受特性字符串作为第一个参数。setAttribute()可以传第二个参数作为设置的新值。特性的名称是不区分大小写的,即"ID""id"代表的都是同一个特性。另外也要注意,根据HTML5规范,自定义特性应该加上data-前缀以便验证。

注意,传递给getAttribute()的特性名与实际的特性名相同。因此要想得到class特性值,应该传入"class"而不是"className",后者只有在通过对象属性访问特性时才用。如果给定名称的特性不存在,getAttribute()返回null

1、 getAttribute()获取属性

任何元素的所有特性,也都可以通过DOM元素本身的属性来访问。不过,只有公认的(非自定义的)特性才会以属性的形式添加到DOM对象中。自定义属性只能通过getAttribute()来获得

<div id="myDiv" align="left" my_special_attribute="hello!"></div>

alert(div.id); //"myDiv"
alert(div.my_special_attribute); //undefined(IE 除外)
alert(div.align); //"left"

有两类特殊的特性,它们虽然有对应的属性名,但属性的值与通过getAttribute()返回的值并不相同。

  • 第一类特性就是style,用于通过CSS 为元素指定样式。

    • 在通过getAttribute()访问时,返回的style特性值中包含的是CSS文本
    • 而通过属性来访问它则会返回一个对象。由于style属性是用于以编程方式访问元素样式的(本章后面讨论),因此并没有直接映射到style特性。
  • 第二类与众不同的特性是onclick这样的事件处理程序。

    • 如果通过getAttribute()访问,则会返回相应代码的字符串。
    • 在访问onclick属性时,则会返回一个JavaScript函数(如果未在元素中指定相应特性,则返回null)。这是因为onclick及其他事件处理程序属性本身就应该被赋予函数值。

由于存在这些差别,在通过JavaScript 以编程方式操作DOM时,开发人员经常不使用getAttribute(),而是只使用对象的属性。只有在取得自定义特性值的情况下,才会使用getAttribute()方法。

2、 setAttribute()获取属性
setAttribute(),这个方法接受两个参数:要设置的特性名和值。如果特性已经存在,setAttribute()会以指定的值替换现有的值;如果特性不存在,setAttribute()则创建该属性并设置相应的值。通过setAttribute()方法既可以操作HTML特性也可以操作自定义特性。通过这个方法设置的特性名会被统一转换为小写形式,即"ID"最终会变成"id"。如果是使用这样的方式oDiv.myColor="div1"的方式设置的自定义属性,用getAttribute()是访问不到的。

3、 removeAttribute()删除属性
接受一个参数,即删除的属性key,这个方法用于彻底删除元素的特性。调用这个方法不仅会清除特性的值,而且也会从元素中完全删除特性,oDiv.removeAttribute("class");

创建元素

1、 document.createElement() 创建元素
这个方法只接受一个参数,即要创建元素的标签名(IE中可以传入完整的HTML标签字符串)。这个标签名在HTML文档中不区分大小写,而在XML(包括XHTML)文档中,则是区分大小写的。

var oDiv = document.createElement("div");

2、 增加元素

  • appendChild()ParentElement.appendChild(element); 在父元素末尾添加一个子元素
  • insertBefore()ParentElement.insertBefore(newElement,refElement); 指定的已有子节点之前插入新的子节点。

3、删除元素

  • removeChild()ParentElement.removeChild(elelment); 在父元素中移除某个子元素

4、改变元素

  • replaceChild()ParentElement.replaceChild(newElement,oldElement); 在父元素中替换某个子元素
元素的子节点

元素的childNodes属性中包含了它的所有子节点,这些子节点有可能是元素、文本节点、注释或处理指令。IE会返回所有的子元素节点,而其他浏览器则返回所有的子节点,这意味着在执行某项操作以前,通常都要先检查一下nodeTpye属性

for (var i=0, len=element.childNodes.length; i < len; i++){
    if (element.childNodes[i].nodeType == 1){ 
        //对子元素节点执行某些操作
    }
}

四、Text 类型

每个可以包含内容的元素最多只能有一个文本节点,而且必须确实有内容存在。(只有空格也算是有内容)

  • nodeType 的值为3
  • nodeName 的值为"#text"
  • nodeValue 的值为节点所包含的文本;

如果这个文本节点当前存在于文档树中,那么修改文本节点的结果就会立即得到反映。另外,在修改文本节点时还要注意,此时的字符串会经过HTML(或XML,取决于文档类型)编码。换句话说,小于号、大于号或引号都会像下面的例子一样被转义。

Some <strong>other</strong> message 
转义为
Some &lt;strong&gt;other&lt;/strong&gt; message
创建文本节点

document.createTextNode() : 创建新文本节点,这个方法接受一个参数——要插入节点中的文本。与设置已有文本节点的值一样,作为参数的文本也将按照HTMLXML 的格式进行编码。创建的文本节点,可以使用元素的appendChild()将其添加到元素中,在没有添加进DOM树之前,都是属于孤儿节点,文档碎片。

var textNode = document.createTextNode("Hello world!");
element.appendChild(textNode);

一般情况下,每个元素只有一个文本子节点。不过,在某些情况下也可能包含多个文本子节点,比如使用appendChild()方法多次添加文本节点,那么这两个节点中的文本就会连起来显示,中间不会有空格。如果在一个包含两个或多个文本节点的父元素上调用normalize()方法,则会将所有文本节点合并成一个节点,结果节点的nodeValue等于将合并前每个文本节点的nodeValue 值拼接起来的值。

浏览器在解析文档时永远不会创建相邻的文本节点。这种情况只会作为执行DOM操作的结果出现。

五、DOM操作技术

DOM操作往往是JavaScript程序中开销最大的部分,而因访问NodeList导致的问题最多。NodeList对象都是“动态的”,这就意味着每次访问NodeList对象,都会运行一次查询。有鉴于此,最好的办法就是尽量减少DOM操作。

动态脚本

指的是在页面加载时不存在,但将来的某一时刻通过修改DOM动态添加的脚本。

1、插入外部文件

function loadScript(url){
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = url; 
    document.body.appendChild(script); // 在这行代码执行之前都不会下载文件
}

loadScript("client.js");  // 方法调用时 动态添加script标签 并执行脚本

2、行内方式,也是添加script标签 ,但是脚本代码吧不是从外部引入,而是本地添加进去。所有写在<script></script>标签对中的代码都算是script元素的文本节点

var script = document.createElement("script");
script.type = "text/javascript";
script.text = "function sayHi(){alert('hi');}";
document.body.appendChild(script);

Safari 3.0 之前的版本不能正确地支持text属性,可以使用创建文本节点的方式解决。

function loadScriptString(code){
    var script = document.createElement("script");
    script.type = "text/javascript";
    try {
        script.appendChild(document.createTextNode(code));
    } catch (ex){
        script.text = code;
    }
    document.body.appendChild(script);
}

loadScriptString("function sayHi(){alert('hi');}");
动态样式

能够把CSS 样式包含到HTML 页面中的元素有两个。其中,<link>元素用于包含来自外部的文件,而<style>元素用于指定嵌入的样式。

同理。操作DOM代码添加标签可以很容易地动态创建出作用CSS样式的元素:

1、外部引入link

function loadStyles(url){
    var link = document.createElement("link");
    link.rel = "stylesheet";
    link.type = "text/css";
    link.href = url;
    var head = document.getElementsByTagName("head")[0];
    head.appendChild(link);
}

loadStyles("myStyle.css");

加载外部样式文件的过程是异步的,也就是加载样式与执行JavaScript代码的过程没有固定的次序。

2、内部style

这种方式会实时地向页面中添加样式,因此能够马上看到变化。

function loadStyleString(css){
    var style = document.createElement("style");
    style.type = "text/css";
    try{
        style.appendChild(document.createTextNode(css));
    } catch (ex){
        style.styleSheet.cssText = css;
    }
    var head = document.getElementsByTagName("head")[0];
    head.appendChild(style);
}

loadStyleString("body{background-color:red}");

事实上,IE 此时抛出的错误(将<script><style>视为一个特殊的元素,不允许DOM访问其子节点。)。解决IE 中这个问题的办法,就是访问元素的styleSheet属性,该属性又有一个cssText属性,可以接受CSS代码

操作表格

为了方便构建表格,HTML DOM还为<table><tbody><tr>元素添加了一些属性和方法。

<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 集合中的指定位置插入一行。

<tbody>元素添加的属性和方法如下。

  • rows:保存着<tbody>元素中行的HTMLCollection
  • deleteRow(pos):删除指定位置的行。
  • insertRow(pos):向rows 集合中的指定位置插入一行,返回对新插入行的引用。

<tr>元素添加的属性和方法如下。

  • cells:保存着<tr>元素中单元格的HTMLCollection
  • deleteCell(pos):删除指定位置的单元格。
  • insertCell(pos):向cells 集合中的指定位置插入一个单元格,返回对新插入单元格的引用。
使用NodeList

理解NodeList及其“近亲”NamedNodeMapHTMLCollection,是从整体上透彻理解DOM的关键所在。这三个集合都是“动态的”;换句话说,每当文档结构发生变化时,它们都会得到更新。因此,它们始终都会保存着最新、最准确的信息。从本质上说,所有NodeList对象都是在访问DOM文档时实时运行的查询。

如果想要迭代一个NodeList,最好是使用length属性初始化第二个变量,然后将迭代器与该变量进行比较。因为NodeList是动态的,NodeList.length也是动态的,如果在迭代的过程对这个NodeList有添加Node的操作,将会无限迭代下去,所以可以使用变量暂存length:var len = NodeList.length

一般来说,应该尽量减少访问NodeList 的次数。因为每次访问NodeList,都会运行一次基于文档的查询。所以,可以考虑将从NodeList中取得的值缓存起来。

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

推荐阅读更多精彩内容

  • 人一定都会做梦。世界上不做梦的只有两种人:一是至人无梦。至人是得道、成仙、成佛的人,以及有最高智能的人。二是愚人无...
    何须洒酒阅读 1,485评论 2 4
  • 当初的过去的情谊 已不可追 我曾经爱过的那个人你 十二年已经过去 虽然有不舍 可终究没能保护你 是我不够好 所以难...
    植成乔木阅读 143评论 0 0
  • 说到甜甜圈,大家第一印象可能就是简单的“yo,那个甜甜圈,它又厚又甜” 今天我们介绍一家把甜甜圈当做艺术品在卖的小...
    世界甜品阅读 417评论 0 0
  • 感情上的失意,不是在乎,而是在乎不起,因为你最怕失去的不应是已经拥有的东西,而是梦想。爱情如果只是一个过程,那么经...
    丿啷个哩个啷阅读 172评论 0 0