css补充

这里再补充几个css的知识点,下面会用到

最小宽度-min-width

设置元素的最小宽度。
举例说明,比如设置一个百分比的宽度,那么元素的宽度的像素值是会随着页面大小而变化的。如果设置一个最小的像素宽度,那么当变化到最小值之后,不会继续变小。在下面的例子中,会出现滚动条,保证元素的宽度:

<body>
<div style="height: 80px;width: 100%;background-color: blue;min-width: 800px;"></div>
<!--下面的情况是:本来正好能放一排的,但是狂赌不够后放不下了-->
<div style="height: 80px;width: 20%;background-color: yellow;float: left"></div>
<div style="height: 80px;width: 80%;background-color: red;float: right;min-width: 600px;"></div>
</body>

另外,min-width 的值可以是像素,也可以是百分比。

圆形边框-border-radius

这个属性是为元素添加一个圆角边框。值可以是像素,也可以是百分比。
如果把值设为50%,再保证高和宽一样,就能做出一个圆形的边框,放在img标签中,就能做出一个圆形的头像:

<img src="1.jpg" style="border-radius: 50%;height: 80px;width: 80px;" />

:hover进阶用法

之前已经讲过:hover,这里要补充的是,不但可以改变自己标签的属性,也可以通过选择器的语法来改变其他标签的属性:

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .father{
            height: 100px;
            background-color: yellow;
        }
        .son{
            height: 50%;
        }
        .father:hover{
            color: red;
        }
        .father:hover .special{
            background-color: blue;
            color: white;
        }
    </style>
</head>
<body>
<div class="father">
    <div class="son special">字体背景都变色</div>
    <div class="son">字体变色</div>
</div>
</body>

还没完,如果我们改变的是display属性,那么不需要js,仅用css也可以实现鼠标悬停展开菜单的功能:

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .item .header{
            height: 35px;
            background-color: blue;
            color: white;
            line-height: 35px;
        }
        .item .content{
            display: none;
        }
        .item:hover .content{
            display: block;
        }
    </style>
</head>
<body>
<div class="item">
    <div class="header">菜单</div>
    <div class="content">
        <div>内容一</div>
        <div>内容二</div>
        <div>内容三</div>
    </div>
</div>
</body>

后台管理页面布局

一般布局的时候,先把页面分成头部(pg-header)、中间(pg-content)、底部(pg-footer)三个部分。主要的内容周集中在中间。现在应该已经掌握了一种形式的布局,就是商城页面。之前没有把布局的问题单独拿出来讲,这节以后台管理页面举例,讲解布局的问题和实现。

回顾商城页面的布局

之前学习和作业都是以商城为例子。布局大概是这个样子的:都是从上到下布局,给页面设置一个宽度,然后所有内容都是居中显示。

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        body{
            margin: 0;
        }
        .w{
            width: 1000px;
            margin: 0 auto;
        }
        .pg-header, .pg-footer{
            height: 48px;
            line-height: 48px;
            background-color: black;
            color: white;
        }
        .left{
            float: left;
        }
        .right{
            float: right;
        }
        .content1{
            height: 100px;
            width: 160px;
            background-color: gold;
        }
        .content2{
            width: 840px;
            background-color: yellow;
        }
        .goods{
            height: 200px;
            width: 200px;
            border: 1px solid black;
            margin: 4px;
        }
    </style>
</head>
<body>
<div class="pg-header">
    <div class="w">
        <div style="">
            这里放头部的内容
        </div>
    </div>
</div>
<div class="pg-content">
    <div class="w">
        <div class="content1 left">这里放中间的内容</div>
        <div class="content2 right">
            <div class="goods left">商品信息</div>
            <div class="goods left">商品信息</div>
            <div class="goods left">商品信息</div>
            <div class="goods left">商品信息</div>
            <div class="goods left">商品信息</div>
            <div class="goods left">商品信息</div>
            <div class="goods left">商品信息</div>
            <div class="goods left">商品信息</div>
        </div>
        <div style="clear: both"></div>
    </div>
</div>
<div class="pg-footer">
    <div class="w">
        <div>
            这里放底部的内容
        </div>
    </div>
</div>
</body>

布局-fixed

常见的后台管理页面的布局,同样有头部、中继、底部。但是菜单一般显示在中间的最左侧(即固定在左边),而中间的右侧则是显示的主要内容:

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        body{
            margin: 0;
        }
        .pg-header{
            height: 48px;
            background-color: red;
        }
        .pg-content .menu{
            position: fixed;
            top: 48px;
            left: 0;
            bottom: 30px;
            width: 200px;
            background-color: blue;
            color: white;
        }
        .pg-content .content{
            position: fixed;
            top: 48px;
            right: 0;
            bottom: 30px;
            left: 200px;
            background-color: yellow;
            overflow: auto;
        }
        .pg-footer{
            position: fixed;
            height: 30px;
            right: 0;
            left: 0;
            bottom: 0;
            background-color: purple;
        }
    </style>
</head>
<body>
<div class="pg-header"></div>
<div class="pg-content">
    <div class="menu">这里是菜单</div>
    <div class="content">
        <script>
            for (var i=0; i<100; i++){
                document.writeln("<p>test</p>")
            }
        </script>
    </div>
</div>
<div class="pg-footer"></div>
</body>

一般内容会比较多,往往一屏显示不完。这里就填入了很多内容,为了可以看到所有的内容,需要设置overflow属性,可以用滚轮翻页。
这里只是抛砖引玉,不建议这么实现。现在来看看后面absolute的实现方法。

布局-absolute

还是上面的样子,差不多的代码,这次用absolute来实现。不设置relative,这里会将元素定位在屏幕的某个位置和fixed一样,但是滚动滚动屏幕的时候,元素会跟着上下移动。具体代码如下:

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        body{
            margin: 0;
        }
        .pg-header{
            height: 48px;
            background-color: red;
        }
        .pg-content .menu{
            position: absolute;
            top: 48px;
            left: 0;
            width: 200px;
            background-color: blue;
            color: white;
        }
        .pg-content .content{
            position: absolute;
            top: 48px;
            right: 0;
            left: 200px;
            background-color: yellow;
            /*overflow: auto;*/
            /*bottom: 0;*/
        }
    </style>
</head>
<body>
<div class="pg-header"></div>
<div class="pg-content">
    <div class="menu">
        <script>
            for (var i=0; i<10; i++){
                document.writeln("<p>菜单</p>")
            }
        </script>
    </div>
    <div class="content">
        <script>
            for (var i=0; i<100; i++){
                document.writeln("<p>test</p>")
            }
        </script>
    </div>
</div>
</body>

这个样式和上面的略有不同,头部和左侧会跟随滚轮移动。这里没写底部,并且暂时不好做能跟随滚轮移动的底部。
变化新样式:在 &lt;style&gt; 标签里,.pg-content .content 的样式里有两句被注释了,去掉注释后,就是设置overflow。效果是和上面一样的固定头部和左侧的样式了。
注意:可以再设置一个 min-width 属性,最小宽度。在你缩小窗口的时候,当动态调整的宽度小于这个属性时,元素的宽度就不会继续变小了,而是会出现滚动条。这可以有效的保证你的页面布局不会因为窗口大小的变化而混乱。

布局-补全头部

之前的头部没有任何内容,简单的在头部的左边放一个LOGO,这个没什么难度。然后再在右边放一个头像,头像使用圆形,然后鼠标悬停图片上会展开菜单。圆形图片和css实现鼠标悬停展开,在开头的css补充内容里可以参考:

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        body{
            margin: 0;
        }
        .pg-header{
            height: 48px;
            line-height: 48px;
            background-color: red;
        }
        .pg-header img{
            height: 48px;
        }
        .pg-header .logo{
            width: 200px;
            float: left;
        }
        .pg-header .user{
            float: right;
            background-color: pink;
            padding: 5px;
            margin-right: 62px;
        }
        .pg-header .user:hover .user_list{
            display: block;
        }
        .pg-header .user .profile_pic>img{
            height: 40px;
            width: 40px;
            border-radius: 50%;
        }
        .pg-header .user .user_list{
            z-index: 20;
            position: absolute;
            top: 48px;
            right: 30px;
            background-color: orange;
            width: 120px;
            text-align: center;
            display: none;
        }
        .pg-header .user .user_list>a{
            display: block;
        }
        .pg-header .user .user_list>a:hover{
            background-color: blue;
            color: white;
        }
    </style>
</head>
<body>
<div class="pg-header">
    <div class="logo">
        <a style="margin: 0 auto">
            <img src="1.jpg" />
        </a>
    </div>
    <!--设置user的div往右飘,上面的logo要往左飘,这样2个才能在一行-->
    <div class="user">
        <a class="profile_pic">
            <!--把头像设置成了圆形图片-->
            <img src="2.jpg">
        </a>
        <!--user_list设置了相对定位,和下面的pg-content重叠,要设置z-index-->
        <div class="user_list">
            <!--a标签要设置成块级标签,让它换行-->
            <a>我的收藏</a>
            <a>注销</a>
        </div>
    </div>
</div>
</body>

上面是头部的所有内容,可以和前面的页面内容拼接在一起,就是整个页面了。

使用图标-Font Awesome

如果要在页面中使用图标,推荐去 Font Awesome 找。这个是中文网: http://www.fontawesome.com.cn/

开始使用

.1 先去官网下载font-awesome压缩包
.2 解压文件夹,把 font-awesome 的整个文件夹复制到你的项目中:

Python自动化开发学习16-前端内容综合进阶

.3 在 &lt;head&gt; 处加载 font-awesome.min.css 如下:

<link rel="stylesheet" href="font-awesome-4.7.0/css/font-awesome.min.css">

使用图标

&lt;i&gt; 标签包裹起来,把图标用到你网站的任何地方:

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="font-awesome-4.7.0/css/font-awesome.min.css">
</head>
<body>
<ul class="fa-ul">
  <li><i class="fa-li fa fa-check-square"></i>使用列表类图标</li>
  <li><i class="fa-li fa fa-check-square"></i>轻松的替换</li>
  <li><i class="fa-li fa fa-spinner fa-spin"></i>无序列表</li>
  <li><i class="fa-li fa fa-square"></i>中的默认图标</li>
</ul>
<i class="fa fa-refresh fa-spin fa-3x fa-fw"></i>
<div>
  <i class="fa fa-envelope-o fa-fw" aria-hidden="true"></i>
  <input class="form-control" type="text" placeholder="您的邮箱地址">
</div>
<div>
  <i class="fa fa-key fa-fw" aria-hidden="true"></i>
  <input class="form-control" type="password" placeholder="请输入密码">
</div>
</body>

要用图标的话,就上网站去找,然后把代码复制下来就好。上面还有更多使用案例。

JavaScript 进阶

switch 语句

这是另外一种条件语句:

switch(n)
{
case 1:
  执行代码块 1
  break;
case 2:
  执行代码块 2
  break;
default:
  都不配时才执行的代码块
}

break: 每个 case 最后,请使用 break 来阻止代码自动地向下一个 case 运行。
default:使用 default 关键词来规定匹配不存在时执行的代码。

函数(function)

普通函数

函数就是在大括号中的代码块,前面使用了关键词 function:

function myFunction(var1,var2)
{
这里是要执行的代码
}

匿名函数

就是没有函数名的函数,普通函数去掉函数名就是匿名函数了。只能被调用一次,比如定时器(setInterval),第一个参数是要执行的代码,就可以使用匿名函数。

function (var1,var2)
{
这里是要执行的代码
}

自执行函数

自执行函数是用小括号将匿名函数包起来,然后加小括号(传参)立即执行。因为函数无名字,实现了作用域的绝对隔离和函数名的冲突问题。基本形式如下:

(function (var1,var2)
{
这里是要执行的代码
})(var1,var2);

普通的函数,定义完之后暂时不执行,之后通过函数名加小括号调用执行。自执行函数,定义完之后紧接着是一个小括号,马上调用执行。

序列化和反序列化

语法如下:

  • JSON.stringify(obj) :序列化,将对象的状态转换为字符串
  • JSON.parse(str) :反序列化
> JSON.stringify([1,2,3,4,5])  // 序列化
< "[1,2,3,4,5]"
> JSON.parse("[1,2,3,4,5]")  // 反序列化
< [object Array]: [1, 2, 3, 4, 5]
>

转义

转义url
encodeURI() :把字符串作为 URI 进行编码
decodeURI() :对 encodeURI() 函数编码过的 URI 进行解码
encodeURIComponent() :把字符串作为 URI 组件进行编码
decodeURIComponent() :对 encodeURIComponent() 函数编码的 URI 进行解码

> url = "https://www.baidu.com/s?wd=前端"
< "https://www.baidu.com/s?wd=前端"
> url2 = encodeURI(url)
< "https://www.baidu.com/s?wd=%E5%89%8D%E7%AB%AF"
> decodeURI(url2)
< "https://www.baidu.com/s?wd=前端"
> url3 = encodeURIComponent(url)
< "https%3A%2F%2Fwww.baidu.com%2Fs%3Fwd%3D%E5%89%8D%E7%AB%AF"
> decodeURIComponent(url3)
< "https://www.baidu.com/s?wd=前端"

用上面的四个方法就好了,下面两个知道一下。
escape() :对字符串进行编码,这样就可以在所有的计算机上读取该字符串
unescape() :对通过 escape() 编码的字符串进行解码
注意:所有的方法不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII 标点符号进行编码: - _ . ! ~ * ' ( )

eval-执行字符串

eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。语法:

eval(string)

参数是字符串形式的表达式或代码,并且可以有返回值。

作用域

一、js默认是以函数作为作用域的(不考虑let)。

python中也是以函数作为作用域。
在其他语言里,有的以代码块作为作用域。其实js也可以,通过使用let。

二、js函数的作用域,在函数未被调用之前就已经创建。

结合下面的第三条一起理解。

三、函数的作用域存在作用域链,并且也是在被调用之前创建。

这个要在函数里套用函数的情况下会产生这个问题

<script>
    var name = 'Adam';
    function func() {
        var name = 'Bob';
        function inner() {
            var name = 'Cara';
            alert(name)
        }
        return inner
    }
    var ret = func();
    ret()
</script>

上面的函数,执行的结果是Cara;如果把Cara注释掉,结果是Bob;如果把Bob再注释掉,则结果是Adam。这就是作用域链。而且由于作用域是调用之前创建的,所以和在哪里调用这个函数没有关系。虽然是在最外部调用的函数,但是作用域依然是定义它时的位置,即作用域是 inner 内部。

四、js函数内的变量提前声明

直接看例子说明:

<script>
    function func() {
        alert(name);
        var name = 'Adam';
    }
    func()
</script>

name的值是调用之后才赋值的,所以alert并不能取到后面的值。但是在alert之前name变量就已经创建好了,相当于在alert之前有一句 var name; 。虽然按顺序看定义变量是也是在alert后面,然而实际上是在执行代码之前就完成了所有变量的声明。一个未赋值的变量的值是 undefined ,运行的结果就是这个值。如果注释掉赋值语句,直接取一个不存在的变量,结果会报错(一般浏览器应该会捕获这个错误,但是至少结果是不一样的)。

支持面向对象

js没有类,js可以用函数作类,定义一个类和定义一个函数一样。

<script>
    // 定义类,其实这个就是构造函数
    function Foo(name) {
        this.name = name
    }
    var obj = new Foo('Barry');  // 创建对象
    var obj2 = new Foo('Oliver');  // 创建另一个对象
    alert(obj.name);  // 取对象的属性
</script>

关于this,在构造函数中的this指向新创建的对象本身。

原型

类有属性还有方法,属性有了,还要方法。既然Foo是构造函数,显然不能再Foo里定义类的方法。这里要引入原型,方法定义在原型里,Foo.prototype 是 Foo 的原型:

<script>
    // 定义类,其实这个是构造函数
    function Foo(name) {
        this.name = name
    }
    // 定义方法的方式,里面是字典的形式,貌似可以在里面定义多个方法
    Foo.prototype = {
        'sayName': function () {
            alert(this.name)
        }
    };
    var obj = new Foo('Barry');  // 创建对象
    obj.sayName();  // 调用对象的方法
</script>

结合python理解

把这里的概念和之前学习过的python的面向对象的概念比较一下:

  • 函数名就是类名
  • new 类名() ,创建对象,就是实例化。
  • 函数本身就是构造函数,函数中的this,就是构造函数中的self
  • 类名.prototype 是原型,可以理解为就是类。里面定义的函数就是类里定义的方法。

DOM 和 JavaScript 的补充内容

input示例

做一个input的输入框,默认在框内先填上提示内容。当用户选择了input框后,情况框内的文本,让用户输入。
这里要用到2个新的事件,onfocus 和 onblur。这里的事件不能用onclick,因为用户有多种方式可以选定输入框,比如鼠标点击,还有用Tab切换等等。要响应这类事件,使用 onfocus 。相反,如果用户选定后离开,则触发事件 onblur
onfocus 事件 :在获得焦点时触发
onblur 事件 :在失去焦点时触发

<body>
<div>
    <label for="keyword">关键字:</label>
    <input id="keyword" type="text" value="请输入关键字" onfocus="Focus()" onblur="Blur()">
</div>
<script>
    // 获得焦点时,确认里面是否是默认的value;是则清空
    function Focus() {
        var obj = document.getElementById('keyword');
        if(obj.value === "请输入关键字"){
            obj.value = ''
        }
    }
    // 失去焦点时,确认内容是否为空;是则变回默认的value
    function Blur() {
        var obj = document.getElementById('keyword');
        if(obj.value.length === 0){
            obj.value = "请输入关键字"
        }
    }
</script>
</body>

另一种实现方法
在的html5中提供了一个新的属性(placeholder),简单的设置这个属性就能完成上面的效果。前提是客户端的浏览器支持html5。

<body>
<div>
    <label for="keyword">关键字:</label>
    <input id="keyword" type="text" placeholder="请输入关键字">
</div>
</body>

样式操作

之前已经用过通过修改标签的 class 属性来达到修改样式的效果,直接添加或去除 class 来获取或去除某个 class 的样式。
这里通过DOM获取到对象后,直接修改对象的 style 属性,更加直接。

<body>
<span id="i1" style="font-size: 16px;">字体大小</span>
<script>
    alert("点击后修改字体大小");
    obj = document.getElementById('i1');
    obj.style.fontSize = '48px';
</script>
</body>

这里要注意一下style在标签中和js中的写法是不一样的。

  • 样式名称的分隔符,中横杠(-)去掉了,在js里是小驼峰的形式
  • 赋值不再用冒号(:),在js里使用等号(=)

属性操作

属性操作,有下面这些方法:
obj.setAttribute(key, value) :设置标签属性
obj.getAttribute(key) :获取标签属性的值
obj.hasAttribute(key) :检查标签是否存在该属性
obj.removeAttribute(key) :去除标签属性
obj.attributes() :获取标签所有的属性

标签操作

创建标签

方法一:通过对象来创建
document.createElement() :创建一个标签对象

> var tag = document.createElement('span')  // 创建一个span标签
< undefined
> tag.innerText = "创建标签"  // 标签的内容
< "创建标签"
> tag.className = 'c1'  //  设置标签的属性
< "c1"
> tag
< <span class="c1">创建标签</span>

这里的tag是一个对象。
方法二:通过字符串来创建

> var tag = '<span class="c1">创建标签</span>'
< undefined
> tag
< "<span class="c1">创建标签</span>"

添加的时候注意内部的引号,你可以错开使用单引号和双引号。另外还可以使用(\"),表示字符串内的引号字符。
这里的tag实际只是html代码的字符串。

添加标签

方法一:基于Element的操作
targetElement.insertAdjacentElement(position, element); :插入标签对象
element.insertAdjacentHTML(position, text); :插入标签字符串
position参数,只有4个值。beforebegin、afterbegin、beforeend、afterend。对应的位置如下所示:

<!-- beforebegin -->
<p>
<!-- afterbegin -->
foo
<!-- beforeend -->
</p>
<!-- afterend -->

代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .c1{
            color: red;
        }
    </style>
</head>
<body>
<div id="i1">
    <p>初始的内容</p>
</div>
<script>
    var tag1 = document.createElement('p');
    tag1.innerText = "创建的标签1";
    tag1.className = 'c1';
    var obj = document.getElementById('i1');
    obj.insertAdjacentElement("beforeend", tag1);  // 插入标签对象
    var tag2 = '<p class="c1">创建的标签2</p>';
    obj.insertAdjacentHTML("beforeend", tag2);  // 插入标签字符串
</script>
</body>
</html>

方法二:基于Node的操作
其实上面的方法应该够用了,不过这里还有两个方法:
var aChild = element.appendChild(aChild); :在当前对象后面添加,和上面的 beforeend 的效果一样。
var insertedNode = parentNode.insertBefore(newNode, referenceNode); :在父节点的下的 reference 子节点前插入。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .c1{
            color: red;
        }
    </style>
</head>
<body>
<div id="i1">
    <p>初始的内容</p>
</div>
<script>
    var tag1 = document.createElement('p');
    tag1.innerText = "创建的P标签1";
    tag1.className = 'c1';
    var obj = document.getElementById('i1');
    obj1 = obj.appendChild(tag1);
    var tag2 = document.createElement('div');
    tag2.innerText = '创建的DIV标签2';
    tag2.setAttribute('class', 'c1');
    var parentObj = document.getElementById('i1').parentNode;
    parentObj.insertBefore(tag2, obj);
</script>
</body>
</html>

提交表单

不一定非得要 input 来提交表单,任何标签通过js触发都可以提交表单。只要获取 form 对象,调用对象的 submit() 方法即可:
formObj.submit() :提交表单

<body>
<form action="https://www.baidu.com/s" id="f1">
    <input type="text" name="wd" />
    <input type="submit" value="搜索" />
    <a onclick="submitForm()" style="border: 1px solid">点我也能提交</a>
</form>
<script>
    function submitForm() {
        document.getElementById('f1').submit();
    }
</script>
</body>

JavaScript 消息框

在 js 中有三种消息框:警告框、确认框、提示框。
另外还有一个 console.log() ,是向控制台输出信息。警告框之前已经掌握了。
下面是确认框和提示框。

确认框

语法:confirm(message) 显示一个消息的确认框。和警告框不同,有两个按钮,并且有返回值。按确认会返回true,按取消会返回false。
举例如下,分别试试按确定和取消的效果:

<body>
<h1 id="i1">分别试试按确定和取消</h1>
<script>
    // 这里顺便试试自执行函数
    (function () {
        obj = document.getElementById('i1')
        var r = confirm("弹出确认框");
        if(r === true){
            obj.innerText = "你点击了确定"
        } else {
            obj.innerText = "你点击了取消"
        }
    })()
</script>
</body>

提示框

语法:prompt("文本","默认值") 弹出一个让用户输入的对话框。第一个变量,会显示在输入框上面作为提示信息。第二个变量会作为输入框内的默认值事先填写好。

<body>
<script>
    (function () {
        var name = prompt("请输入你的名字", "张三");
        if(name.length > 0){
            document.write("<p>你好,"+ name)
        }
    })()
</script>
</body>

现在谁还用弹出框做交互呢,用 input 应该根据友好吧。

location 对象

location.href :设置或返回完整的 URL。
示例,先用浏览器随便打开一个页面,进入控制台测试:

> location.href // 返回当前的url
< "about:blank"
> location.href = "http://www.51cto.com"  // 设置url,页面会跳转

location.reload() :重新加载
location对象是包含有关当前 URL 的信息,还有更多属性和方法,这里只分别举例了两个常用的。

定时器

用两种定时器,一种是循环执行的定时器,一种是只执行一次的定时器:
setInterval(code,millisec) :按照指定的周期(以毫秒计)来调用函数或计算表达式。clearInterval(t) ,取消这个定时器。
setTimeout(code,millisec) :在指定的毫秒数后调用函数或计算表达式。clearTimeout(t) ,取消这个定时器
上面两个方法都有返回值(假设是t),后面的方法就是用于取消这个定时器的,具体看下面的例子:

<body>
<h1>定时器测试<span id="i1"></span></h1>
<script>
    // 启动一个定时器,每秒刷新显示内容
    var count = 0;
    t = setInterval(function () {
        count ++;
        document.getElementById('i1').innerText = count
    }, 1000);
    // 设置另一个定时器,5秒后清除前面的定时器,并弹框
    setTimeout("clearInterval(t); alert('结束')", 5000)
</script>
</body>

timeout 定时器的应用场景,比如当你点击某个按钮后,会显示一条提示信息,但是信息不用永久显示在哪里,经过一段时间后该条信息会自动消失。这时就要用到这种只执行一次的定时器了:

<body>
<div id="i1">点击下面的按钮</div>
<div>
    <input type="button" value="开始" onclick="start()">
    <input type="button" value="取消" onclick="stop()">
</div>
<script>
    var t;  // 这里先把t声明为全局变量,下面的两个function里都是全局的t
    // 点击按钮后显示一条提示消息,2秒后变回原来的信息
    function start() {
        obj = document.getElementById('i1');
        obj.innerText = "你刚点了开始(2秒后消息消失)";
        t = setTimeout(function () {
            obj.innerText = "点击下面的按钮"
        }, 2000)
    }
    // 点了取消按钮后,清除了计时器,文字就不会变回去了
    function stop() {
        clearTimeout(t);
    }
</script>
</body>

事件

事件绑定的方法

这里的事件都以onclick事件举例,事件绑定的方法有下面几种
在标签内写onclick事件。之前都是用这个方法绑定事件的,简单,直观,但是LOW。
在JS写onclick=function(){}函数。推荐用这种方法绑定事件。结构更加清晰,让js代码从html标签中剥离出来。

<body>
<p>
    在标签内写onclick事件
    <input type="button" onclick="func()" value="点我1">
</p>
<p>
    在JS写onclick=function(){}函数
    <input type="button" value="点我2" id="i2">
</p>
<script>
    function func() {
        alert("点我1")
    }
    document.getElementById('i2').onclick = function () {
        alert("点我2")
    }
</script>
</body>

从上面的例子中并看不出有什么优势,假如我们有很多标签要绑定同一个事件,那么只要在js里写一个for循环就能一次全部完成绑定了:

<body>
<input type='button' value='0'/>
<input type='button' value='1'/>
<script>
    list_btn = document.getElementsByTagName('input');
    // 给所有的button都绑定下面的事件
    for(var i=0; i<list_btn.length; i++){
        list_btn[i].onclick = function () {
            // alert(list_btn[i].value);  // 这句是错的
            // alert(i);  // 上面的问题,看看这个i的值
            alert(this.value);  // 这里的this指的是调用这个函数的对象,其实还是上面的list_btn[i],但是不能那么写
        }
    }
</script>
</body>

上面的例子中,如果使用第一个alert的写法,会报错。得用最后的写法,用this来指代当前的对象(即标签)。这里产生问题的原因是js作用域的问题。

事件绑定中遇到的作用域的问题

在上面的例子中,如果我们放开第二个alert语句查看i的值,就会发现,始终是2。因为这里i不是这个函数内的局部变量,而是在函数外部一直在变的,最终的值是2。所以上面的数组要取下标2自然是取不到的。解决方法就是用this,这里谁调用这个函数,这个this就指向谁。
既然是作用域的问题,那么不用this,通过闭包来限制作用域一样也是可以实现的。

<body>
<input type='button' value='0'/>
<input type='button' value='1'/>
<script>
    list_btn = document.getElementsByTagName('input');
    // 给所有的button都绑定下面的事件
    for(var i=0; i<list_btn.length; i++){
        // 外面再套一层function,做成一个闭包
        // 把i的值传递给arg,这里arg的作用域是这里闭包内
        (function (arg) {
            list_btn[i].onclick = function () {
            alert(list_btn[arg].value);  // 现在应该对了
            // alert(arg);  // 看看这里变量的值
            }
        })(i)

    }
</script>
</body>

肯定还是用this简单,一般都用this,这里就展开解释一下作用域的问题。

事件绑定的另一个方法

上面的方法将事件绑定写在js中的方法基本已经够用了,仍有不足。如果需要为一个标签绑定两个onclick事件,之前的方法显然都无法实现。就要用到 addEventListener ,下面只是简单的讲一下实现上面这种需求的实现方法,更深就算了。
target.addEventListener(type, listener, useCapture); :将 type 的事件类型注册到 target 对象上。
type :表示监听事件类型的字符串。这里 click 表示按下并释放任意鼠标按键(没有 onclick)。
listener :在这里写上要触发的函数
useCapture :布尔值,默认false。两个元素都对同一个事件注册了一个处理函数时有两种不同的事件传播方式,事件冒泡(false)和事件捕获(true)。
先不考虑第三个参数,点击按钮后要触发了两个事件,分别修改 i1 和 i2 的样式:

<body>
<h1 id="i1">Test1</h1>
<h1 id="i2">Test2</h1>
<input id="btn" type="button" value="改变字体颜色">
<script>
    obj1 = document.getElementById('i1');
    obj2 = document.getElementById('i2');
    btn = document.getElementById('btn');
    btn.addEventListener('click', function () {
        obj1.style.color = 'red'
    });
    btn.addEventListener('click', function () {
        obj2.style.color = 'blue'
    });
</script>

下面是有元素嵌套的情况,这时候第三个参数就有区别了。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        #main{
            background-color: blue;
            width: 500px;
            height: 400px;
        }
        #content{
            background-color: red;
            width: 300px;
            height: 200px;
        }
    </style>
</head>
<body>
<h1 id="i1">看我的颜色</h1>
<div id="main">
    <div id="content"></div>
</div>
<script>
    var farther = document.getElementById('main');
    var son = document.getElementById('content');
    var str = document.getElementById('i1');
    farther.addEventListener('click', function () {
        str.style.color = 'blue'
    }, true);  // 就是这里的参数会影响运行的结果,改成false试试
    son.addEventListener('click', function () {
        str.style.color = 'red'
    });
</script>
</body>
</html>

当有元素重叠的时候,同时出发一个事件,每个元素的事件执行会有先后顺序。默认是false,冒泡,先执行内层元素的事件,然后向外传播。上面的例子中设置了true,捕获,先执行外层元素的事件,然后往内传播。当点击重叠区域的时候,每个事件都响应并执行了,最后执行的是内层的事件,所以最后变成了内层事件设置的颜色。

作业

这周的内容并没有作业,要等学了下周的jQuery一起搞个作业了。