XyyFighting


  • 首页

  • 标签

  • 分类

  • 归档

JavaScript高级程序设计读书笔记3

发表于 2018-01-11 | 分类于 读书笔记 , JavaScript高级程序设计

第3章 基本概念

严格模式

  • 要在整个脚本启用严格模式,可以在顶部添加如下代码: "use strict"
  • 也可以在指定函数的严格模式下进行:

    1
    2
    3
    4
    function doSomething(){
    "use strict";
    //函数体
    }
  • 严格模式下,JS的执行结果会有很大的不同

关键字和保留字

  • 保留字在JS中还没有任何特性的用途,但它们有可能在将来被用作关键字

变量

  • var操作符定义的变量将成为定义该变量的作用域中的局部变量。
  • 省略var操作符,就可以创建一个全局变量。但不是推荐的做法,因为在局部作用域中定义的全局变量很难维护。

数据类型

  • 5种简单数据类型:Undefined、Null、Boolean、Number、String。1种复杂数据类型:Object。
  • typeof操作符是一个操作符而不是函数。

Undefined类型

  • Undefined类型只有一个值,就是undefined。在使用var生命变量但未对其甲乙初始化时,这个变量的值就是undefined。
  • 但是undefined值的变量与上位定义的变量是不一样的。

    1
    2
    3
    var message;
    alert(message); //"undefined"
    alert(age); //age未声明,产生错误
  • 对于尚未声明过的变量,只能执行typeof操作检测其数据类型。

  • 对未初始化和未声明的变量执行typeof操作符都反悔了undefined值
    1
    2
    3
    var message;
    alert(typepf message); //"undefined"
    alert(typeof age); //"undefined"

Null类型

  • Null类型只有一个值,就是null。
  • typeof null会返回object,因为特殊值null会被认为是一个空的对象引用
  • underfined值是派生自null值的。

    1
    alert(null==undefined);   //true
  • 不需要把一个变量的值显式地设置为undefined,但是保存对象的变量最好显示初始化为null。

数据类型 转换为true 转换为false
Boolean true false
String 任何非空字符串 “”空字符串
Number 任何非零数字值(包括无穷大) 0和NaN
Object 任何对象 null
Undefined 不适用 undefined

Number类型

  • 八进制字面值的第一位必须是0,十六进制的前两位必须是0X
  • 由于浮点型数值在计算时有精度,所以0.1+0.2=0.300000000004
  • ECMAScript的最大数和最小数分别保存在Number.MAX_VALUE和Number.MIN_VALUE中,如果超出范围,则会被转换成-Infinity和Infinity。
  • isFinite()参数在最大值和最小值之间会返回true。
  • NaN用于表示本来要返回数值的操作数未返回数值的情况。
  • NaN有两个特点:

    1. 任何涉及NaN的操作都会返回NaN;
    2. NaN与任何值都不相等,包括NaN本身。
  • isNaN()函数,确定参数是否不是数值。

  • 数值转换

    1
    2
    Number("");    //0
    ParseInt(""); //NaN
  • parseFloat()和parseInt()的区别:

    1. parseFloat的小数点有效;parseInt小数点无效,只输出整数
    2. parseFloat忽略前导的0,只解析十进制数;parseInt能够解析八进制和十六进制数

String

  • 字符串可以由双引号或单引号表示
  • 字符串是不可变的,要改变某个变量保存的字符串,首先要销毁原来的字符串,再用另一个包含新值的字符串填充该变量。
  • 数值、布尔值、对象和字符串值都有toString()方法,但是null和undefined没有这个方法。
  • String()函数如果值有toString()方法,则调用该方法并返回相应的结果;如果是null,则返回“null”;如果是undefined,则返回“undefined”

Object

  • var o=new Object()

操作符

一元操作符

  • num=+num对非数值应用一元加操作符时,该操作符会像Number()转型函数一样对这个值执行转换。

    位操作符

  • ~非操作:操作数的负值减一
  • &与操作:将两个二进制数的每一位对齐按与操作
  • |或操作:将两个二进制数的每一位对齐按或操作
  • ^异或操作:将两个二进制数的每一位对齐按异或操作。1^1=0;0^0=0;1^0=1
  • <<左移:将数值的所有位向左移动指定的位数。左移不会影响操作数的正负号
  • >>右移:将数值的所有位向右移动指定的位数。右移不会影响操作数的正负号,补上的值是符号位的值
  • >>>无符号右移:正数的无符号右移与有符号右移相同,但是负数的结果不同,因为用0来填补空位。

布尔操作符

  • Boolean()与!!效果相同
  • 不能在逻辑与和逻辑或中使用为定义的值,会发生错误。

乘性操作符

  • Infinity*0=NaN
  • Infinity1=Infinity ; Infinity(-1)=-Infinity
  • Infinity*Infinity=NaN ; Infinity/Infinity=NaN ; Infinity%Infinity=NaN
  • 0/0=NaN
  • 1/0=Infinity ; -1/0=-Infinity
  • Infinity/1=Infinity ; Infinity/(-1)=-Infinity
  • Infinity%1=NaN
  • 1%0=NaN
  • 1%Infinity=1
  • 0%1=0

加性操作符

  • Infinity-Infinity=NaN
    1
    2
    3
    4
    5
    6
    var message="The sum of 5 and 10 is "+5+10;
    alert(message);
    //The sum of 5 and 10 is 510
    var message="The sum of 5 and 10 is "+(5+10);
    alert(message);
    //The sum of 5 and 10 is 15

关系操作符

  • “23”<”3” //true 因为字符串比较的是字符编码
  • “23”<3 //false 在比较字符串和数值时,字符串会被转换成数值,再进行比较
  • NaN<3 //false
  • NaN>=3 //false
  • “a”>3 //false

相等操作符

相等和不相等

  • 如果有一个操作符是布尔值或字符串,就在比较值前先转换成数值
  • 如果有一个操作数是NaN,相等操作符返回flase,不相等操作符返回true
  • null==undefined //true

全等和不全等

  • 全等与不全等操作符 相等与不相等操作符的区别: 比较之前不转换操作数
  • “55” == 55 //true
  • “55” ===55 //flase
  • null===undefined //false 不同类型的值

逗号操作符

  • 在用于赋值的时候,逗号操作符总会返回表达式中的最后一项
  • var num =(5,1,4,8,0) //num的值为0

语句

循环语句

  • 不存在块级作用域,因此在循环内部定义的变量也可以在外部访问到
  • for-in语句,如果要迭代的对象的变量值是null或undefined,for-in语句会抛出错误。ECMAScript 5更正这一行为,不再抛出错误,而是不执行循环体。

    label语句

  • label:startement语句可以在代码中添加标签,以便将来使用。经常与break和continue连用
  • 添加该标签break不仅会退出内部的for循环,还会退出外部的for循环

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var num=0;
    outermost:
    for(var i=0; i<10; i++) {
    for(var j=0; j<10; j++) {
    if(i==5&&j==5) {
    break outermost;
    }
    num++;
    }
    }
    alert(num); //55
  • 强制从外部的outermost标签处继续开始循环。53 54 60 61 …

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var num=0;
    outermost:
    for(var i=0; i<10; i++) {
    for(var j=0; j<10; j++) {
    if(i==5&&j==5) {
    continue outermost;
    }
    num++;
    }
    }
    alert(num); //95

with语句

  • with(expression) statement 语句目的是为了简化多次编写同一个对象的工作

Switch

  • 每个case的值不一定是常量,可以是变量,甚至是表达式
  • 使用表达式作为case值可以有如下操作,在外面定义变量num,则可以
    1
    2
    3
    4
    5
    6
    7
    8
    9
    var num = 25;
    switch (true) {
    case num < 20:
    alert("less than 20");
    break;
    case num >20:
    alert("more than 20");
    break;
    }

函数

  • 函数在执行完return语句后停止并立即退出。因此return之后的任何代码都永远不会执行。
  • return;语句可以不带有任何返回值,函数停止执行后将返回undefined值
  • 严格模式对函数有一些限制:
    1. 不能把函数命名为eval或arguments
    2. 不能把参数命名为eval或arguments
      3.不能出现两个命名参数同名的情况

参数

  • 函数体内可以通过arguments对象来访问参数数组,从而获取传递给函数的每一个参数。
  • 重要:传递与函数命名参数个数不同也没有关系,参数在函数内部用一个数组表示。命名的参数只提供便利,不是必需的。
  • 修改arguments[1],就会修改num2。这两个值内存空间独立,但是值同步,且影响是单向的。
  • 如果只传入一个参数,那么srguments[1]设置的值不会反映到命名参数中。因为arguments对象的长度由传入的参数个数决定。num2还是undefined
    1
    2
    3
    4
    5
    6
    7
    8
    function doAdd(num1,num2){
    arguments[1]=10;
    alert(arguments[0]+num2);
    alert(arguments[1]);
    alert(num2);
    }
    doAdd(1,2); // 11 arguments[1]=10 num2=10
    doAdd(1); // NaN arguments[1]=10 num2=undefined

没有重载

  • 如果定义两个名字相同的函数,则该名字只属于后定义的函数

JavaScript高级程序设计读书笔记1-2

发表于 2018-01-10 | 分类于 读书笔记 , JavaScript高级程序设计

第1章 JavaScript简介

JavaScript组成

  • 核心-ECMAScript:对实现该标准规定的各个方面内容的语言的描述,提供核心语言功能
  • 文档对象模型-DOM:是针对XML但经过扩展用于HTML的应用程序编程接口(API),提供访问和操作网页内容的方法和接口
  • 浏览器对象模型-BOM:没有相关的标准,提供与浏览器交互的方法和接口

  • tips:DOM并不值针对JS,很多别的语言也都实现了DOM。

  • 五个浏览器(IE、Firefox、Chrome、Safari、Opera)对ECMAScript支持程度都不错,对DOM的支持彼此相差比较多,BOM实现了某些众所周知的共同特性,但是其他特性还是会因浏览器而异。

第2章 在HTML中使用JavaScript

script函数

  • 如果包含了嵌入代码,则只会下载并执行外部脚本文件,嵌入的代码会被忽略。

标签的位置

  • JS引用一般放在<body>元素中页面内容的后面。在解析包含Js代码之前,页面的内容将完全呈现在浏览器中。如果放在<head>中,必须等到所有的js代码都被下载解析和执行完成以后,才能开始呈现页面内容。

延迟脚本

  • defer="defer"表明脚本会被延迟到整个页面都解析完毕后再运行,立即下载,但是延迟执行。

###异步脚本 ###

  • async立即下载脚本,但不妨碍页面中的其他操作,并不保证按照指定的先后顺序执行。
  • 异步脚本不要在加载期间修改DOM。
  • 异步脚本一定会在页面的load事件前执行。

XHTML

  • XHTML可扩展超文本标记语言,是将HTML作为XML的应用而重新定义的一个标准

小于号(<)在XHTML中将被当做开始一个新标签来解析。有两种方法可以解决:

  1. 用相应的HTML实体(<)替代代码中所有的小于号(<)
  2. 用一个CData片段来包含JS代码。在不兼容XHTML的浏览器中,不支持CData,故使用JS注释
1
2
3
4
5
6
7
8
9
10
11
12
13
script type="text/javascript">
//<![CDATA[
function compare(a, b) {
if (a < b) {
alert("A is less than B");
} else if (a > b) {
alert("A is greater than B");
} else {
alert("A is equal to B");
}
}
//]]>
</script>
  • 在XHTML中,CData片段是文档中的一个特殊区域,这个区域可以包含不需要解析的任意格式的文本内容

嵌入代码与外部文件

支持外部文件的人会有如下优点:

  1. 可维护性
  2. 可缓存
  3. 适应未来

文档模式

  • 混杂模式、标准模式。如果在文档开始出没有发现文档类型声明,则所有浏览器都会默认开启混杂模式。

noscript元素

  • <noscript>用于在不支持JS的浏览器中显示替代的内容。但在启用了脚本的浏览器中,用户不会看到它。
  • 包含在<noscript>元素中的内容只有在下列两种情况中的任何一种出现时,才会显示出来:
  1. 浏览器不支持脚本
  2. 浏览器支持脚本,但是脚本被禁用

实训项目遇到的问题

发表于 2018-01-02 | 分类于 问题解决

在加入表单组件时出现getFieldDecorator of undefined错误

  • 必须调用Form.create(),否则在使用getFieldDecorator时便会出现getFieldDecorator of undefined错误
    1
    2
    3
    4
    5
    6
    7
    8
    9
    //可以这么写
    let Test = React.createClass({
    render() {
    const { getFieldProps } = this.props.form;
    return( );
    }
    });
    Test = Form.create()(Test);
    export default Test;

//最终我是这么写的,也是可以的

1
2
3
4
5
6
7
8
class Down extends Component {
render() {
const { getFieldProps } = this.props.form;
return( );
}
});
Test = Form.create()(Test);
export default Test;

margin:0 auto;是让样式居中

windows 下 webstorm 格式化代码的快键键 Ctrl+Alt+l

在使用Git时遇到的问题

发表于 2017-12-22 | 分类于 问题解决

git修改远程仓库地址

  • git remote rm origin

  • git remote add origin [url]

如何解决failed to push some refs to git

  • 出现错误的主要原因是github中的README.md文件不在本地代码目录中

  • 可以通过如下命令进行代码合并[注:pull=fetch+merge] git pull --rebase origin master

  • 此时再执行语句 git push -u origin master即可完成代码上传到github

项目上传github步骤

  • 一个项目建一个仓库,先在项目中 git init,再git add ./,最后 git commit -m""。

  • tips:第一次push以前要先pull,因为有readme.md文件会冲突

想上传除了某文件以外的所有文件

  • 比如在react文件node_modules不想上传,想上传除了该文件以外的所有文件。先vim .gitignore写一个文件:
    /.idea/
    /node_modules/
  • 注意文件夹以前要写上/才可以被运行。

  • i是插入可写,:wq!是提交文件。

搭建react框架问题总结

发表于 2017-12-17 | 分类于 问题解决

react、react js、react native三者的区别

  1. React Js的目的是为了使前端的V层更具组件化,能更好的复用,它能够使用简单的html标签创建更多的自定义组件标签,内部绑定事件,同时可以让你从操作dom中解脱出来,只需要操作数据就会改变相应的dom。

  2. React Native的目的是希望我们能够使用前端的技术栈就可以创建出能够在不同平台运行的一个框架。可以创建出在移动端运行的app,但是性能可能比原声app差一点。

搭建react框架时遇到的问题

学习的教程是:从零搭建React全家桶教程,在搭建的过程中也遇到了不少问题,基本都解决了,少数未解决的等到熟练运用了react,再重新搭一遍框架理解一下。

模块热替换(Hot Module Replacement)

  • 加入了react-hot-loader v3以后计数依然变成0:至今未解决

文件路径优化:找不到这些文件。

  • 教程里写错了,不是在webpack.config.js中写路径文件,而是在webpack.dev.config.js中写

使用axios和middleware优化API请求,请求错误

  • 应该本来存在dist/api/user.json,模拟服务器传回来的数据,其中代码为
1
2
3
4
{
"name": "brickspert",
"intro": "please give me a star"
}
  • 但是我这边的不存在了,所以会请求错误.之后使用mock.js就可以代替这个api了.

调整文本编辑器

  • Sublime和Intellij都没有找到safe write。不过在后续的工作中没有影响

使用 json-server 代替 Mock.js,出现用户信息请求失败错误

  • 是因为”mockdev”: “npm run mock & npm start”这个运行命令只运行了npm run mock,&不能同时运行两个命令。而用”mockdev”: “npm run mock | npm start”就可以实现同时运行两个命令.

Error: listen EADDRINUSE 0.0.0.0:8080

  • 表明端口号被占用,要修改端口号。在webpack.dev.config.js中修改。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    devServer: {
    port: 8081, //修改8081
    contentBase: path.join(__dirname, './dist'),
    historyApiFallback: true,
    host: '0.0.0.0',
    proxy: {
    "/api/*": "http://localhost:8090/$1"
    }
    }

要在React全家桶中应用ant design的步骤

发表于 2017-12-14 | 分类于 前端框架

搭建react框架的教程是:从零搭建React全家桶教程,应用ant design需要在安装后更改一些配置。

  1. webpack.dev.config.js中

    1
    2
    3
    4
    5
    6
    7
    module: {
    rules: [{
    test: /\.css$/,
    // use: ["style-loader", "css-loader?modules&localIdentName=[local]-[hash:base64:5]", "postcss-loader"] //把这句话中的参数去掉
    use: ["style-loader", "css-loader", "postcss-loader"]
    }]
    },
  2. .babelrc中添加

    1
    2
    3
    "plugins": [
    ["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }]
    ]
  3. webpack.config.js中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    module: {
    rules: [{
    test: /\.css$/,
    use: ExtractTextPlugin.extract({
    fallback: "style-loader",
    // use: ["css-loader?modules&localIdentName=[local]-[hash:base64:5]", "postcss-loader"] //也把这句话中的参数去掉
    use: ["css-loader", "postcss-loader"]
    })
    }]
    },

Javascript DOM编程艺术读书笔记12

发表于 2017-10-21 | 分类于 读书笔记 , Javascript DOM编程艺术

综合示例

导航栏

  • 不同页面的导航栏拥有不同的样式,当前页面的导航栏应该颜色更深

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function highlightPage() {
if (!document.getElementsByTagName) return false;
if (!document.getElementById) return false;
var headers = document.getElementsByTagName('header');
if (headers.length == 0) return false;
var navs = headers[0].getElementsByTagName('nav');
if (navs.length == 0) return false;
//取得导航链接,然后循环遍历它们
var links = navs[0].getElementsByTagName("a");
for (var i=0; i<links.length; i++) {
var linkurl;
for (var i=0; i<links.length; i++) {
//取得链接中的URL
linkurl = links[i].getAttribute("href");
//window.location.href取得当前页面的URL。String.indexOf(substring)返回字符串第一次出现的位置,用于在字符串中寻找子字符串的位置。比较当前链接的URL与当前页面的URL
if (window.location.href.indexOf(linkurl) != -1) {
//如果找到就添加here类
links[i].className = "here";
//取得当前链接的文本,将其转换成小写形式。如果链接中的文本是"Home",那么linktext的值是home。
var linktext = links[i].lastChild.nodeValue.toLowerCase();
//为body添加上id属性,可以为不同弄的页面设置不同的页面头部
document.body.setAttribute("id",linktext);
}
}
}
}

z-index

  • z-index 属性设置元素的堆叠顺序。拥有更高堆叠顺序的元素总是会处于堆叠顺序较低的元素的前面。
  • Z-index 仅能在定位元素上奏效(例如 position:absolute;)

内部导航

  • 点击内部导航,只出现对应的那一部分内容,其他的内容不可见。
  • 1.传入元素ID,设置其可见,其余不可见。修改每个部分的display属性,除了作为参数传入的id对应的部分,其他部分的display属性都被设置成“none”,而与传入id对应的那个部分的display属性被设置成block。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function showSection(id) {
    var sections = document.getElementsByTagName("section");
    for (var i=0; i<sections.length; i++ ) {
    if (sections[i].getAttribute("id") != id) {
    sections[i].style.display = "none";
    } else {
    sections[i].style.display = "block";
    }
    }
    }
  • 2.设置onclick点击事件,传入需要可见部分的元素ID。<article><nav><li><a href="#jay">Jay Skript</a></li></nav></article> 需要在

    中的
    所包含的链接被单击时调用showSection函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    function prepareInternalnav() {
    if (!document.getElementsByTagName) return false;
    if (!document.getElementById) return false;
    var articles = document.getElementsByTagName("article");
    if (articles.length == 0) return false;
    var navs = articles[0].getElementsByTagName("nav");
    if (navs.length == 0) return false;
    var nav = navs[0];
    var links = nav.getElementsByTagName("a");
    for (var i=0; i<links.length; i++ ) {
    // array=string.split(character)数组中的第一个元素是character以前的所有字符,第二个元素是character以后的所有字符
    var sectionId = links[i].getAttribute("href").split("#")[1];
    //确保真的存在相应的id元素
    if (!document.getElementById(sectionId)) continue;
    //页面加载以后,默认隐藏所有部分
    document.getElementById(sectionId).style.display = "none";
    //sectionId是一个局部变量,在onclick事件处理函数时就已经不存在了,可以设置一个属性
    links[i].destination = sectionId;
    //事件处理函数
    links[i].onclick = function() {
    showSection(this.destination);
    return false;
    }
    }
    }

增强表单

  • 点击label元素,关联的表单字段就会获得焦点。并不是所有的浏览器都实现了该行为。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    function focusLabels() {
    if (!document.getElementsByTagName) return false;
    //取得文档的label元素
    var labels = document.getElementsByTagName("label");
    for (var i=0; i<labels.length; i++) {
    //如果label有for属性,添加一个事件处理函数
    if (!labels[i].getAttribute("for")) continue;
    labels[i].onclick = function() {
    //在label被单击时,提取for属性的值,这个值就是相应表单字段的id。
    var id = this.getAttribute("for");
    //确保存在相应的表单字段
    if (!document.getElementById(id)) return false;
    var element = document.getElementById(id);
    //让相应的表单字段获得焦点
    element.focus();
    }
    }
    }

form对象和DOM

  • form.elements返回input、select、textarea以及其他表单字段。childNodes返回表单中的所有节点。
  • 每个表单元素都有自己的一组属性。element.value与element.getAttribute(“value”)等价。

  • 在不支持placeholder属性的浏览器中也能实现就编写如下方法。这个方法与11章的方法类似,只是用的是HTML的form方法,11章用的是DOM方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    function resetFields(whichform) {
    //检查浏览器是否支持placeholder属性,如果不支持,继续
    if (Modernizr.input.placeholder) return;
    //循环遍历表单中的每个元素
    for (var i=0; i<whichform.elements.length; i++) {
    var element = whichform.elements[i];
    //如果当前元素是提交按钮,跳过。
    if (element.type == "submit") continue;
    //如果没有placeholder属性,继续。
    if (!element.getAttribute('placeholder')) continue;
    //元素获得焦点的事件添加一个处理函数。如果字段的值等于占位符文本,则将字段的值设置为空
    element.onfocus = function() {
    if (this.value == this.getAttribute('placeholder')) {
    this.value = "";
    }
    }
    //元素失去焦点的事件添加一个处理函数。如果字段的值为空,则将字段的值设置为占位符
    element.onblur = function() {
    if (this.value == "") {
    //为了应用样式,在字段显示占位符值的时候,添加placeholder类
    this.className='placeholder';
    //鉴于不同的浏览器对位置属性的实现方式有所不同。同时使用了DOM placeholder属性和DOM的getAttribute('placeholder')方法
    this.value = this.getAttribute('placeholder')|| this.getAttribute('placeholder');
    }
    }

    element.onblur();
    }
    }

表单验证

使用JS验证表单的注意事项

  1. 验证脚本写的不好,反而不如没有验证
  2. 千万不要完全依赖JS。客户端验证并不能取代服务器端的验证。即使有了JS验证,服务器照样还应该对接收到的数据再次验证。
  3. 客户端验证的目的在于帮助用户填好表单,避免他们提交未完成的表单,从而节省他们的时间。服务器验证的目的在于保证数据库和后台系统的安全。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//表单验证
function validateForm(whichform) {
//循环遍历表单的elements数组
for (var i=0; i<whichform.elements.length; i++) {
var element = whichform.elements[i];
//如果发现了required属性,把相应的元素传递给isFilled函数
if (element.getAttribute("required") == 'required') {
if (!isFilled(element)) {
//如果isFilled函数返回false,显示警告信息,并且validateForm函数也返回false
alert("Please fill in the "+element.name+" field.");
return false;
}
}
//如果发现了email类型的字段,把相应的元素传递给isEmail函数
if (element.getAttribute("type") == 'email') {
if (!isEmail(element)) {
//如果isEmail函数返回false,显示警告信息,并且validateForm函数也返回false
alert("The "+element.name+" field must be a valid email address.");
return false;
}
}
}
//否则validateForm返回true
return true;
}

function isFilled(field) {
return (field.value.length > 1 && field.value != field.placeholder);
}

function isEmail(field) {
return (field.value.indexOf("@") != -1 && field.value.indexOf(".") != -1);
}

Ajax

  • 提交表单能够发送一个Ajax请求,感谢信息以嵌入方式添加到表单所在的页面,体验会更好。就是表单提交成功后,不打开新页面了,而是拦截请求,自己显示结果。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    //创建XMLHttpRequest对象
    function getHTTPObject() {
    if (typeof XMLHttpRequest == "undefined")
    XMLHttpRequest = function () {
    try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); }
    catch (e) {}
    try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); }
    catch (e) {}
    try { return new ActiveXObject("Msxml2.XMLHTTP"); }
    catch (e) {}
    return false;
    }
    return new XMLHttpRequest();
    }

    //展示loading图片
    function displayAjaxLoading(element) {
    // 删除所有子元素
    while (element.hasChildNodes()) {
    element.removeChild(element.lastChild);
    }
    // 创建一个图像元素
    var content = document.createElement("img");
    content.setAttribute("src","images/loading.gif");
    content.setAttribute("alt","Loading...");
    // 将图像添加到该元素中
    element.appendChild(content);
    }

    function submitFormWithAjax( whichform, thetarget ) {
    //在修改DOM和显示加载图像之前,首先要检查是否存在有效的XMLHttpRequest对象
    var request = getHTTPObject();
    if (!request) { return false; }
    //调用displayAjaxLoading函数,删除目标元素的子元素,并添加loading.gif图像
    displayAjaxLoading(thetarget);

    //循环遍历表单的每个字段,手机他们的名字和编码后的值
    var dataParts = [];
    var element;
    for (var i=0; i<whichform.elements.length; i++) {
    element = whichform.elements[i];
    dataParts[i] = element.name + '=' + encodeURIComponent(element.value);
    }
    //收集到所有数据后,把数组中的项用和号&连接起来
    var data = dataParts.join('&');
    //向原始表单的action属性指定的处理函数发送post请求
    request.open('POST', whichform.getAttribute("action"), true);
    //并在请求头部添加application/x-www-form-urlencoded头部。这个头部信息对于POST请求是必须的,它表示请求中包含URL编码的表单
    request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

    request.onreadystatechange = function () {
    if (request.readyState == 4) {
    if (request.status == 200 || request.status == 0) {
    //数组matches的第一个元素是responseText中的整个模式匹配的部分,即包括<article>和</article>在内的部分
    //数组matches的第二个元素responseText中与补货组中的模式匹配的部分
    var matches = request.responseText.match(/<article>([\s\S]+)<\/article>/);
    if (matches.length > 0) {
    thetarget.innerHTML = matches[1];
    } else {
    thetarget.innerHTML = '<p>Oops, there was an error. Sorry.</p>';
    }
    } else {
    thetarget.innerHTML = '<p>' + request.statusText + '</p>';
    }
    }
    };
    request.send(data);
    return true;
    };

    //提交表单触发的onclick按钮
    function prepareForms() {
    for (var i=0; i<document.forms.length; i++) {
    var thisform = document.forms[i];
    resetFields(thisform);
    thisform.onsubmit = function() {
    //如果表单没有通过验证,就返回false.验证失败,不能提交表单
    if (!validateForm(this)) return false;
    var article = document.getElementsByTagName('article')[0];
    //submitFormWithAjax函数成功发送了Ajax请求并返回true,则让submit事件处理函数返回false,以便阻止浏览器重复提交表单
    if (submitFormWithAjax(this, article)) return false;
    //submitFormWithAjax函数没有成功发送了Ajax请求,故submit事件返回true,让表单 像什么也没有发生一样继续通过页面提交
    return true;
    }
    }
    }

压缩代码

  • 谷歌的closure Compiler可以压缩代码

Javascript DOM编程艺术读书笔记11

发表于 2017-10-19 | 分类于 读书笔记 , Javascript DOM编程艺术

HTML5

Modernizr

  • 提供一些不同的CSS类名以及特性检测属性

    音频和视频

  • 在每个影片容器中,音频和视频轨道都使用不同的编解码器来编码。编码器决定了浏览器在播放时应该如何解码音频和视频。
  • 有代表性的视频解码器有:H.264、Theora和VP8
  • 没有一款浏览器支持所有容器和编解码器,因此必须提供多种后备格式。
  • 为了确保HTML5的最大兼容性,至少包含下列三个版本:

    基于H.264和AAC的MP4
    webM(VP8+Vorbis)
    基于Theora视频和Vorbis音频的Ogg文件

表单

  • 如果没有使用Modernize,可以使用如下函数检查浏览器是否支持某种类型的输入控件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function inputSupporsType(type){
    if(!document.createElement) return false;
    var input=document.createElement('input');
    //设置input表单的type属性
    input.setAttribute('type',type);
    //如果input表单的type属性是text,但是传入的type类型不是text,说明浏览器不支持该类型的输入控件
    if(input.type=='text'&&type!='text'){
    return false;
    }else{
    return true;
    }
    }
  • 如果没有使用Modernize,可以使用如下函数检查浏览器是否支持特定的属性

    1
    2
    3
    4
    5
    6
    function elementSupportsAttribute(elementName,attribute){
    if(!document.createElement) return false;
    var temp=document.createElement(elementName);
    //如果浏览器支持该属性,则会返回true,否则是false
    return(attribute in temp);
    }
  • 在不支持placeholder属性的浏览器中实现相同的效果,得编写以下DOM脚本实现完成同样的功能
    <input type="text" id="first-name" placeholder="you first name" />

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //使用Modernizr判断当前浏览器是否支持placeholder属性
    if(!Modernizr.input.placeholder){
    //根据id获得placeholder的input元素
    var input=document.getElementById('first-name');
    input.onfocus=function(){
    //如果input元素有placeholder属性值
    var text=this.placeholder||this.getAttribute('placeholder');
    //当鼠标焦距时是提示信息,就不显示提示信息
    if(this.value==text){
    this.value='';
    }
    }
    input.onblur=function(){
    //当鼠标离开时,没有文本内容,就仍然显示提示信息
    if(this.value==''){
    this.value=this.placeholder||this.getAttribute('placeholder');
    }
    }
    input.onblur();
    }

Javascript DOM编程艺术读书笔记10

发表于 2017-10-18 | 分类于 读书笔记 , Javascript DOM编程艺术

第10章 用javascript实现动画效果

css的overflow属性

overflow属性的可取值有4种:visible、hidden、scroll、auto。

  • visible:不裁剪溢出的内容。内容只显示在其容器元素的显示区域内,这意味着只有一部分内容可见。
  • hidden:隐藏溢出的内容。内容只显示在其容器元素的显示区域里,这意味着只有一部分内容可见。
  • scroll:类似于hidden,浏览器将对溢出的内容进行隐藏,但显示一个滚动条以便让用户能够滚动看到内容的其他部分。
  • auto:类似于scroll,但是浏览器只在确实发生溢出时才显示滚动条,如果内容没有溢出,就不显示滚动条。

position

  • 如果把position属性值是absolute的元素A,放入一个position是relative的元素B,B就成为A的容器元素,A将在B的显示区域里按absolute方式进行摆放。

math函数

  • Math.ceil(number)将浮点数number向“大于”方向舍入为与之最接近的整数
  • Math.floor(number)将浮点数number向“小于”方向舍入为与之最接近的整数
  • Math.round(number)将浮点数number舍入为与之最接近的整数

动画效果

message.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Message</title>

</head>
<body>
<p id="message1">Whee!</p>
<P id="message2">Whoa!</P>
<script src="scripts/addLoadEvent.js"></script>
<script src="scripts/positionMessage.js"></script>
<script src="scripts/moveElement.js"></script>
</body>
</html>

addLoadEvent.js
moveElement.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//elementID打算移动的元素id,final_x元素目的地左位置,final_y元素目的地上位置,interval两次移动之间的停顿时间
function moveElement(elementID,final_x,final_y,interval){
if(!document.getElementById) return false;
if(!document.getElementById(elementID))return false;
var elem=document.getElementById(elementID);
var xpos=parseInt(elem.style.left);
var ypos=parseInt(elem.style.top);
if(xpos==final_x&& ypos==final_y) {
return true;
}
//进行位置的移动
if(xpos<final_x){
xpos++;
}
if(xpos>final_x){
xpos--;
}
if(ypos<final_y){
ypos++;
}
if(ypos>final_y){
ypos--;
}
elem.style.left=xpos+"px";
elem.style.top=ypos+"px";
//注意其中的双引号和单引号的格式
var repeat="moveElement('"+elementID+"',"+final_x+","+final_y+","+interval+")";
movement=setTimeout(repeat,interval);
}

positionMessage.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function positionMessage() {
if (!document.getElementById) return false;
if (!document.getElementById("message1")) return false;
var elem = document.getElementById("message1");
elem.style.position = "absolute";
elem.style.left = "50px";
elem.style.top = "100px";
moveElement("message1", 125, 25, 20);
if (!document.getElementById("message2")) return false;
var elem = document.getElementById("message2");
elem.style.position = "absolute";
elem.style.left = "50px";
elem.style.top = "50px";
moveElement("message2", 125, 125, 20);
}
addLoadEvent(positionMessage);

滑动的效果

鼠标放在链接上,就会滑动到该图片
list.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Web Design</title>
<style type="text/css" media="screen">
@import url(styles/layout.css);
</style>
<script type="text/javascript" src="scripts/addLoadEvent.js">
</script>
<script type="text/javascript" src="scripts/moveElement.js">
</script>
<script type="text/javascript" src="scripts/prepareSlideshow.js">
</script>
<script type="text/javascript" src="scripts/insertAfter.js"></script>
</head>
<body>
<h1>Web Design</h1>
<p>These are the things you should know.</p>
<ol id="linklist">
<li>
<a href="structure.html">Structure</a>
</li>
<li>
<a href="presentation.html">Presentation</a>
</li>
<li>
<a href="behavior.html">Behavior</a>
</li>
</ol>
</body>
</html>

addLoadEvent.js
insertAfter.js
moveElement.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
function moveElement(elementID,final_x,final_y,interval) {
if (!document.getElementById) return false;
if (!document.getElementById(elementID)) return false;
var elem = document.getElementById(elementID);
//每当用户吧鼠标悬停在某个连接上,不管上一次调用是否已经把图片移动到位,moveElement函数都会再次调用并试图把这个函数移动到另一个地方。当用户在链接之间快读移动鼠标时,movement变量会像拔河绳一样来回。
if (elem.movement) {
clearTimeout(elem.movement);
}
//添加安全检查,如果没有被设置,就设置成0px
if (!elem.style.left) {
elem.style.left = "0px";
}
if (!elem.style.top) {
elem.style.top = "0px";
}
var xpos = parseInt(elem.style.left);
var ypos = parseInt(elem.style.top);
if (xpos == final_x && ypos == final_y) {
return true;
}
//动画设置越远移动越快,越近移动越慢
if (xpos < final_x) {
//math.ceil(number)浮点数number向“大于”方向舍入为最接近的整数
var dist = Math.ceil((final_x - xpos)/10);
xpos = xpos + dist;
}
if (xpos > final_x) {
var dist = Math.ceil((xpos - final_x)/10);
xpos = xpos - dist;
}
if (ypos < final_y) {
var dist = Math.ceil((final_y - ypos)/10);
ypos = ypos + dist;
}
if (ypos > final_y) {
var dist = Math.ceil((ypos - final_y)/10);
ypos = ypos - dist;
}
elem.style.left = xpos + "px";
elem.style.top = ypos + "px";
var repeat = "moveElement('"+elementID+"',"+final_x+","+final_y+","+interval+")";
//不能使用全局变量,不能使用局部变量,需要一个至于正在被移动的那个元素有关的变量,即将movement设置成属性。
elem.movement = setTimeout(repeat,interval);
}

prepareSlidershow.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

function prepareSlideshow() {
// 确保浏览器支持DOM方法
if (!document.getElementsByTagName) return false;
if (!document.getElementById) return false;
//确保元素存在
if (!document.getElementById("linklist")) return false;
//创建并添加元素
var slideshow = document.createElement("div");
slideshow.setAttribute("id","slideshow");
var preview = document.createElement("img");
preview.setAttribute("src","topics.gif");
preview.setAttribute("alt","building blocks of web design");
preview.setAttribute("id","preview");
slideshow.appendChild(preview);
var list = document.getElementById("linklist");
insertAfter(slideshow,list);
// 取得列表中的所有链接
var links = list.getElementsByTagName("a");
// 为mouseover实践添加动画效果
links[0].onmouseover = function() {
moveElement("preview",-100,0,10);
}
links[1].onmouseover = function() {
moveElement("preview",-200,0,10);
}
links[2].onmouseover = function() {
moveElement("preview",-300,0,10);
}
}
addLoadEvent(prepareSlideshow);

layout.css

1
2
3
4
5
6
7
8
9
#slideshow {
width: 100px;
height: 100px;
position: relative;
overflow: hidden;
}
#preview {
position: absolute;
}

Javascript DOM编程艺术读书笔记9

发表于 2017-10-17 | 分类于 读书笔记 , Javascript DOM编程艺术

第9章 CSS-DOM

  • 网页由一下三层信息构成一个共同体:结构层、表示层、行为层
  • 只有把CSS style属性插入到标记里,才可以用DOM style属性去查询这些信息。放在style.css或者写在文件部分里的<style>标签里,都没有用。

根据元素在节点树里的位置来设置样式

查找<h1>元素后面的元素,设置成加粗。

1
2
3
4
5
6
7
8
├─story.html
│
├─scripts
│ addLoadEvent.js
│ styleHeaderSiblings.js
│
└─styles
format.css

story.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>Man bites dog</title>
<script type="text/javascript" src="scripts/addLoadEvent.js"></script>
<script type="text/javascript" src="scripts/styleHeaderSiblings.js"></script>
<link rel="stylesheet" type="text/css" media="screen" href="styles/format.css"/>
</head>
<body>
<h1>Hold the front page</h1>
<p>This first paragraph leads you in.</p>
<p>Now you get the nitty-gritty of the story.</p>
<p>The most important information is delivered first.</p>
<h1>Extra! Extra!</h1>
<p>Further developments are unfolding.</p>
<p>You can read all about it here.</p>
</body>
</html>

addLoadEvent.js

1
2
3
4
5
6
7
8
9
10
11
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
oldonload();
func();
}
}
}

styleHeaderSiblings.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
function styleHeaderSiblings(){
if(!document.getElementsByTagName) return false;
var headers=document.getElementsByTagName("h1");
var elem;
for(var i=0;i<headers.length;i++){
elem=getNextElement(headers[i].nextSibling);
addClass(elem,"intro");
}
}

function getNextElement(node){
//如果这个节点是元素节点就返回这个节点
if(node.nodeType==1){
return node;
}
//如果这个节点不是元素节点,就继续寻找下一个节点
if(node.nextSibling){
return getNextElement(node.nextSibling);
}
return null;
}
//以下两种是更通用的方法
function addClass(element,value) {
//检查className属性的值是否为null
if (!element.className) {
//如果是把新的class设置值直接赋值给className
element.className = value;
} else {
//如果不是,就把一个空格和新的calss设置值追加到className属性上去
newClassName = element.className;
newClassName+= " ";
newClassName+= value;
element.className = newClassName;
}
}
function styleElementSiblings(tag,theclass){
if(!document.getElementsByTagName)return false;
var elems=document.getElementsByTagName(tag);
var elem;
for(var i=0;i<elems.length ;i++){
elem=getNextElement(elems[i].nextSibling);
addClass(elem,theclass);
}
}
//addLoadEvent(styleHeaderSiblings);
addLoadEvent(function(){
styleElementSiblings("h1","intro")
})

format.css

1
2
3
4
.intro {
font-weight: bold;
font-size: 1.2em;
}

根据某种条件反复设置某种样式

将表格中奇数行和偶数行的背景颜色设置成不同的

1
2
3
4
5
6
7
8
9
10
│  itinerary.html
│
├─scripts
│ addLoadEvent.js
│ displayAbbreviations.js
│ highlightRows.js
│ stripeTables.js
│
└─styles
format.css

itinerary.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Cities</title>
<link rel="stylesheet" type="text/css" media="screen" href="styles/format.css" />
<script type="text/javascript" src="scripts/addLoadEvent.js"></script>
<script type="text/javascript" src="scripts/stripeTables.js"></script>
<script type="text/javascript" src="scripts/displayAbbreviations.js"></script>
<script type="text/javascript" src="scripts/highlightRows.js"></script>
</head>
<body>
<table>
<caption>Itinerary</caption>
<thead>
<tr>
<th>When</th>
<th>Where</th>
</tr>
</thead>
<tbody>
<tr>
<td>June 9th</td>
<td>Portland, <abbr title="Oregon">OR</abbr></td>
</tr>
<tr>
<td>June 10th</td>
<td>Seattle, <abbr title="Washington">WA</abbr></td>
</tr>
<tr>
<td>June 12th</td>
<td>Sacramento, <abbr title="California">CA</abbr></td>
</tr>
</tbody>
</table>
</body>
</html>

addLoadEvent.js

1
2
3
4
5
6
7
8
9
10
11
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
oldonload();
func();
}
}
}

displayAbbreviations.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
function displayAbbreviations() {
if (!document.getElementsByTagName || !document.createElement || !document.createTextNode) return false;
// 得到所有"abbr"元素
var abbreviations = document.getElementsByTagName("abbr");
if (abbreviations.length < 1) return false;
var defs = new Array();
// 循环遍历
for (var i=0; i<abbreviations.length; i++) {
var current_abbr = abbreviations[i];
if (current_abbr.childNodes.length < 1) continue;
var definition = current_abbr.getAttribute("title");
var key = current_abbr.lastChild.nodeValue;
defs[key] = definition;
}
// 创建一个dl列表
var dlist = document.createElement("dl");
// 循环创建列表内的元素
for (key in defs) {
var definition = defs[key];
// 创建列表中的项目
var dtitle = document.createElement("dt");
var dtitle_text = document.createTextNode(key);
dtitle.appendChild(dtitle_text);
// 创建它的描述
var ddesc = document.createElement("dd");
var ddesc_text = document.createTextNode(definition);
ddesc.appendChild(ddesc_text);
// 将他们添加到列表上
dlist.appendChild(dtitle);
dlist.appendChild(ddesc);
}
if (dlist.childNodes.length < 1) return false;
// 创建一个标题
var header = document.createElement("h2");
var header_text = document.createTextNode("Abbreviations");
header.appendChild(header_text);
// 添加标题和列表到html页面上
document.body.appendChild(header);
document.body.appendChild(dlist);
}
addLoadEvent(displayAbbreviations);

highlightRows.js:鼠标指针悬停在某个表格行的上方时,把该文本加黑加粗

1
2
3
4
5
6
7
8
9
10
11
12
13
function highlightRows() {
if(!document.getElementsByTagName) return false;
var rows = document.getElementsByTagName("tr");
for (var i=0; i<rows.length; i++) {
rows[i].onmouseover = function() {
this.style.fontWeight = "bold";
}
rows[i].onmouseout = function() {
this.style.fontWeight = "normal";
}
}
}
addLoadEvent(highlightRows);

stripeTables.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function stripeTables() {
if(!document.getElementsByTagName) return false;
//找出文档中的所有table元素
var tables=document.getElementsByTagName("table");
var odd,rows;
for(var i=0;i<tables.length;i++){
//对每个table元素,创建odd变量并把它初始化为false
odd=false;
//遍历表格中的所有数据行
rows=tables[i].getElementsByTagName("tr");
for(var j=0;j<rows.length;j++){
//如果变量odd的值是true,设置样式并把odd变量修改为false
if(odd==true){
addClass(rows[j],"odd");
odd=false;
}else{
//如果变量odd的值是false,不设置样式,但把odd变量修改为true
odd=true;
}
}
}
}

function addClass(element,value) {
//检查className属性的值是否为null
if (!element.className) {
//如果是把新的class设置值直接赋值给className
element.className = value;
} else {
//如果不是,就把一个空格和新的calss设置值追加到className属性上去
newClassName = element.className;
newClassName+= " ";
newClassName+= value;
element.className = newClassName;
}
}
addLoadEvent(stripeTables);

ClassName

  • 问题:在DOM直接设置或修改样式,让行为层干表示层的活,并不是理想的工作方式。
  • 解决方法:为元素追加一个className,在css文档中含有该类名的样式。可以通过修改CSS中类名的样式修改样式而不需要修改JS函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
function addClass(element,value) {
//检查className属性的值是否为null
if (!element.className) {
//如果是把新的class设置值直接赋值给className
element.className = value;
} else {
//如果不是,就把一个空格和新的calss设置值追加到className属性上去
newClassName = element.className;
newClassName+= " ";
newClassName+= value;
element.className = newClassName;
}
}
1…456
Xyy

Xyy

I want to get ahead in a step-by-step

59 日志
16 分类
8 标签
© 2018 Xyy
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.4