黄子涵

第11章 客户端JavaScript实践

绩效

章节 代码量(行)
11.1 303

11.1 样式

对样式进行操作指的是对页面的外观进行操作,而不是页面的内容。只要设计出合适的样式,就能够创建出易读、清晰、美观的 Web 应用程序。话虽如此,易读和美观主要由设计师负责,属于静态设计的范畴,需要 CSS 方面的知识。而借助 JavaScript,则能够动态地变更样式,增强页面的可读性。

JavaScript 实现了动态样式变更,其目的是为用户提供视觉反馈。例如,针对可被点击的元素,如果在鼠标指针移动至该元素上方时改变其图标,并改变 DOM 元素的颜色,就能够向用户传达该元素可被点击的信息。当鼠标指针位于 DOM 元素之上时,将触发 mouseover 事件。只需准备一个能够改变鼠标指针以及 DOM 元素色彩的事件侦听器,并将其注册至该事件,就能够实现这一效果。

11.1.1 样式的变更方法

可以通过以下这些方法对样式进行变更。

  • 通过 className 属性更改 class 名
  • 通过 classList 属性更改 class 名
  • 更改 style 属性
  • 直接更改样式表
    以上任意一种方法都能够实现样式的变更。根据不同的用途选择合适的方法即可。
通过 className 属性更改 class 名

通过更改 DOM 元素的 class 名来改变样式是一种最为简单的做法。即事先通过 CSS 定义好对应于更改前与更改好的 class 名的样式,并在JavaScript 中替换 class 名。可以通过 className 属性设定 class 名,如代码清单 11.1 所示。在这个例子中,当点击时,字符的颜色与背景色将会调换。

代码清单 11.1 通过 className 属性更改 class 名
<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>通过 className 属性更改 class 名</title>
    <style>
        .hzh1 {
            color: red;
        }

        .hzh2 {
            color: orange;
        }

        .hzh3 {
            color: yellow;
        }

        .hzh4 {
            color: green;
        }

        .hzh5 {
            color: cyan;
        }

        .hzh6 {
            color: purple;
        }

        .hzh7 {
            color: blue;
        }

        .hzh8 {
            color: limegreen;
        }

        .hzh9 {
            color: palevioletred;
        }

        .hzh0 {
            color: dodgerblue;
        }
    </style>
</head>

<body>
    <h1 id="hzh" class="hzh1">点击这个会切换文字颜色</h1>

    <script>
        var hzh = document.getElementById('hzh');

        hzh.onclick = function huangzihan() {
            this.className = ("hzh" + Math.floor(Math.random()*10))
            console.log("在控制台输出目前的类名:");
            console.log("hzh" + Math.floor(Math.random()*10));
        }
    </script>
</body>

</html>

image

在更改 class 名时应该注意并清楚了解的是,如果更改了 class 名,到底有哪些元素将会受到影响。例如对于代码清单 11.2 的情况,如果更改了 1 个元素的 class 名,其相邻元素与子孙元素的样式也会一起改变。这时,如果需要改变的元素过多,则可能出现性能上的问题。不过,如果要考虑性能的话,指定了相邻元素与子元素的CSS 的写法本身就是有问题的。最好对这些元素分别设定不同的 class 名并进行式样操作。

代码清单 11.2 在更改 class 名后相关元素的样式也会改变
<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>在更改 class 名后相关元素的样式也会改变</title>
    <style>
        .hzh-before {
            background-color: white;
            color: black;
        }
        .hzh-before p {
            text-decoration: none;
        }
        .hzh-before + div {
            text-decoration: none;
        }
        .hzh-after {
            background-color: black;
            color: white;
        }
        .hzh-after p {
            text-decoration: underline;
        }
        .hzh-after + div {
            text-decoration: line-through;
        }
    </style>
</head>

<body>
    <div id="hzh" class="hzh-before">
        <p>hzh1</p>
        <p>hzh2</p>
        <p>hzh3</p>
        <p>hzh4</p>
    </div>
    <div>
        黄子涵
    </div>
    <script>
        var hzh = document.getElementById('hzh');
        hzh.onclick = function toggleStyle() {
            this.className = (this.className === 'hzh-before') ? 'hzh-after' : 'hzh-before'; 
        };
    </script>
</body>

</html>

image

通过 classList 属性更改 class 名

还可以通过对在 HTML5 中新增的 classList 属性进行操作,来更改 class 名。与通过对 className 属性进行操作以更改 class 名相比,这种方法更加容易理解(代码清单 11.3)

代码清单 11.3 在更改 class 名后相关元素的样式也会改变
<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>在更改 class 名后相关元素的样式也会改变</title>
    <style>
        .hzh-before {
            background-color: white;
            color: black;
        }
        .hzh-before p {
            text-decoration: none;
        }
        .hzh-before + div {
            text-decoration: none;
        }
        .hzh-after {
            background-color: black;
            color: white;
        }
        .hzh-after p {
            text-decoration: underline;
        }
        .hzh-after + div {
            text-decoration: line-through;
        }
    </style>
</head>

<body>
    <div id="hzh" class="hzh-before">
        <p>hzh1</p>
        <p>hzh2</p>
        <p>hzh3</p>
        <p>hzh4</p>
    </div>
    <div>
        黄子涵
    </div>
    <script>
        var hzh = document.getElementById('hzh');
        hzh.onclick = function toggleStyle() {
            this.classList.toggle('hzh-before');
            this.classList.toggle('hzh-after');
        };
    </script>
</body>

</html>

image

表 11.1 总结了可以使用 classList 属性的方法。classList 属性是一种对 DOM TokenList 接口的实现。

表 11.1 可以使用 classList 属性的方法
方法名 说明
contains(clazz) 判断在 class 名中是否含有 clazz
add(clazz) 向 class 名中添加 clazz
remove(clazz) 从 class 名中删除 clazz
toggle(clazz) 如果在 class 名中含有 clazz 则将它删除,否则向 class 名中添加 clazz
更改 style 属性

可以直接更改 DOM 元素的 style 属性的值以实现样式的更改。这与更改 class 名的情况不同,样式更改的范围被明确地限定于这个元素。另外,通过 style 属性指定的内容的应用优先级将仅次于 CSS 中被标记为 !important 的元素。

style 属性中的各个属性名是由在 CSS 中所指定的属性名演变而来的,其更改之处为去除了属性名中的连字符(-)并将连字符之后的字母改为了大写形式。例如,对于 CSS 中的 margin-top 属性,在 JavaScript 中则可以通过 marginTop 的名称使用。之所以要将连字符去除,是因为连字符在 JavaScript 将被识别为减号。此外,由于 float 在 JavaScript 中是作为保留字使用的,因此 float 属性不能直接使用。如果要在 JavaScript 中更改 float 属性,则需要通过 cssFloat 属性来进行操作。

在代码清单 11.4 中是一个更改 style 属性更改的例子。

代码清单 11.4 更改 style 属性
<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>更改style属性</title>
    <style>
        #hzh {
            background-color: white;
            color: black;
        }
    </style>
</head>

<body>
    <div id="hzh">黄子涵。请点击这里。</div>
    <div id="huangzihan">黄子涵是帅哥!</div>
    <script>
        var hzh = document.getElementById('hzh');
        hzh.onclick = function toggleStyle() {
            var style = this.style;
            if(!style.cssFloat) {
                style.cssFloat = 'left';
                style.backgroundColor = 'black';
                style.color = 'white';
            } else {
                style.cssFloat = '';
                style.backgroundColor = 'white';
                style.color = 'black';
            } 
        };
    </script>
</body>

</html>

image

直接更改样式表

还可以直接对是否应用样式表进行设定。如果将 link 元素与 style 元素的 disabled 属性设为 true,相应的样式表就将被禁用(代码清单11.5)。

代码清单 11.5 直接更改样式表
<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>直接更改样式表</title>
    <link rel="stylesheet" type="text/css" href="hzh1.css" id="hzh1" disabled="true">
    <link rel="stylesheet" type="text/css" href="hzh2.css" id="hzh2" disabled="true">
    <style id="hzh3" disabled="true">
        #hzh {
            background-color: #999;
        }
    </style>
    <script>
        function change(id, enable) {
            // 在勾选了复选框之后启用样式
            document.getElementById(id).disabled = !enable;
        }
        window.addEventListener('load', function () {
            // 在初始化处理中禁用所有样式
            var style = document.styleSheets;
            for (var i = 0; i < style.length; i++) {
                style[i].disabled = true;
            }
        }, false);
    </script>
</head>

<body>
    <div id="hzh">黄子涵</div>

    <input type="checkbox" onchange="change('hzh1', this.checked)">
    <input type="checkbox" onchange="change('hzh2', this.checked)">
    <input type="checkbox" onchange="change('hzh3', this.checked)">
</body>

</html>

<!-- 
    /* hzh1.css的内容*/
    #hzh {
         font-size: x-large;
    }
    /* hzh2.css的内容*/
    #hzh {
         text-decoration: underline;
    }
-->

image

需要更改整个页面的样式时,就可以采用这种切换样式表启用禁用状态的方式。例如,对于已经事先准备了一些样式主题,需要让用户选择自己喜欢的主题以显示页面内容的情况,就可以通过这种方式实现。

如果不需要切换整个主题,则没有必要专门通过启用或禁用样式表来实现样式的更改。直接更改 class 名,或更改 style 属性的值即可。

11.1.2 位置的设定

在更改样式时,诸如文字大小或背景颜色等方面的更改操作理解起来并不难,但如果要更改 DOM 元素的位置,则必须知道更多的信息。学会如何将 DOM 元素设置于任意的位置,将有助于实现复杂的网页布局。

在设定位置时,最为关键的两点是 position 属性与鼠标指针的位置。另外则是要对如何处理 DOM 元素的宽度和高度有所了解。

position 属性

position 属性可以被指定为以下几种值之一。
• static    • fixed    • absolute    •relative

如果在此设定了 static 以外的值,就能够设置 top、bottom、left、right 属性。

static

position 属性的默认值。将根据 HTML 中所写的标签来决定元素的配置。这种情况下无法通过 top 或 left 之类的属性来指定元素的位置。

fixed

如果将 position 属性指定为 fixed,则将会以浏览器窗口为基准来确定元素的相对位置。由于是相对于浏览器窗口的位置,因此即使对页面进行了滚动操作,元素在画面上的位置也不会发生改变。也就是说元素将始终保持在同一位置。

该值无法在 Internet Explorer 6 中使用。

absolute

如果将 position 属性指定为了 absolute,则可以对该元素与含有该元素的元素之间的相对位置进行设定。通常情况下都是元素与 body 元素之间的相对位置,不过如果嵌套了非 static 值的其他元素,则将以该元素为基准确定相对位置。

relative

如果将 position 属性指定为了 relative,则会在根据 HTML 中所写的标签进行配置的基础上,对元素的相对位置进行设置。

不过,如果已经指定了 relative 值,一般也就不会再设置 top 或 left 之类的值了。通常的做法是,以被设定为 absolute 的元素为标准来进行位置设定。根据对 absolute 的说明可以知道,被设定为 absolute 属性的元素,是以含有该元素的元素中 position 属性没有被设定为 static 的元素为基准来确定位置的。而被设定为 relative 的元素的位置与其被设定为 static 时的情况并没有什么不同,将会被动态地置于正确的位置。这样一来,就能够按照期望的那样,根据其与被设定为 relative 值的元素的相对位置来配置该元素的位置。

11.1.3 位置

如果要实现在鼠标点击位置附近显示一个框这样的效果,则必须要知道点击位置。在 MouseEvent 被触发时,相应的 Event 对象提供了多个属性来获取这一位置。这时的问题在于,需要知道点击的位置是以什么为基准来表示的。

function onclick(event) {
         // 通过 event 对象获取鼠标指针的位置,并进行相应的处理
}
屏幕坐标

可以通过 screenX 和 screenY 属性来获取屏幕坐标。屏幕坐标是一种以计算机显示器的左上角为原点的坐标系。不过由于这只是屏幕上的位置坐标,因此能够对其进行有效利用的情况很少。

窗口坐标

可以通过 clientX 和 clientY 属性来获取窗口坐标。窗口坐标是一种以浏览器的显示范围的左上角为原点的坐标系。这一坐标系与文档、元素的滚动情况无关,其坐标值仅由内容的显示位置决定。

文档坐标

可以通过 pageX 和 pageY 属性来获取元素在文档中的位置。文档坐标是一种以文档页面的左上角为原点的坐标系。与窗口坐标不同,其坐标值与显示位置无关,是由元素在整个文档中的位置决定的。

DOM 对 screenX 与 clientX 进行了定义,但没有定义 pageX。这一坐标系是由浏览器自定义实现的。Internet Explorer 8 以及更早版本无法使用这一坐标。

在特定元素内的相对坐标

通过 layerX 与 layerY,或 offsetX 与 offsetY 属性,可以获取触发了事件的元素内的相对坐标。这些属性没有在 DOM 中被定义,是由浏览器自定义实现的功能。

Element.getBoundingClientRect() 虽然不能直接取得相对坐标,但也能实现类似的功能。该方法是在 CSSOMView Module 这一与文档显示方式有关的标准中被定义的。通过使 getBoundingClientRect(),能够获取元素范围信息的窗口坐标。这里的范围信息指的是,距离左侧的距离(left)、距离上方的距离(top)、宽度(width)与高度(height)。可以像代码清单 11.6 中这样,将这些值与 clientX 和 clientY 结合使用以获取元素内的相对坐标。

而代码清单 11.7 是一个完整的例子,它将在点击的位置显示元素。

代码清单 11.6 在元素内取得相对坐标
function onclick(event) {
    var hzhX = event.clientX;                        // 窗口坐标系中鼠标指针的x坐标
    var hzhY = event.clientY;                        // 窗口坐标系中鼠标指针的y坐标
    var hzhR = event.target.getBoundingClientRect(); // 窗口坐标系中被点击元素的范围信息    
    hzhX -= hzhR.left;                               // 鼠标指针在被点击元素内部的x坐标
    hzhY -= hzhR.top;                                // 鼠标指针在被点击元素内部的y坐标 
}
代码清单 11.7 一个在点击位置显示元素的例子
<!DOCTYPE html>
<html lang="zh">

<head>
    <title>一个在点击位置显示元素的例子</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
    <div id="hzh" style="width: 2000px; height: 2000px; position: relative;">
        <div id="message" style="position: absolute; background: lightgray; width: 100px;">黄子涵</div>
    </div>
    <script>
        var hzh = document.getElementById('hzh');
        function getPosition(event) {
            var hzhX = event.clientX;                        // 窗口坐标系中鼠标指针的x坐标
            var hzhY = event.clientY;                        // 窗口坐标系中鼠标指针的y坐标
            var hzhR = event.target.getBoundingClientRect(); // 窗口坐标系中被点击元素的范围信息    
            hzhX -= hzhR.left;                               // 鼠标指针在被点击元素内部的x坐标
            hzhY -= hzhR.top;                                // 鼠标指针在被点击元素内部的y坐标 
            return { x: hzhX, y: hzhY };
        }
        hzh.addEventListener('click', function (event) {
            var message = document.getElementById('message');
            if (event.target === message) {
                // 如果点击 message, 则不进行操作
                return;
            }
            var hzhPos = getPosition(event);
            message.style.left = hzhPos.hzhX;
            message.style.top = hzhPos.hzhY;
        }, false);
    </script>
</body>

</html>

这里暂时没有做出效果,标记一下。

11.2 AJAX

AJAX 是 Asynchronous JavaScript + XML 的简称。AJAX 一词的实际含义为“不发生页面跳转、异步载入内容并改写页面内容的技术”。在实际操作中,AJAX 不仅仅会使用 XML 数据,很多时候也会对 JSON 或纯文本进行操作。AJAX这个词是由 Jesse James Garrett 在 2005 年命名的。不过,在那之前就已经有使用 AJAX 的网站。众所周知,Google 的 Gmail 就是一个利用了AJAX 的优秀范例。如今,AJAX 正在全世界范围内迅速地得到普及。

异步处理的优点

AJAX 的关键在于它是以异步的方式执行的。异步处理的优点是不会让用户白白等待。对于同步处理来说,在处理完来自服务器的响应之前,用户无法进行任何其他操作,只能等待。如果服务器的响应发生了延迟,会让用户误以为页面失去了响应。在优先考虑用户体验时,与同步处理相比,采用异步处理的方式更为合适,这一点是显而易见的。JavaScript 是一种事件驱动程序设计语言,在很多地方都会用到异步处理,所以要理解异步处理并不是一件难事。

XMLHttpRequest

如果要通过 JavaScript 动态地向服务器发送请求,则需要使用 XMLHttpRequest 对象。不过在这里要稍微提醒一下大家,XMLHttpRequest 目前尚未被制定为标准。之所以说是“稍微”,是因为那些现代的浏览器自然不必多说,即使是Internet Explorer 也在 7 以及之后的版本里全都使用了通用的 API,因此它已经成为了一种事实标准。考虑到这点,尚未标准化也就不是什么问题了。问题在于 Internet Explorer 6。不过它也仅仅是在对象的创建方式上有所不同,对象所含有的方法与其他浏览器中的实现是通用的。

基本的处理流程

XMLHttpRequest 对象的创建
发送请求
readyState的含义

同步通信

超时

响应

跨源限制

所谓跨源限制指的是,对源不同的通信进行限制。而这里的源指的是由 URL 的协议(http: 或 https: 等)、主机名、端口号所构成的元素。在 Web 领域,为了确保安全性,只有同源的通信才能被允许进行,这称为同源策略。

虽然可以在 HTML 中使用 iframe 以实现在一个页面中同时显示来自不同域的文档,不过 JavaScript 仍然只能访问同一个源的文档。如果文档的 URL 和 iframe 的不同,则无法通过文档中所包含的 JavaScript 对 iframe 内的 DOM 进行操作,而 iframe 内的 JavaScript 也无法操作文档中的 DOM。如果不这样,就会发生诸如不同域的 Cookie 能够相互访问等安全问题。

对于 XMLHttpRequest 来说,同源策略的含义是,一个 XMLHttpRequest 对象只能发送至一个特定的服务器,即提供了使用该 XMLHttpRequest 对象的文档的下载的那个服务器。不过,只要让服务器转发该请求,就能够将请求发送至不同域的服务器。

跨源通信

JSONP

iframe攻击(iframe hack)

window.postMessage

跨源通信的安全问题

11.3 表单

表单元素

表单控件

内容验证

内容验证的必要性
进行内容验证的时机

可用于验证的事件

submit
focus、blur
change
keydown、keyup、keypress
input

使用表单而不产生页面跳转的方法

form的target属性
posted @ 2022-05-15 12:19  黄子涵  阅读(8)  评论(0编辑  收藏  举报