首页 前端知识 HTML5 权威指南(八)

HTML5 权威指南(八)

2024-08-21 10:08:57 前端知识 前端哥 474 376 我要收藏

原文:The Definitive Guide to HTML5

协议:CC BY-NC-SA 4.0

二十七、使用窗口对象

对象已经作为 HTML5 的一部分被添加到 HTML 规范中。在此之前,一直是非官方标准;浏览器以大体一致的方式实现了大致相同的功能。对于 HTML5,规范中的Window对象包含了事实上的功能和一些增强。此对象的实现是混合的;不同的浏览器有不同的合规水平。本章重点介绍具有合理支持级别的功能。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意本章描述的一些高级特性依赖于 DOM 事件,这是第三十章的主题。如果你对事件不熟悉,你可能希望阅读那一章,然后回到这一章中的例子。

对于那些在别处没有自然归属的特性来说,Window对象有点像一个垃圾场。当我们浏览这个物体的特征时,你会明白我的意思。表 27-1 对本章进行了总结。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

获取窗口对象

有两种方法可以得到一个Window对象。官方的 HTML5 方式是在Document对象上使用defaultView属性。另一种方法是使用所有浏览器都支持的window全局变量。清单 27-1 展示了这两种技术。

清单 27-1。获取一个窗口对象

`

              Example                                                      
outerWidth:
outerHeight:


    

`

在脚本中,我使用了Window对象来读取一对属性outerWidthouterHeight的值,这将在下一节中解释。

获取窗口信息

顾名思义,Window对象的基本功能与当前显示文档的窗口有关。表 27-2 列出了处理这个功能的属性和方法。出于 HTML 的目的,浏览器窗口中的选项卡被视为独立的窗口。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

清单 27-2 显示了如何使用这些属性来获取关于窗口的信息。

清单 27-2。获取关于窗口的信息

`

              Example                                                                                                                                               
outerWidth:outerHeight:             
innerWidth:innerHeight:             
screen.width:screen.height:             


    

`

本例中的脚本在一个表格中显示了各种Window属性的值。注意,我使用了screen属性来获得一个Screen对象。该对象提供窗口显示的屏幕信息,并定义表 27-3 所示的属性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

你可以在图 27-1 中看到脚本的效果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 27-1。显示关于窗口和屏幕的信息

与窗口互动

Window对象提供了一组方法,通过这些方法你可以与包含你的文档的窗口进行交互。这些方法在表 27-4 中描述。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

所有这些方法都应该谨慎使用,因为它们将浏览器窗口的控制权从用户手中夺走。用户对应用的行为有非常固定的期望,滚动、打印和自动关闭的窗口很大程度上不受欢迎。如果你必须使用这些方法,把控制权放在用户手中,并提供关于将要发生什么的清晰的视觉提示。

清单 27-3 显示了一些正在使用的窗口交互方法。

清单 27-3。与窗口互动

`

              Example                   

            Scroll             Print             Close         

        

            There are lots of different kinds of fruit - there are over 500 varieties             of banana alone. By the time we add the countless types of apples, oranges,             and other well-known fruit, we are faced with thousands of choices.             apple             One of the most interesting aspects of fruit is the variety available in             each country. I live near London, in an area which is known for             its apples.             banana             When traveling in Asia, I was struck by how many different             kinds of banana were available - many of which had unique flavors and             which were only available within a small region.

And, of course, there are fruits which are truly unique - I am put in mind
            of the durian, which is widely consumed in SE Asia and is known as the
            “king of fruits.” The durian is largely unknown in Europe and the USA - if
            it is known at all, it is for the overwhelming smell, which is compared
            to a combination of almonds, rotten onions and gym socks.
        


        

function handleButtonPress(e) {
                if (e.target.id == “print”) {
                    window.print();
                } else if (e.target.id == “close”) {
                    window.close();
                } else {
                    window.scrollTo(0, 400);
                }
            }
        
    

`

本例中的脚本打印、关闭和滚动窗口以响应按钮的按下。

提示用户

Window对象包含一组以不同方式提示用户的方法,如表 27-5 所述。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这些方法中的每一种都提供了不同类型的提示。清单 27-4 展示了如何使用它们。

清单 27-4。提示用户

`

              Example                   

Alert
        Confirm
        Prompt
        Modal Dialog

var buttons = document.getElementsByTagName(“button”);
            for (var i = 0 ; i < buttons.length; i++) {
                buttons[i].onclick = handleButtonPress;
            }             function handleButtonPress(e) {
                if (e.target.id == “alert”) {
                    window.alert(“This is an alert”);
                } else if (e.target.id == “confirm”) {
                    var confirmed
                       = window.confirm(“This is a confirm - do you want to proceed?”);
                    alert("Confirmed? " + confirmed);
                } else if (e.target.id == “prompt”) {
                    var response = window.prompt(“Enter a word”, “hello”);
                    alert("The word was " + response);
                } else if (e.target.id == “modal”) {
                    window.showModalDialog(“http://apress.com”);
                }
            }
        
    

`

应该谨慎使用这些功能。每个浏览器处理提示的方式不同,为用户创造了不同的体验。

作为一个例子,考虑图 27-2 ,它显示了 Chrome 和 Firefox 对警告提示采取的不同方法。提示可能看起来相似,但效果却大不相同。Chrome 从字面上理解了这个规范,并创建了一个模态对话框。这意味着在用户点击 OK 按钮并关闭提示之前,浏览器不会做任何事情。用户不能切换标签、关闭当前标签或使用浏览器做任何其他事情。Firefox 采取了更自由的观点,将提示的效果限制在当前标签页。这是一个更明智的方法,但是这是一个不同的方法,并且当选择在 web 应用中使用的特性时,不一致性是需要仔细考虑的事情。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 27-2。Chrome 和 Firefox 显示警告提示

showModalDialog方法创建了一个弹出窗口——一个被广告商滥用的功能。事实上,它已经被如此滥用,以至于所有的浏览器都努力限制这个特性在用户先前已经认可的网站上使用。如果你依靠一个弹出窗口向用户显示关键信息,你就有可能根本看不到这些信息。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 提示如果想吸引用户的注意力,可以考虑使用 jQuery 等 JavaScript 库提供的内嵌对话框。它们使用简单,侵入性小,并且在行为和视觉上跨浏览器保持一致。关于 jQuery 的更多信息,请参见我的书 Pro jQuery ,由 Apress 出版。

获取一般信息

Window对象提供了对返回更多一般信息的对象的访问,包括当前位置的详细信息(加载文档的 URL)和用户的浏览历史。这些属性在表 27-6 中描述。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个Document物体就是第二十六章的主题。由Window.location属性返回的Location对象与Document.location属性相同,我也在第二十六章中描述过。接下来我们将看看如何处理浏览器历史。

使用浏览器历史记录

Window.history属性返回一个History对象,您可以使用它对浏览器历史执行基本操作。表 27-7 描述了History对象定义的属性和方法。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在浏览历史中导航

backforwardgo方法告诉浏览器导航到历史中的一个 URL。backforward方法与浏览器的后退和前进按钮具有相同的效果。go方法导航到历史中相对于当前文档的位置。正值指定浏览器应该在历史中前进,负值指定后退。值的大小指定了多少步。例如,-2的值告诉浏览器导航到历史中最后一个之前的文档。清单 27-5 展示了这三种方法的使用。

清单 27-5。在浏览器历史中导航

`

              Example                   Back         Forward         Go

var buttons = document.getElementsByTagName(“button”);
            for (var i = 0 ; i < buttons.length; i++) {
                buttons[i].onclick = handleButtonPress;
            }

function handleButtonPress(e) {
                if (e.target.id == “back”) {
                    window.history.back();
                } else if (e.target.id == “forward”) {
                    window.history.forward();
                } else if (e.target.id == “go”) {
                    window.history.go(“http://www.apress.com”);
                }
            }
        
    

`

除了这些基本功能之外,HTML5 还支持在一定的限制下更改浏览器历史记录。最好从一个改变历史可以帮助解决问题的例子开始,如清单 27-6 所提供的。

清单 27-6。处理浏览器历史记录

`

              Example                   

        Banana         Apple

var sel = “No selection made”;
            document.getElementById(“msg”).innerHTML = sel;

var buttons = document.getElementsByTagName(“button”);
            for (var i = 0; i < buttons.length; i++) {
                buttons[i].onclick = function(e) {
                    document.getElementById(“msg”).innerHTML = e.target.innerHTML;
                };
            }
        
    

`

此示例包含一个脚本,该脚本根据用户单击的按钮显示消息。一切都很简单。问题是当用户离开示例文档时,关于哪个按钮被单击的信息丢失了。你可以在图 27-3 中看到这个效果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 27-3。常规历史序列

事件的顺序如下:

  1. 我导航到示例文档。显示No selection made消息。
  2. 我点击Banana按钮。显示Banana消息。
  3. 我导航到[apress.com](http://apress.com)
  4. 我单击 back 按钮返回到示例文档。

在这个序列的最后,我返回到示例文档,没有我先前选择的记录可用。这是浏览器的常规行为——浏览历史是使用 URL 处理的。当我单击 back 按钮时,浏览器返回到我的示例的 URL,我又从头开始了。我的会话历史如下所示:

  • [titan/listings/example.html](http://titan/listings/example.html)
  • [apress.com](http://apress.com)
将条目插入历史记录

History.pushState方法允许您将一个 URL 添加到浏览器历史记录中,并带有一些约束。URL 必须来自与当前文档相同的服务器名称和端口。添加 URL 的一种方法是只使用附加到当前文档的查询字符串或散列片段,如清单 27-7 所示。

清单 27-7。向浏览器历史记录添加条目

`

              Example                   

        Banana         Apple

var sel = “No selection made”;
            if (window.location.search == “?banana”) {
                sel = “Selection: Banana”;
            } else if (window.location.search == “?apple”) {
                sel = “Selection: Apple”;
            }

            document.getElementById(“msg”).innerHTML = sel;

var buttons = document.getElementsByTagName(“button”);
            for (var i = 0; i < buttons.length; i++) {
                buttons[i].onclick = function(e) {                     document.getElementById(“msg”).innerHTML = e.target.innerHTML;
                    window.history.pushState(“”, “”, “?” + e.target.id);
                };
            }
        
    

`

本例中的脚本使用pushState方法向浏览器历史添加一个项目。它添加的 URL 是当前文档的 URL 加上一个指示用户单击了哪个按钮的查询字符串。我还添加了一些代码,使用Location对象(在第二十六章中描述)来读取查询字符串和选择的值。这个脚本产生了两个用户可以识别的变化。第一个发生在用户点击其中一个按钮时,如图图 27-4 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 27-4。将项目推入浏览器历史记录的效果

当用户点击香蕉按钮时,浏览器导航栏会显示我推入浏览历史的 URL。文档不会重新加载;只有历史记录和显示的 URL 会改变。此时,浏览器历史如下所示:

  • [titan/listings/example.html](http://titan/listings/example.html)
  • [titan/listings/example.html?banana](http://titan/listings/example.html?banana)

每次点击一个按钮,都会有一个新的 URL 被添加到历史记录中,创建用户导航路径的记录。当用户导航到别处,然后返回到文档时,这些额外条目的好处就来了,如图 27-5 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 27-5。通过浏览器历史保存应用痕迹

这一次,当用户单击 back 按钮时,加载我插入到历史记录中的 URL,脚本使用查询字符串保存一些简单的应用状态。这是一个简单但有用的技术。

为不同的文件添加条目

向浏览器历史记录中添加项目时,不需要使用查询字符串或文档片段作为 URL。您可以指定与当前文档来源相同的任何 URL。然而,有一个奇怪的现象需要注意。清单 27-8 提供了一个演示。

清单 27-8。在历史记录条目中使用不同的 URL

`

              Example                   

        Banana         Apple

var sel = “No selection made”;
            if (window.location.search == “?banana”) {
                sel = “Selection: Banana”;
            } else if (window.location.search == “?apple”) {
                sel = “Selection: Apple”;
            }
            document.getElementById(“msg”).innerHTML = sel;

var buttons = document.getElementsByTagName(“button”);             for (var i = 0; i < buttons.length; i++) {
                buttons[i].onclick = function(e) {
                    document.getElementById(“msg”).innerHTML = e.target.innerHTML;
                    window.history.pushState(“”, “”, “otherpage.html?” + e.target.id);
                };
            }
        
    

`

这个脚本只有一个变化:我将pushState方法的 URL 参数设置为otherpage.html。清单 27-9 显示了otherpage.html的内容。

清单 27-9。otherpage.html 的内容

`

              Other Page                   

Other Page

        

              `

我仍然使用查询字符串来维护用户的选择,但是文档本身已经发生了变化。这就是奇怪的地方。图 27-6 显示了当你运行这个例子时,你会得到什么。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 27-6。在历史记录条目中使用不同的 URL

如图所示,其他文档的 URL 显示在导航框中,但是文档本身没有变化。这里有一个问题:如果用户导航到另一个文档,然后单击 back 按钮,浏览器可以选择显示原始文档(本例中为example.html)或指定的文档(otherpage.html)。你无法控制哪一个将被使用。更糟糕的是,不同的浏览器有不同的操作方式。

存储历史中的复杂状态

注意,当我在前几个例子中使用pushState方法时,我对前两个参数使用了空字符串("")。所有主流浏览器都忽略了中间的论点,这里也不感兴趣。但是第一个参数可能非常有用,因为它允许您将复杂的状态对象与浏览器历史中的 URL 相关联。

在前面的例子中,我使用了查询字符串来表示用户的选择,这对于这样简单的数据来说很好,但是如果您有更复杂的数据要保存,就没什么帮助了。清单 27-10 展示了如何使用第一个pushState参数来存储更复杂的东西。

清单 27-10。在浏览器历史中存储状态对象

`

              Example                                                                                                      
Name:
Color:
Size:
State:
Event:
        Banana         Apple

if (window.history.state) {
                displayState(window.history.state);
                document.getElementById(“state”).innerHTML = “Yes”;
            } else {
                document.getElementById(“name”).innerHTML = “No Selection”;
            }

window.onpopstate = function(e) {
                displayState(e.state);
                document.getElementById(“event”).innerHTML = “Yes”;
            }
            var buttons = document.getElementsByTagName(“button”);
            for (var i = 0; i < buttons.length; i++) {
                buttons[i].onclick = function(e) {
                    var stateObj;
                    if (e.target.id == “banana”) {
                        stateObj = {
                            name: “banana”,
                            color: “yellow”,
                            size: “large”
                        }
                    } else {
                        stateObj = {
                            name: “apple”,
                            color: “red”,
                            size: “medium”
                        }
                    }
                    window.history.pushState(stateObj, “”);
                    displayState(stateObj);
                };
            }

function displayState(stateObj) {
                document.getElementById(“name”).innerHTML = stateObj.name;
                document.getElementById(“color”).innerHTML = stateObj.color;
                document.getElementById(“size”).innerHTML = stateObj.size;
            }
        
    

`

在本例中,我使用一个具有三个属性的对象来表示用户的选择,这三个属性包含用户采摘的水果的名称、颜色和大小,如下所示:

stateObj = { name: "apple", color: "red", size: "medium"}                        

当用户做出选择时,我使用History.pushState方法创建一个新的历史条目,并将状态对象与它相关联,如下所示:

window.history.pushState(stateObj, "");

在这个例子中,我没有指定 URL,这意味着状态对象与当前文档相关联。(我这样做是为了论证可能性;我可以像前面的例子那样指定一个 URL。)

当用户返回到您的文档时,您可以使用两种方法来检索状态对象。第一种是通过history.state属性,就像这样:

... if (**window.history.state**) {     displayState(**window.history.state**); ...

你面临的问题是,并不是所有的浏览器都通过这个属性提供状态对象(比如 Chrome 就没有)。为了处理这个问题,您还必须监听popstate事件。我在第三十章中解释了事件,但是这个例子对于使用历史功能很重要,所以你可能想在阅读完那一章后再回到这一节。下面是监听和响应popstate事件的代码:

window.**onpopstate** = function(e) {     displayState(e.state);     document.getElementById("event").innerHTML = "Yes"; }

注意,我在一个table元素中显示了状态信息,以及如何获得状态对象的细节:通过属性还是事件。你可以在图 27-7 中看到这一点,但这是一个真正需要第一手实验的例子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 27-7。使用浏览器历史中的状态对象

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意你必须小心不要依赖于可用的状态信息。浏览器的历史记录可能会在许多不同的情况下丢失,包括用户明确删除它。

替换历史中的项目

前面的例子都集中在向当前文档之外的历史中添加条目,但是您可以使用replaceState方法来替换当前文档的条目。清单 27-11 提供了一个演示。

清单 27-11。替换浏览器历史中的当前条目

`

              Example                   

        Banana         Apple

var sel = “No selection made”;
            if (window.location.search == “?banana”) {
                sel = “Selection: Banana”;
            } else if (window.location.search == “?apple”) {
                sel = “Selection: Apple”;
            }
            document.getElementById(“msg”).innerHTML = sel;

var buttons = document.getElementsByTagName(“button”);
            for (var i = 0; i < buttons.length; i++) {
                buttons[i].onclick = function(e) {
                    document.getElementById(“msg”).innerHTML = e.target.innerHTML;
                    window.history.replaceState(“”, “”, “otherpage?” + e.target.id);
                };
            }
        
    

`

使用跨文档消息传递

Window对象是 HTML5 中另一个新特性的网关,这个新特性叫做跨文档消息传递。在正常情况下,来自不同来源(被称为来源)的脚本是不允许通信的,尽管脚本之间的通信是如此受欢迎的功能,以至于有无数的黑客和变通办法来绕过浏览器的安全措施。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 这是一个使用事件的高级话题,在第三十章中有描述。在阅读本节之前,您可能希望先阅读那一章。

了解剧本起源

浏览器使用 URL 的组成部分来确定资源(如脚本)的来源。不同来源的脚本之间的交互和通信受到限制。如果协议、主机名和端口相同,那么即使 URL 的其他部分不同,两个脚本也被认为来自同一来源。下表给出了一些例子,每个例子都与 URL [titan.mydomain.com/example.html](http://titan.mydomain.com/example.html)进行了比较。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

脚本可以使用document.domain属性来改变它们的原点,尽管只是为了扩大当前 URL 的焦点。例如,源自[server1.domain.com](http://server1.domain.com)[server2.domain.com](http://server2.domain.com)的脚本都可以将domain属性设置为domain.com,以便具有相同的来源。

HTML5 通过表 27-8 中描述的Window方法提供了这种通信的规范。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

为了设置这个特性的场景,清单 27-12 显示了我试图解决的问题。

清单 27-12。跨文档问题

`

              Example                   

Ready

        Send Message         

` `            ****         

        

document.getElementById(“status”).innerHTML = “Message Sent”;
            }
        
    

`

该文档包含一个从不同来源加载文档的iframe元素。只有当脚本来自相同的主机和端口时,它们才来自相同的源。我将从名为titan的服务器上的端口80加载该文档,因此端口81上的第二台服务器被视为不同的源。清单 27-13 显示了otherdomain.html文档的内容,它将由iframe加载。

清单 27-13。otherdomain.html 文件

`

              Other Page                                  `

目标是让主文档example.html能够调用嵌入文档otherdomain.htmlscript元素中定义的displayMessage函数。

我使用了postMessage方法,但是我需要在包含我想要定位的文档的Window上调用该方法。幸运的是,Window对象提供了查找嵌入文档所需的支持,如表 27-9 所述。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对于这个例子,我将使用数组样式的符号来定位我想要的Window对象,这样我就可以调用postMessage方法。清单 27-14 显示了对example.html文档的必要补充。

清单 27-14。定位一个窗口对象并调用 postMessage 方法

`

              Example                   

Ready

        Send Message         

                     

              `

我找到包含我要向其发送消息的脚本(window["nested"])的对象Window,然后调用postMessage方法。这两个参数是我想要发送的消息和目标脚本的来源,在本例中是[titan:81](http://titan:81),但是如果您遵循本例,那么您的环境会有所不同。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意作为一种安全措施,如果用错误的目标原点调用postMessage方法,浏览器将丢弃消息。

为了接收消息,我需要监听另一个脚本中的message事件。(如前所述,我在第三十章中解释了事件,如果您不熟悉事件及其操作,您可能希望现在阅读该章。)浏览器传递一个MessageEvent对象,该对象定义了表 27-10 中所示的属性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

清单 27-15 展示了如何使用message事件接收跨文档消息。

清单 27-15。监听消息事件

`

              Other Page                            

function receiveMessage(e) {
                if (e.origin == “http://titan”) {
                    displayMessage(e.data);
                } else {
                    displayMessage(“Message Discarded”);
                }
            }**

function displayMessage(msg) {
                document.getElementById(“banner”).innerHTML = msg;
            }
        
    

`

你可以在第三十章的中了解到addEventListener的方法。注意,当接收到消息事件时,我检查了MessageEvent对象的origin属性,以确保我识别并信任另一个脚本。这是一项重要的预防措施,可以防止来自未知和不可信脚本的消息被执行。我现在有了一个简单的机制,可以将消息从一个脚本发送到另一个脚本,即使它们有不同的来源。在图 27-8 中可以看到效果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 27-8。使用跨文档信息功能

使用计时器

Window对象提供的一个有用的特性是设置一次性和循环定时器的能力。这些定时器用于在预设时间后执行一项功能。表 27-11 总结了支持该特性的方法。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

setTimeout方法创建一个只执行一次指定函数的定时器,而setInterval方法创建一个重复执行函数的定时器。这些方法返回一个惟一的标识符,这个标识符以后可以用作clearTimeoutclearInterval方法的参数来取消计时器。清单 27-16 展示了定时器方法的使用。

清单 27-16。使用计时方法

`

              Example     ` `             

        

            Set Time             Clear Time             Set Interval             Clear Interval         

var timeID;
            var intervalID;
            var count = 0;

function handleButtonPress(e) {
                if (e.target.id == “settime”) {
                    timeID = window.setTimeout(function() {
                        displayMsg(“Timeout Expired”);
                    }, 5000);

                    displayMsg(“Timeout Set”);
                } else if (e.target.id == “cleartime”) {
                    window.clearTimeout(timeID);
                    displayMsg(“Timeout Cleared”);
                } else if (e.target.id == “setinterval”) {
                    intervalID = window.setInterval(function() {
                         displayMsg("Interval expired. Counter: " + count++);
                    }, 2000);

                    displayMsg(“Interval Set”);
                } else if (e.target.id == “clearinterval”) {
                    window.clearInterval(intervalID);
                    displayMsg(“Interval Cleared”);
                }
            }

function displayMsg(msg) {
                document.getElementById(“msg”).innerHTML = msg;
            }


    

`

本例中的脚本设置和取消调用displayMsg函数来设置p元素内容的计时器和时间间隔。你可以在图 27-9 中看到效果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 27-9。使用计时器和时间间隔

计时器和时间间隔可能很有用,但是您应该仔细考虑它们的使用。用户希望应用的状态保持一致,除非他们直接与之交互。如果您发现自己使用计时器来自动改变应用的状态,那么您可能希望考虑这样做的结果是对用户有帮助,还是仅仅是令人讨厌。

总结

在这一章中,我已经展示了通过Window对象组合在一起的奇怪的功能集合。有些特性与窗口直接相关,比如获取浏览器窗口的内部和外部大小以及显示窗口的屏幕。其他功能没有那么直接的关系。其中包括历史和跨文档消息传递功能,这是 HTML5 的重要功能。

二十八、使用 DOM 元素

在前一章中,HTMLElement对象的一些特性泄露到了对文档级特性的讨论中。现在,我们可以将注意力转向元素对象本身,并给予它应有的关注。在这一章中,我将向您展示不同的HTMLElement属性和方法,并演示如何使用它们。表 28-1 对本章进行了总结。请注意,并不是所有的例子都适用于所有的主流浏览器。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用元素对象

对象提供了一组属性,您可以使用这些属性来读取和修改关于所表示的元素的数据。表 28-2 描述了这些属性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

清单 28-1 显示了表中列出的一些基本属性的用法。

清单 28-1。使用基本元素数据属性

`

              Example                                              


            There are lots of differentß kinds of fruit - there are over 500 varieties
            of banana alone. By the time we add the countless
            types of apples,
            <span=“orange”>oranges, and other well-known fruit, we are
            faced with thousands of choices.
        


        
 

        

results.innerHTML += "tag: " + elem.tagName + “\n”;
            results.innerHTML += "id: " + elem.id + “\n”;
            results.innerHTML += "dir: " + elem.dir + “\n”;
            results.innerHTML += "lang: " + elem.lang + “\n”;
            results.innerHTML += "hidden: " + elem.hidden + “\n”;
            results.innerHTML += "disabled: " + elem.disabled + “\n”;
        
    

`

你可以在图 28-1 中看到浏览器为这些属性提供的结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 28-1。获取一个元素的信息

使用类

有两种方法可以处理一个元素所属的类。第一种是使用className属性,它返回一个类列表。您可以通过更改字符串的值来添加或移除类。在清单 28-2 中,你可以看到以这种方式读取和修改类。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 提示类的一个常见用途是用样式指向元素。你将在第二十九章中学习如何处理 DOM 中的样式。

清单 28-2。使用类名属性

`

              Example                                                       

            There are lots of different kinds of fruit - there are over 500 varieties             of banana alone. By the time we add the countless types of apples, oranges,             and other well-known fruit, we are faced with thousands of choices.         

        Press Me               `

在本例中,单击按钮会触发脚本,该脚本会向元素列表追加一个新类。请注意,我需要给附加到className属性值的值添加一个前导空格。这是因为浏览器需要一个类列表,每个类之间用空格隔开。当我做出这样的改变时,浏览器将应用选择器基于类的样式,这意味着在这个例子中有一个清晰的视觉变化,如图 28-2 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 28-2。使用类名属性

当您想要快速地向一个元素添加类时,className属性很容易使用,但是如果您想要做其他事情,比如删除一个类,它就变得很困难。幸运的是,您可以使用classList属性,它返回一个DOMTokenList对象。该对象定义了一些有用的方法和属性,允许你管理类列表,如表 28-3 所述。

除了这些属性和方法之外,您还可以使用数组样式的表示法通过索引来检索类。清单 28-3 中的显示了DOMTokenList对象的用法。

清单 28-3。使用 classList 属性

`

              Example                                                       

            There are lots of different kinds of fruit - there are over 500 varieties             of banana alone. By the time we add the countless types of apples, oranges,             and other well-known fruit, we are faced with thousands of choices.         

        
         Toggle Class          

listClasses();             function listClasses() {
                var classlist = document.getElementById(“textblock”).classList;
                results.innerHTML = "Current classes: "
                for (var i = 0; i < classlist.length; i++) {
                    results.innerHTML += classlist[i] + " ";
                }
            }

function toggleClass() {
                document.getElementById(“textblock”).classList.toggle(“newclass”);
                listClasses();
            }
        
    

`

在这个例子中,listClasses函数使用classList属性获取并枚举p元素所属的类,使用数组风格的索引器检索类名。

单击按钮时调用的toggleClass函数使用toggle方法来添加和删除一个名为newclass的类。这个类关联了一个样式,你可以在图 28-3 中看到这个类变化的视觉效果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 28-3。枚举和切换一个类

使用元素属性

有一些最重要的全局属性,但也支持读取和设置元素的任何属性。表 28-4 描述了由HTMLElement对象为此定义的可用方法和属性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

处理属性的四种方法都很容易使用,并且正如您所期望的那样。清单 28-4 展示了这些方法的使用。

清单 28-4。使用属性方法

`

              Example                                                       

            There are lots of different kinds of fruit - there are over 500 varieties             of banana alone. By the time we add the countless types of apples, oranges,             and other well-known fruit, we are faced with thousands of choices.         

        
          

results.innerHTML = "Element has lang attribute: "
                + elem.hasAttribute(“lang”) + “\n”;
            results.innerHTML += “Adding lang attribute\n”;
            elem.setAttribute(“lang”, “en-US”);
            results.innerHTML += "Attr value is : " + elem.getAttribute(“lang”) + “\n”;
            results.innerHTML += “Set new value for lang attribute\n”;
            elem.setAttribute(“lang”, “en-UK”);
            results.innerHTML += "Value is now: " + elem.getAttribute(“lang”) + “\n”;
             

`

在这个例子中,我检查、添加和更改了lang属性的值。你可以在图 28-4 中看到这个脚本产生的结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 28-4。使用属性方法

使用数据-*属性

在第三章的中,我描述了 HTML5 如何支持以data-为前缀的自定义属性,比如data-mycustomattribute。您可以通过dataset属性在 DOM 中处理这些自定义属性,该属性返回一个值数组,由名称的自定义部分进行索引。清单 28-5 包含了一个例子。

清单 28-5。使用数据集属性

`

              Example                                                       

for (var attr in elem.dataset) {
                results.innerHTML += attr + “\n”;
            }

results.innerHTML += "Value of data-fruit attr: " + elem.dataset[“fruit”];
        
    

`

属性返回的值数组不像常规数组那样按位置索引。如果想枚举data-*属性,可以使用一个for…in语句,如清单所示。或者,您可以通过名称请求值。注意,您只需要提供属性名的自定义部分。例如,如果您想要data-fruit属性的值,您可以请求value dataset[“fruit”]。你可以在图 28-5 中看到这个脚本的效果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 28-5。使用数据集属性

使用所有属性

您可以通过attributes属性获得包含元素所有属性的集合,该属性返回一个由Attr对象组成的数组。Attr对象的属性在表 28-5 中描述。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

清单 28-6 展示了如何使用attributes属性和Attr对象来读取和修改元素的属性。

清单 28-6。使用属性特性

`

              Example                                                       

            There are lots of different kinds of fruit - there are over 500 varieties             of banana alone. By the time we add the countless types of apples, oranges,             and other well-known fruit, we are faced with thousands of choices.         

        
          

var attrs = elem.attributes;

for (var i = 0; i < attrs.length; i++) {
                results.innerHTML += "Name: " + attrs[i].name + " Value: "
                    + attrs[i].value + “\n”;
            }

attrs[“data-fruit”].value = “banana”;

results.innerHTML += "Value of data-fruit attr: "
                + attrs[“data-fruit”].value;
        
    

`

从清单中可以看出,Attr对象数组中的属性是按照位置和名称进行索引的。在这个例子中,我列举了应用于元素的属性的名称和值,然后修改其中一个属性的值。你可以在图 28-6 中看到这个脚本的效果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 28-6。使用属性属性

使用文本

元素的文本内容由一个Text对象表示,它在文档模型中作为元素的子元素出现。清单 28-7 显示了一个带有一些文本内容的元素。

清单 28-7。具有文本内容的元素

`…

    There are lots of different kinds of fruit - there are over 500 varieties     of banana alone. By the time we add the countless types of apples, oranges,     and other well-known fruit, we are faced with thousands of choices.

...`

当浏览器表示文档模型中的p元素时,元素本身会有一个HTMLElement对象,内容会有一个Text对象,如图图 28-7 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 28-7。代表元素的对象与其内容之间的关系

如果一个元素有子元素,并且它们包含文本,那么每个子元素都将以相同的方式处理。清单 28-8 给段落添加了一个元素。

清单 28-8。在段落中增加一项内容

`…

    There are lots of different kinds of fruit - there are over **500** varieties     of banana alone. By the time we add the countless types of apples, oranges,     and other well-known fruit, we are faced with thousands of choices.

...`

添加b元素改变了用于表示p元素及其内容的节点层次,如图图 28-8 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 28-8。向段落添加元素的效果

p元素的第一个子元素是一个Text对象,表示从块的开始到b元素的文本。然后是b元素,它有自己的子Text对象,代表包含在开始和结束标签之间的文本。最后,p元素的最后一个子元素是一个Text对象,表示跟随b元素直到块尾的文本。表 28-6 描述了Text对象支持的成员。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

不幸的是,没有方便的方法来定位Text元素,除了通过找到它们的父元素对象并浏览它们的子元素。这使得使用Text元素变得更加困难。清单 28-9 展示了一些正在使用的Text元素的方法和属性。

清单 28-9。处理文本

`

              Example                                                       

            There are lots of different kinds of fruit - there are over 500             varieties of banana alone. By the time we add the countless types of apples,             oranges, and other well-known fruit, we are faced with thousands of choices.         

        Press Me         
          

document.getElementById(“pressme”).onclick = function() {
                var textElem = elem.firstChild;
                results.innerHTML = “The element has " + textElem.length + " chars\n”;                 textElem.replaceWholeText("This is a new string ");
            };
        
    

`

当按下button元素时,我显示p元素的第一个Text子元素中的字符数,并使用replaceWholeText方法更改其内容。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意处理文本时需要注意的重要一点是,空白是不会折叠的。这意味着任何用于向 HTML 添加结构的空格或其他空白字符都被视为文本的一部分。

修改模型

在前面的小节中,我已经向您展示了如何使用 DOM 来修改单个元素。例如,您可以更改属性和文本内容。您可以这样做,因为在 DOM 和文档本身之间有一个活动的链接。一旦对 DOM 进行了更改,浏览器就会在文档中进行相应的更改。您可以使用此链接进一步更改文档本身的结构。您可以随意添加、移除、复制和拷贝元素。这可以通过改变 DOM 层次结构来实现,因为链接是活动的,所以对层次结构所做的更改会立即反映在浏览器中。表 28-7 描述了可用于改变 DOM 层次结构的属性和方法。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这些属性和方法在所有元素对象上都可用。此外,document对象定义了两个允许您创建新元素的方法。当您想要向文档中添加内容时,这是非常重要的。这些创建方法在表 28-8 中描述。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建和删除元素

您通过document对象创建新元素,然后通过找到现有的HTMLElement并使用前面描述的方法之一插入它们。清单 28-10 提供了一个演示。

清单 28-10。创建和删除元素

`

              Example                                                                                                                                         
NameColor
BananaYellow
AppleRed/Green

Add Element
        Remove Element

document.getElementById(“add”).onclick = function() {

var row = tableBody.appendChild(document.createElement(“tr”));
              row.setAttribute(“id”, “newrow”);
              row.appendChild(document.createElement(“td”))
                .appendChild(document.createTextNode(“Plum”));
              row.appendChild(document.createElement(“td”))
                .appendChild(document.createTextNode(“Purple”));
            };

document.getElementById(“remove”).onclick = function() {
                var row = document.getElementById(“newrow”);
                row.parentNode.removeChild(row);
            }
        
    

`

本例中的脚本使用 DOM 在 HTML table中添加和删除行(在第十一章中有描述)。当添加行时,我首先创建一个tr元素,然后用它作为tdText对象的父元素。注意我是如何使用方法结果将调用链接在一起并(稍微)简化代码的。

如您所见,创建元素的过程很费力。您需要创建元素,将其与其父元素相关联,并对任何子元素或文本内容重复该过程。移除元素的过程也很笨拙。您必须找到元素,导航到父元素,然后使用removeChild方法。你可以在图 28-9 中看到这个脚本的效果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 28-9。使用 DOM 创建和删除元素

复制元素

您可以使用cloneNode方法来复制现有的元素。这是一种避免从头开始创建所需元素的便捷方法。清单 28-11 展示了这种技术。

清单 28-11。复制元素

`

              Example                                                                                                                        
MultiplyResult
1 x 11

Add Row         

document.getElementById(“add”).onclick = function() {
                var count = tableBody.getElementsByTagName(“tr”).length + 1;

var newElem = tableBody.getElementsByTagName(“tr”)[0].cloneNode(true);
                newElem.getElementsByClassName(“sum”)[0].firstChild.data = count
                    + " + " + count;
                newElem.getElementsByClassName(“result”)[0].firstChild.data =
                    count * count;

tableBody.appendChild(newElem);
            };
        
    

`

在这个例子中,我复制了表中的一个现有行来创建更多的行。方法的布尔参数指定了元素的子元素是否也应该被复制。在本例中,我指定了true,因为我希望包含在tr元素中的td元素构成我的新行的结构。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 提示注意这个例子中我需要为表格单元格设置文本的笨拙方式。处理对象真的是一件痛苦的事。关于更简单的方法,请参阅本章后面的“使用 HTML 片段”一节。

移动元素

当将元素从文档的一个部分移动到另一个部分时,只需将想要移动的元素与其新的父元素相关联。你不需要把元素从它的起始位置移开。清单 28-12 通过将一行从一个表格移动到另一个表格提供了一个演示。

清单 28-12。移动元件

`

              Example                                                                                                                   ****                      
FruitColor
BananaYellow
AppleRed/Green


            
            
                
            
        
FruitColor
PlumPurple


            Move Row
        


        
     `

button元素被按下时,脚本用appleid移动tr元素,用fruitsBodyid调用tbody元素上的appendChild元素。这具有将行从一个表格移动到另一个表格的效果,如图图 28-10 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 28-10。将元素从文档的一部分移动到另一部分

比较元素对象

可以用两种方式比较元素对象。第一个简单的方法是查看它们是否表示同一个元素,这可以使用isSameNode方法来完成。这允许你比较从不同查询中获得的对象,如清单 28-13 所示。

清单 28-13。比较元素对象

`

              Example                                                                                                                        
FruitColor
PlumPurple
        
` `         

var elemByID = document.getElementById(“plumrow”);
            var elemByPos
                = document.getElementById(“fruitsBody”).getElementsByTagName(“tr”)[0];

if (elemByID.isSameNode(elemByPos)) {
                document.getElementById(“results”).innerHTML = “Objects are the same”;
            }


    

`

本例中的脚本使用两种不同的技术来定位元素对象:通过搜索特定的id和根据父元素的标记类型进行搜索。当比较这些对象时,isSameNode方法返回 true,因为它们表示相同的元素。

另一种方法是测试元素对象是否相等,这可以通过使用isEqualNode方法来实现。如果元素属于相同的类型,具有相同的属性值,并且它们的每个子元素也是相同的并且顺序相同,那么它们就是相等的。清单 28-14 展示了一对相等的元素。

清单 28-14。使用相同的元素

`

              Example                                                                                                  ****                      
FruitColor
PlumPurple


            
            
                             
        
FruitColor
PlumPurple

 

        

if (elems[0].isEqualNode(elems[1])) {
                document.getElementById(“results”).innerHTML = “Elements are equal”;
            } else {
                document.getElementById(“results”).innerHTML = “Elements are NOT equal”;
            }
        
    

`

在这个例子中,两个tr元素是相等的,即使它们是文档不同部分中的不同元素。如果我改变了子元素td的任何属性或内容,那么这些元素将不再相等。

使用 HTML 片段

innerHTMLouterHTML属性以及insertAdjacentHTML方法是方便的语法快捷方式,允许您处理 HTML 片段,从而避免了创建复杂的元素和文本对象层次结构的需要。清单 28-15 演示了如何使用innerHTMLouterHTML属性从元素中获取 HTML。

清单 28-15。使用 innerHTML 和 outerHTML 属性

`

              Example                                                                                 ` `                ****                      
FruitColor
PlumPurple
                 

            Inner HTML             Outer HTML         

        

document.getElementById(“inner”).onclick = function() {
                results.innerHTML = row.innerHTML;
            };

document.getElementById(“outer”).onclick = function() {
                results.innerHTML = row.outerHTML;
            }
        
    

`

属性返回一个字符串,该字符串包含定义元素的 HTML 及其所有子元素的 HTML。属性只返回孩子的 HTML。在这个例子中,我定义了一对按钮,显示表格行的内部和外部 HTML。我将内容显示在一个textarea元素中,以便浏览器将这些属性返回的字符串视为文本,而不是 HTML。你可以在图 28-11 中看到脚本的效果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 28-11。显示表格行的 outerHTML 属性

更改文档结构

您也可以使用outerHTMLinnerHTML属性来改变文档的结构。我在本书这一部分的许多例子中使用了innerHTML属性,作为设置元素内容的一种便捷方式,因为我可以使用该属性来设置文本内容,而无需创建Text元素。清单 28-16 展示了如何使用这些属性来修改文档模型。

清单 28-16。修改文档模型

`

              Example                                                                                                                                         
FruitColor
BananaYellow
AppleRed/Green


            
            
                
                
            
        
FruitColor
PlumPurple
This is the placeholder


            Move Row
        


        
     `

在这个例子中,我使用了innerHTML属性来设置表格行的子元素,使用outerHTML来替换内联元素。这些属性作用于字符串,这意味着您可以通过读取属性值或从头开始创建字符串来获得 HTML 片段,如清单所示。你可以在图 28-12 的中看到效果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 28-12。使用 innerHTML 和 outerHTML 属性

插入 HTML 片段

innerHTMLouterHTML属性对于替换现有元素很有用,但是如果你想使用一个 HTML 片段来插入新元素,你必须使用insertAdjacentHTML方法。这个方法有两个参数。第一个是来自表 28-9 的值,指示片段相对于当前元素应该插入的位置,第二个是要插入的片段。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

清单 28-17 展示了使用insertAdjacentHTML方法在表格行元素中插入 HTML 片段。

清单 28-17。使用 insertAdjacentHTML 方法

`

              Example                                                                                                               
FruitColor
Placeholder


            After Begin
            After End
            Before Begin
            Before End
        


        

function handleButtonPress(e) {
                if (e.target.id == “ab”) {
                    target.insertAdjacentHTML(“afterbegin”, “After Begin”);
                } else if (e.target.id == “be”) {
                    target.insertAdjacentHTML(“beforeend”, “Before End”);
                } else if (e.target.id == “bb”) {
                    target.insertAdjacentHTML(“beforebegin”,
                        Before Begin”);
                } else {
                    target.insertAdjacentHTML(“afterend”,
                        After End”);
                }
            }


    

`

在这个例子中,我使用不同的位置值来演示如何在不同的位置插入 HTML 片段。这个例子最好在浏览器中实验,但是你可以在图 28-13 中看到基本效果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 28-13。将 HTML 片段插入文档

将元素插入文本块

修改模型的一个重要变化是向文本块添加一个元素,由一个Text对象表示。清单 28-18 展示了这是如何做到的。

清单 28-18。将元素插入文本块

`

              Example                                              

There are lots of different kinds of fruit - there are over             500 varieties of banana alone. By the time we add the countless types of             apples, oranges, and other well-known fruit, we are faced with thousands of             choices.         

        

            Insert Element         

              `

在这个例子中,我完成了一个稍微困难的任务,从现有的文本中取出一个单词,并使它成为一个新的b元素的子元素。与前面的例子一样,处理模型意味着一些冗长的代码。图 28-14 显示了结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 28-14。将元素插入文本块

总结

本章介绍了HTMLElementText对象的功能,它们分别代表 HTML 文档中的元素和内容。您了解了如何从对象中获取元素信息;如何处理文本内容;以及如何使用 DOM 的功能来添加、修改、复制、移动和删除元素。使用 DOM 可能需要冗长的脚本,但是对象模型和向用户显示文档的方式之间的实时链接使得这种努力是值得的。

二十九、样式化 DOM 元素

正如您在《??》第四章中回忆的那样,您可以间接(通过样式表或style元素)或直接(通过style属性)将样式应用于元素。在这一章中,我将展示如何使用 DOM 来处理文档中的 CSS 样式——既有明确定义的样式,也有浏览器用来实际显示元素的计算样式。在 DOM 中使用 CSS 的规范包含一些深层次的对象类型,其中许多不是由浏览器实现的。我简化了这一章中的对象,把重点放在浏览器使用的对象上。表 29-1 提供了本章的总结。请注意,并不是所有的例子都适用于所有的主流浏览器。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用样式表

您可以使用document.styleSheets属性访问文档中可用的 CSS 样式表,该属性返回表示与文档相关联的样式表的对象集合。表 29-2 总结了document.styleSheets属性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

每个样式表由一个CSSStyleSheet对象表示,该对象提供了一组属性和方法来操作文档中的样式。表 29-3 总结了CSSStyleSheet成员。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

获取关于样式表的基本信息

首先要获得关于文档中定义的样式表的一些基本信息。清单 29-1 给出了一个演示。

清单 29-1。获取文档中样式表的基本信息

`

              Example                                    ****                   

There are lots of different kinds of fruit - there are over             500 varieties of banana alone. By the time we add the countless types of             apples, oranges, and other well-known fruit, we are faced with thousands of             choices.         

        

            One of the most interesting aspects of fruit is the variety available in             each country. I live near London, in an area which is known for             its apples.         

                 **

for (var i = 0; i < sheets.length; i++) {
                var newElem = document.createElement(“table”);
                newElem.setAttribute(“border”, “1”);
                addRow(newElem, “Index”, i);
                addRow(newElem, “href”, sheets[i].href);
                addRow(newElem, “title”, sheets[i].title);
                addRow(newElem, “type”, sheets[i].type);
                addRow(newElem, “ownerNode”, sheets[i].ownerNode.tagName);
                placeholder.appendChild(newElem);
            }

function addRow(elem, header, value) {
                elem.innerHTML += “” + header + “:”
                    +  value + “”;
            }
        
    

`

本例中的脚本列举了文档中定义的样式表,并创建了一个包含每个样式表的基本信息的table元素。在这个文档中,有三个样式表。两个使用script元素定义,另一个包含在名为styles.css的外部文件中,并使用link元素导入到文档中。您可以在图 29-1 中看到脚本的输出。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 29-1。获取关于文档中样式表的信息

注意,不是所有的属性都有值。例如,只有当样式表作为外部文件加载时,href属性才会返回值。

使用媒体约束

正如我在第七章中所演示的,在定义样式表时,可以使用media属性来限制应用样式的环境。您可以通过CSSStyleSheet.media属性访问这些约束,该属性返回一个MediaList对象。MediaList对象的方法和属性在表 29-4 中描述。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

清单 29-2 展示了MediaList对象的使用。

清单 29-2。使用媒体列表对象

`

              Example                                                       

There are lots of different kinds of fruit - there are over             500 varieties of banana alone. By the time we add the countless types of             apples, oranges, and other well-known fruit, we are faced with thousands of             choices.         

        

            One of the most interesting aspects of fruit is the variety available in             each country. I live near London, in an area which is known for             its apples.         

                 

for (var i = 0; i < sheets.length; i++) {
                if (sheets[i].media.length > 0) {
                    var newElem = document.createElement(“table”);
                    newElem.setAttribute(“border”, “1”);
                    addRow(newElem, “Media Count”, sheets[i].media.length);
                    addRow(newElem, “Media Text”, sheets[i].media.mediaText);
                    for (var j =0; j < sheets[i].media.length; j++) {
                        addRow(newElem, "Media " + j, sheets[i].media.item(j));

}
                    placeholder.appendChild(newElem);
                }
            }

function addRow(elem, header, value) {
                elem.innerHTML += “” + header + “:”
                    +  value + “”;
            }
        
    

`

在这个例子中,我为任何具有media属性的样式表创建了一个表,枚举了单个媒体、属性值中媒体的总数以及整个media字符串。你可以在图 29-2 中看到脚本的效果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 29-2。使用媒体列表对象

禁用样式表

CSSStyleSheet.disabled属性允许您在一个步骤中启用和禁用样式表中的所有样式。清单 29-3 展示了如何使用这个属性来打开和关闭样式表。

清单 29-3。启用和禁用样式表

`

              Example                                                       

There are lots of different kinds of fruit - there are over             500 varieties of banana alone. By the time we add the countless types of` `            apples, oranges, and other well-known fruit, we are faced with thousands of             choices.         

         Press Me                        `

在本例中,单击按钮切换(唯一的)样式表上 disabled 属性的值。当一个样式表被禁用时,样式表中的任何样式都不会应用于元素,正如你在图 29-3 中看到的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 29-3。禁用和启用样式表

使用个人风格

CSSStyleSheet.cssRules属性返回一个CSSRuleList对象,该对象提供对样式表中各个样式的访问。在表 29-5 中描述了该对象的成员。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

样式表中的每个 CSS 样式都由一个CSSStyleRule对象表示(如果愿意,可以忽略术语上的不一致)。CSSStyleRule的成员见表 29-6 。

清单 29-4 显示了CSSRuleList对象的用法和CSSStyleRule对象的基本属性。我之所以说基本,是因为style属性返回了一个CSSStyleDeclaration属性,这个属性让你可以更深入地研究一个样式,这个对象就是你在将样式应用到单个元素时使用的对象。您可以在本章后面的“使用 CSSStyleDeclaration 对象”一节中了解更多关于CSSStyleDeclaration对象的信息。

清单 29-4。使用 CSSRuleList 和 CSSStyleRule 对象

`

              Example                                                       

There are lots of different kinds of fruit - there are over             500 varieties of banana alone. By the time we add the countless types of             apples, oranges, and other well-known fruit, we are faced with thousands of             choices.         

        

            One of the most interesting aspects of fruit is the variety available in             each country. I live near London, in an area which is known for             its apples.         

         Press Me ` `                 

document.getElementById(“pressme”).onclick = function() {

document.styleSheets[0].cssRules.item(1).selectorText = “#block2”;

if (placeholder.hasChildNodes()) {
                    var childCount = placeholder.childNodes.length;
                    for (var i = 0; i < childCount; i++) {
                        placeholder.removeChild(placeholder.firstChild);
                    }
                }
                processStyleSheet();
            }

function processStyleSheet() {
                var rulesList = document.styleSheets[0].cssRules;

for (var i = 0; i < rulesList.length; i++) {
                    var rule = rulesList.item(i);

var newElem = document.createElement(“table”);
                    newElem.setAttribute(“border”, “1”);

addRow(newElem, “parentStyleSheet”, rule.parentStyleSheet.title);
                    addRow(newElem, “selectorText”, rule.selectorText);
                    addRow(newElem, “cssText”, rule.cssText);
                    placeholder.appendChild(newElem);
                }
            }

function addRow(elem, header, value) {
                elem.innerHTML += “” + header + “:”
                    +  value + “”;
            }
        
    

`

这个例子对这些对象做了两件事。第一个是简单地获取关于已定义样式的信息,报告父样式表、选择器和样式中包含的各个声明。你可以在图 29-4 中看到这一点。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 29-4。获取关于风格的信息

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 提示注意我在样式声明中使用的速记属性是如何被浏览器扩展为它们的组成属性的。不是所有的浏览器都这样做。有些会显示速记属性,如果它们被使用过的话(例如,Firefox 显示速记属性;如图所示,Chrome 没有)。如果您试图将 CSS 解析为字符串,那么您需要考虑这一点。虽然,一般来说,像这样直接处理 CSS 值不是一个好主意。有关更好的方法,请参阅本章后面关于CSSStyleDeclaration对象的部分(“使用 CSSStyleDeclaration 对象”)。

该脚本还演示了更改样式是多么容易。当点击button时,其中一个样式的选择器从#block1变为#block2,这具有改变样式应用于哪个p元素的效果。正如对 DOM 的其他改变一样,浏览器会立即反映新的选择器,并更新样式的应用方式,如图图 29-5 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 29-5。改变风格选择器

使用元素样式

为了获得元素的样式属性中定义的属性,你要读取由HTMLElement对象定义的style属性的值(你可以在第二十八章中了解更多关于HTMLElement对象的信息)。style 属性返回一个CSSStyleDeclaration对象,它是可以通过样式表获得的同类对象。我将在下一节详细描述这个对象。为了演示HTMLElement.style属性,我使用了清单 29-5 中的CSSStyleDeclaration.cssText属性来显示和修改应用于一个元素的样式属性。

清单 29-5。从 HTMLElement 中获取 CSSStyleDeclaration 对象

`

              Example                                              

            There are lots of different kinds of fruit - there are over             500 varieties of banana alone. By the time we add the countless types of             apples, oranges, and other well-known fruit, we are faced with thousands of             choices.         

         Press Me ` `                 

document.getElementById(“pressme”).onclick = function() {
                targetElem.style.cssText = “color:black”;
                displayStyle();
            }

function displayStyle() {
                if (placeholder.hasChildNodes()) {
                    placeholder.removeChild(placeholder.firstChild);
                }
                var newElem = document.createElement(“table”);
                addRow(newElem, “Element CSS”, targetElem.style.cssText);
                placeholder.appendChild(newElem);
            }

function addRow(elem, header, value) {
                elem.innerHTML += “” + header + “:”
                    +  value + “”;
            }
        
    

`

这个脚本显示一个元素的style属性的值,当点击button时,改变这个值以应用不同的样式。你可以在图 29-6 中看到效果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 29-6。读取和改变应用于元素的样式

我在这个图中使用了 Firefox,因为它在cssText值中显示了简写的属性名。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 提示在关于样式表的章节中,我解释过试图解析cssText属性的值并不是一个好主意。这同样适用于处理单个元素。请参阅下面关于CSSStyleDeclaration对象的部分,以获得一种更健壮的方法来深入研究 CSS 属性值的细节。

使用 CSSStyleDeclaration 对象

无论您处理的是样式表还是元素的style属性,都没有关系。为了通过 DOM 完全控制 CSS,必须使用CSSStyleDeclaration对象。表 29-7 描述了这个重要对象的成员。

除了item方法,大多数浏览器都支持数组样式的符号,所以item(4)item[4]是等价的。

使用便利属性

使用CSSStyleDeclaration对象最简单的方法是通过便利属性,这些属性对应于单独的 CSS 属性。您可以通过读取相应的 object 属性来确定 CSS 属性的当前值,并通过为 object 属性分配新值来更改 CSS 值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 提示本节我读取和修改的值是配置的 。您可以有效地读取和修改 HTML 文档中定义的值,无论是在样式表中还是直接应用于元素。当浏览器开始显示一个元素时,它将生成一组计算值,其中允许浏览器样式、样式表和样式属性级联,并使用我在第四章中描述的模型进行继承。有关如何获取元素的计算 CSS 值的详细信息,请参见“使用计算样式”一节。

清单 29-6 提供了一个演示。

清单 29-6。使用 CSSStyleDeclaration 对象便利属性

`

              Example                                                       

There are lots of different kinds of fruit - there are over             500 varieties of banana alone. By the time we add the countless types of             apples, oranges, and other well-known fruit, we are faced with thousands of             choices.         

        

document.getElementById(“pressme”).onclick = function() {
                document.styleSheets[0].cssRules.item(1).style.paddingTop = “10px”;
                document.styleSheets[0].cssRules.item(1).style.paddingRight = “12px”;
                document.styleSheets[0].cssRules.item(1).style.paddingLeft = “5px”;
                document.styleSheets[0].cssRules.item(1).style.paddingBottom = “5px”;
                displayStyles();
            }             function displayStyles() {
                if (placeholder.hasChildNodes()) {
                    var childCount = placeholder.childNodes.length;
                    for (var i = 0; i < childCount; i++) {
                        placeholder.removeChild(placeholder.firstChild);
                    }
                }
                displayStyleProperties(document.styleSheets[0].cssRules.item(1).style);
                displayStyleProperties(document.getElementById(“block2”).style);
            }

function displayStyleProperties(style) {
                var newElem = document.createElement(“table”);
                newElem.setAttribute(“border”, “1”);

addRow(newElem, “border”, style.border);
                addRow(newElem, “color”, style.color);
                addRow(newElem, “padding”, style.padding);
                addRow(newElem, “paddingTop”, style.paddingTop);

placeholder.appendChild(newElem);                
            }

function addRow(elem, header, value) {
                elem.innerHTML += “” + header + “:”
                    +  value + “”;
            }
        
    

`

清单 29-6 中的脚本显示了四个CSSStyleDeclaration便利属性的值。这些是从从样式表和元素的style属性获得的对象中读取的,以演示获取这些对象的两种不同方式。您可以在图 29-7 中看到这些值是如何显示的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 29-7。从样式便利属性中读取值

bordercolorpadding便利属性对应于同名的 CSS 属性。paddingTop便利属性对应于padding-top CSS 属性。这是多单词 CSS 属性的一般命名模式:删除连字符,并将第二个和后续单词的第一个字母大写。如您所见,简写和单独的 CSS 属性都有方便的属性(例如,paddingpaddingTop)。当没有为相应的 CSS 属性设置值时,便利属性返回一个空字符串("")。

当单击按钮时,脚本使用从文档中第一个样式表获得的CSSStyleDeclaration对象上的paddingToppaddingBottompaddingLeftpaddingRight便利属性来修改单个填充属性的值。在图 29-8 中可以看到效果。不仅更改后的值会对文档的外观产生直接影响,而且速记和单个便利属性也会同步以反映新值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 29-8。通过 CSSStyleDeclaration 对象改变 CSS 属性

使用常规属性

如果您已经知道需要使用的 CSS 属性的名称,并且有一个便利属性可用,那么使用便利属性就很简单。如果您需要以编程方式探索 CSS 属性,或者获取/设置一个没有相应便利属性的 CSS 属性,那么CSSStyleDeclaration对象的其他成员会非常有用。清单 29-7 展示了一些正在使用的属性。

清单 29-7。使用 CSSStyleDeclaration 对象的常规属性

`

              Example                                                       

There are lots of different kinds of fruit - there are over             500 varieties of banana alone. By the time we add the countless types of             apples, oranges, and other well-known fruit, we are faced with thousands of             choices.         

         Press Me                           

document.getElementById(“pressme”).onclick = function() {
                var styleDeclr = document.styleSheets[0].cssRules[0].style;
                styleDeclr.setProperty(“background-color”, “lightgray”);
                styleDeclr.setProperty(“padding-top”, “20px”);
                styleDeclr.setProperty(“color”, “black”);
                displayStyles();
            }

function displayStyles() {
                if (placeholder.hasChildNodes()) {
                    var childCount = placeholder.childNodes.length;
                    for (var i = 0; i < childCount; i++) {
                        placeholder.removeChild(placeholder.firstChild);
                    }
                }

var newElem = document.createElement(“table”);
                newElem.setAttribute(“border”, “1”);

var style = document.styleSheets[0].cssRules[0].style;

addRow(newElem, “border”, style.getPropertyValue(“border”));
                addRow(newElem, “color”, style.getPropertyValue(“color”));
                addRow(newElem, “padding-top”, style.getPropertyValue(“padding-top”));
                addRow(newElem, “background-color”,
                       style.getPropertyValue(“background-color”));

placeholder.appendChild(newElem);                
            }

function addRow(elem, header, value) {
                elem.innerHTML += “” + header + “:”
                    +  value + “”;
            }
             

`

在这个例子中,我只从一个来源读取样式属性:样式表。我使用getPropertyValue方法检索 CSS 属性的值,使用setProperty方法定义新值。请注意,您在这些方法中使用了真正的 CSS 属性名称,而不是便利属性的名称。

以编程方式浏览属性

在迄今为止的例子中,我已经明确地命名了我想要使用的 CSS 属性。如果我想在事先不知道的情况下获得关于哪些属性已经被应用的信息,我必须通过CSSStyleDeclaration成员进行探索,如清单 29-8 所示。

清单 29-8。以编程方式探索 CSS 属性

`

              Example                                                       

There are lots of different kinds of fruit - there are over             500 varieties of banana alone. By the time we add the countless types of             apples, oranges, and other well-known fruit, we are faced with thousands of             choices.         

                 

function displayStyles() {
                var newElem = document.createElement(“table”);
                newElem.setAttribute(“border”, “1”);

var style = document.styleSheets[0].cssRules[0].style;
                for (var i = 0; i < style.length; i++) {                     addRow(newElem, style[i], style.getPropertyValue(style[i]));
                }

placeholder.appendChild(newElem);                
            }

function addRow(elem, header, value) {
                elem.innerHTML += “” + header + “:”
                    +  value + “”;
            }
        
    

`

本例中的脚本列举了样式表中第一种样式的属性。你可以在图 29-9 中看到结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 29-9。枚举样式中的属性

获取样式属性重要性

正如我在第四章中解释的,当浏览器评估哪些值用于显示一个元素时,你可以将!important应用到属性声明中,以赋予值优先权。当处理CSSStyleDeclaration对象时,您可以使用getPropertyPriority方法来查看!important是否已经应用到一个属性,如清单 29-9 所示。

清单 29-9。了解财产的重要性

`

              Example                                                       

There are lots of different kinds of fruit - there are over             500 varieties of banana alone. By the time we add the countless types of             apples, oranges, and other well-known fruit, we are faced with thousands of             choices.         

                 

function displayStyles() {
                var newElem = document.createElement(“table”);
                newElem.setAttribute(“border”, “1”);

var style = document.styleSheets[0].cssRules[0].style;

for (var i = 0; i < style.length; i++) {
                    addRow(newElem, style[i], style.getPropertyPriority(style[i]));
                }
                placeholder.appendChild(newElem);                
            }

function addRow(elem, header, value) {
                elem.innerHTML += “” + header + “:”
                    +  value + “”;
            }
        
    

`

getPropertyPriority方法为高优先级值返回important,如果没有指定重要性,则返回空字符串("")。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 提示你可以使用setProperty方法来指定一个值是否重要。我在本章前面演示setProperty方法时省略了重要性参数,但是如果您希望将!important应用于一个值,那么请将important指定为setProperty方法的第三个参数。

使用细粒度的 CSS DOM 对象

通过枚举样式中的属性并使用getPropertyValue方法,您可以发现哪些属性已经被使用。但是,您仍然需要对每个属性有所了解才能使用它。例如,您必须知道width属性的值表示为长度,而animation-delay属性的值表示为时间跨度。

在某些情况下,您不希望提前了解这些知识,因此您可以使用CSSStyleDeclaration.getPropertyCSSValue方法来获取代表为样式中的每个属性定义的值的CSSPrimitiveValue对象。表 29-8 描述了CSSPrimitiveValue对象的成员。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

CSSPrimitiveValue对象的关键是primitiveType属性,它告诉您属性值的单位。定义的单元类型集合如表 29-9 所示。这些对应于我在第四章中描述的 CSS 单元。

清单 29-10 显示了如何使用这个对象来确定 CSS 属性值的单位数量和单位类型。

清单 29-10。使用 CSSPrimitiveValue 对象

`

              Example                           ` `                   

There are lots of different kinds of fruit - there are over             500 varieties of banana alone. By the time we add the countless types of             apples, oranges, and other well-known fruit, we are faced with thousands of             choices.         

                 

function displayStyles() {
                var newElem = document.createElement(“table”);
                newElem.setAttribute(“border”, “1”);

var style = document.styleSheets[0].cssRules[0].style;

for (var i = 0; i < style.length; i++) {
                    var val = style.getPropertyCSSValue(style[i]);

if (val.primitiveType == CSSPrimitiveValue.CSS_PX) {
                        addRow(newElem, style[i],
                               val.getFloatValue(CSSPrimitiveValue.CSS_PX), “pixels”);
                        addRow(newElem, style[i],
                               val.getFloatValue(CSSPrimitiveValue.CSS_PT), “points”);
                        addRow(newElem, style[i],
                               val.getFloatValue(CSSPrimitiveValue.CSS_IN), “inches”);
                    } else if (val.primitiveType == CSSPrimitiveValue.CSS_RGBCOLOR) {
                        var color = val.getRGBColorValue();
                        addRow(newElem, style[i], color.red.cssText + " "
                               + color.green.cssText + " "
                               + color.blue.cssText, “(color)”);
                    } else {
                        addRow(newElem, style[i], val.cssText, “(other)”);
                    }
                }
                placeholder.appendChild(newElem);                
            }

function addRow(elem, header, value, units) {                 elem.innerHTML += “” + header + “:”
                    +  value + “” + units + “”;
            }
        
    

`

对象最有用的特性之一是它可以在一个单位和另一个单位之间转换。在清单 29-10 中,脚本识别以像素表示的值,并请求以磅和英寸表示的相同值。这意味着您可以使用适合您的单位值,而不是最初表示的单位值。

注意,颜色值是通过GetRGBColorValue方法获得的,该方法返回一个RGBColor对象。这个对象有三个属性(红色、绿色和蓝色),每个属性返回自己的CSSPrimitiveValue对象。你可以在图 29-10 中看到浏览器如何处理单元类型。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 29-10。使用 CSSPrimtiveValue 对象

使用计算样式

到目前为止,本章中的所有例子都集中在样式表或style属性中为 CSS 属性指定的值上。这对于确定文档中直接包含的内容是有用的,但是正如我在第四章中解释的,浏览器汇集了许多来源的样式,以确定应该使用哪些值来显示一个元素。这些属性包括您没有明确指定值的属性,因为这些值是继承的,或者是浏览器样式约定的。

浏览器用来显示元素的 CSS 属性值集被称为计算样式。您可以使用document.defaultView.getComputedStyle方法获得一个包含元素计算样式的CSSStyleDeclaration对象。从此方法返回的对象包含浏览器用来显示元素的所有属性的详细信息,以及每个属性的值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 提示你不能通过从getComputedStyle方法得到的CSSStyleDeclaration对象来修改计算出的样式。相反,您必须修改样式表或直接通过元素的 style 属性应用属性,如本章前面所示。

清单 29-11 展示了如何使用一些计算的样式值。

清单 29-11。使用元素的计算样式

`

              Example                                                       

There are lots of different kinds of fruit - there are over             500 varieties of banana alone. By the time we add the countless types of             apples, oranges, and other well-known fruit, we are faced with thousands of             choices.         

                 

function displayStyles() {
                var newElem = document.createElement(“table”);
                newElem.setAttribute(“border”, “1”);

var targetElem = document.getElementById(“block1”);
                var style = document.defaultView.getComputedStyle(targetElem);
                addRow(newElem, “Property Count”, style.length);
                addRow(newElem, “margin-top”, style.getPropertyValue(“margin-top”));
                addRow(newElem, “font-size”, style.getPropertyValue(“font-size”));
                addRow(newElem, “font-family”, style.getPropertyValue(“font-family”));

placeholder.appendChild(newElem);             }

function addRow(elem, header, value) {
                elem.innerHTML += “” + header + “:”
                    +  value + “”;
            }
        
    

`

在这个例子中,我显示了一些我没有明确定义值的属性的值。你可以在图 29-11 中看到效果。您也可以看到为什么我只显示了几个属性。表中的第一行报告了计算样式中有多少特性。不同浏览器的数字不同,但 Chrome 报告的 223 是典型的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 29-11。使用计算样式

总结

在这一章中,我向你展示了使用 DOM 操作 HTML 文档中 CSS 属性和值的不同方法。您可以通过样式表或单个元素的样式属性来工作,并且可以使用大量的对象集合来深入挖掘样式的细节。不仅可以使用显式定义的属性和值,还可以使用浏览器用来显示元素的计算样式。这允许您将您定义的内容与实际使用的内容进行比较。

转载请注明出处或者链接地址:https://www.qianduange.cn//article/16304.html
标签
VKDoc
评论
发布的文章

在线OJ项目

2024-08-27 21:08:43

JSON2YOLO 项目教程

2024-08-27 21:08:42

Redis 实现哨兵模式

2024-08-27 21:08:22

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!