Code Monkey home page Code Monkey logo

blogs's Introduction

欢迎来到这里!

我是Zombie Brand,一名前端爱好者,了解更多关于我的信息,请随时在这里与我联系:

Gmail Badge QQ

⚡ Technologies

JavaScript WebPack Vite Nodejs Nestjs Prisma React AntDesign Vue HTML5 CSS3 Bootstrap TypeScript MongoDB Redis GraphQL PostgreSQL MySQL Docker Git GitHub GitLab BitBucket Vim ApacheEcharts

Visitor Badge

blogs's People

blogs's Issues

Canvas,手机画板demo

制作手机画板Demo重要组成部分

  1. touchstart开始触摸点
  2. touchmove触摸移动点
  3. touchend触摸结束点

touch属性

  • 概要

一个 TouchList,其会列出所有当前在与触摸表面接触的 Touch 对象,不管触摸点是否已经改变或其目标元素是在处于 touchstart 阶段。

  • 语法

var touches = touchEvent.touches;

  • 返回值是touches

一个 TouchList,其会列出所有当前在与触摸表面接触的 Touch 对象,不管触摸点是否已经改变或其目标元素是在处于 touchstart 阶段。

  • 如何调取touch的属性
$(Element).on('touchstart',function(event){
     console.log(event)
}
someElement.addEventListener('touchstart',function(event){
    console.log(event)
})

touches[0]Touch返回值主要属性介绍

  • clientX,clientY 是相对于浏览器窗口的X,Y位置
  • screenX,screenY 是相对于屏幕的X,Y位置
  • pageX,pageY 是相对于页面的X,Y位置
  • 调用方法如下:
someElement.addEventListener('touchstart',function(event){
    let {clientX,clientY} = e.touches[0] //ES6新增语法糖
    //下边两行代码等同于上边代码
    let clientX = e.touches[0].clientX
    let clientY = e.touches[0].clientY
})

Canvas

跨域请求

什么是同源策略

浏览器出于安全的方面考虑,只允许同协议,同域名,同端口下的接口交互,不同源的客户端脚本在没有授权的情况下,不能读写未授权方的资源.

同源的条件

  • 同协议:比如都是http,或者都是https
  • 同域名:比如baidu.com/a ,baidu.com/b
  • 同端口:比如80端口

什么是跨域? 有哪些方式

  • JSOP
  • CORS 跨域资源共享(Cross-Origin Resource Sharing)
  • 降域
  • postMessage()

CORS的原理

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
它允许浏览器向跨源服务器,发出[XMLHttpRequest]请求,从而克服了AJAX只能[同源]使用的限制。
CORS重点是需要后端支持才行.

简单模式:GET POST HEAD

HTTP的头信息不超出以下几种字段
 Accept
 Accept-Language
 Content-Language
 Last-Event-ID
 Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain 
后端添加响应头
response.setHeader('Access-Control-Allow-Origin','http://xxxx.com')    // 允许xxxx.com跨域请求

简单请求:对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。
Origin:http://xxx.com:8888来自源(协议,域名,端口)服务器根据这个值,决定是否同意这次请求。
如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段,就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。

复杂模式: 除了GET POST HEAD

后端添加响应头
response.setHeader('Access-Control-Allow-Origin','http://xxxx.com')    // 允许xxxx.com跨域请求
response.setHeader('Access-Control-Allow-Methods','GET,POST,HEAD,PUT,PATCH,OPTIONS,DELETE'

运用方式

  1. 更改本地hosts,映射本地访问 (Mac用sudo方式更改,windows用管理员运行记事本打开hosts更改)

127.0.0.1 cheche.com

127.0.0.1 duowan.com

  1. 使用nodejs执行index.js,监听80端口 (Mac用sudo方式更改80端口,windows用管理员运行gitbush更改80端口),并且添加响应头response.setHeader('Access-Control-Allow-Origin','http://duowan.com'),response.setHeader('Access-Control-Allow-Method','get,post,head')让duowan.com获得跨域请求权限
  2. 访问http://cheche.com/cheche,发送AJAX请求http://cheche.com/cheche_prototype.JSON
  3. 访问http://duowan.com/duowan,发送AJAX请求http://cheche.com/cheche_prototype.JSON,这样实现http://duowan.com对http://cheche.com跨域请求

具体详情参考阮一峰非简单请求

JSONP

原理:就是利用<script>标签没有跨域限制来达到与第三方通讯的目的。当需要通讯时,本站脚本创建一个<script>元素,地址指向第三方的API网址,形如:
<script src="http://www.example.net/api?param1=1&param2=2"></script>
并提供一个回调函数来接收数据(函数名可约定,或通过地址参数传递)。
第三方产生的响应为json数据的包装(故称之为jsonp,即json padding),形如:
callback({"name":"cheche","gender":"Male"})
这样浏览器会调用callback函数,并传递解析后json对象作为参数。本站脚本可在callback函数里处理所传入的数据。

实例

运行方式

  1. 使用node.js执行index.js监听80端口
  2. 更改本地hosts文件,映射本地访问为
127.0.0.1 cheche.com
127.0.0.1 duowan.com
  1. 访问http://cheche.com, script会请求http://duowan.com/duowan.js

具体实现代码

  1. 声明封装一个jsonp的函数,并且传入两个参数url和一个随机的Messagexxx,因为防止多次跨域请求得到的资源不会被覆盖.让每一次请求的资源都能够独立的显示.
 function jsonp(url,fn){
  var Message = 'Message'+parseInt(Math.random()*1000,10)  //生成随机Message
  window[Message] = fn                                     //让这个随机Message是window中的key,这里不使用window.Message主要是因为这个Message是随机每一都不一样.
  var script = document.createElement('script')            //创建一个script标签
  script.src = url+'?callback='+ Message                   //callback是约定俗成的,可以改变为任意参数
  document.head.appendChild(script)                        //利用script的特性,只有在html文档中才会触发请求
 }
  1. 在duowan.js中放入要被跨域请求的数据
    {{callback}}({"name":"cheche","gender":"male"}) //{{callback}}是一个占位等待被回调的参数

  2. 在后端中需要添加如下代码

  if(path === "/duowan.js"){
    var string = fs.readFileSync('./duowan.js','utf-8')
    var callback = query.callback                         //利用?查询字符串调取callback的值,这个值就是Message看上边步骤1
    response.setHeader('Content-Type','charset=utf-8')    
    response.end(string.replace('{{callback}}',callback)) //替换string字符串中的{{callback}}为callback,它的值又是Message,Message在window中它的值是function(data){}
  }
  1. 在前端执行jsonp函数
  jsonp('http://duowan.com',function(data){
    console.log(第一次数据)
    console.log(data)
  })
  jsonp('http://duowan.com',function(data){
    console.log(第二次数据)
    console.log(data)
  })
  1. 得到响应的数据为Message加随机数的执行函数:Message0323({"name":"cheche","gender":"male"})
  第一次的数据
  {"name":"cheche","gender":"male"}
  第二次的数据
  {"name":"cheche","gender":"male"}
  

jQuery也有同样的函数实现JSONP

  $.ajax({
    url:"http://duowan.com/duowan.js"
    dataType:'jsonp'
    success:function(data){
      console.log('第一次执行')
      console.log(data)
    }
    
  })

CORS与JSONP的使用目的相同,但是比JSONP更强大。
JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。

降域请求和postMessage()的原理

浏览器同源政策及其规避方法

高雪莹跨域博客

js 原型链与原型继承

原型

了解原型链,我们先了解下原型

  1. 所有的对象都有toString()、valueOf()、constructor、hasOwnProperty()等
  2. 所有的数组都是有对象应该有的属性
  3. 所有的数组都有push()、shift()、join()、slice()、splice()等
  4. var array = [] 应该有数组共有的所有属性
var array = []

我们用console.dir()检测下array有什么属性

img

我们发现 console.dir(array) 打出来的结果是:

  1. array 有一个属性叫做__proto__(它是一个对象)
  2. array.__proto__ 有很多属性,包括 push()、shift()、join()、slice()、splice()等
  3. array.__proto__ 其实也有一个叫做 __proto__ 的属性,包括toString()、valueOf()、constructor、hasOwnProperty()等
  4. array.__proto__.__proto__ 其实也有一个叫做__proto__ 的属性(console.log 没有显示),值为 null

原型链

当我们读取 array.toString 时,JS 引擎会做下面的事情:

  1. 看看 array 数组本身有没有 toString 属性。没有就走到下一步。
  2. 看看 array.__proto__ 有没有 toString 属性,发现 array.__proto__ 有 toString 属性,于是找到了

所以 array.toString 实际上就是第 2 步中找到的 array.__proto__.toString

可以想象,

  1. 如果 array.__proto__ 没有,那么浏览器会继续查看 array.__proto__.__proto__
  2. 如果 array.__proto__.__proto__ 也没有,那么浏览器会继续查看 array.__proto__.__proto__.__proto__
  3. 直到找到 toString 或者__proto__ 为 null。

上面这个搜索过程,是连着由__proto__ 组成的链子一直走的。

这个链子,就叫做原型链。

img

API

hasOwnProperty() 方法

hasOwnProperty() 方法会返回一个布尔值,指示对象是否具有指定的属性作为自身(不继承)属性。

var o = {
  name:'jack'
}
o.hasOwnProperty('name'); // true
o.hasOwnProperty('toString'); // false

Object.create() 方法

Object.create() 方法使用指定的原型对象和其属性创建了一个新的对象。

var a ={
  name:'jack'
}
var b = {
  form:'china'
}
a = Object.create(b)

img

prototype

对象.__proto__ === 对象.constructor.prototype

function Person(){}
Person.prototype.name = 'jack'
var person= new Person()
console.log(person.name)  // jack
console.log(person.__proto__ === Person.prototype) // true
console.log(Person.__proto__ === Person.constructor.prototype) // true
console.log(Person.__proto__ === Function.prototype) //true
console.log(Person.prototype.constructor === Person) // true
console.log(Object.prototype.__proto__ === null) // true
console.log(Object.prototype.constructor === Object) // true
console.log(person.constructor === Person) // true
console.log(person.constructor === Person.prototype.constructor) // true

img

只有对象才有__proto__,只有函数才有 prototype

JS原型继承

通过一段代码来展示js原型继承的过程

下面这段代码如何让Cat是Animal的子类

function Animal(){
	this.物种=物种
}
Animal.prototype.run={}
function Cat(name){
	Animal.call(this)      //这句话是调用Animal里的方法.由于下边会用new调用这段代码所以这里的this指向的是一个空的临时对象里.
	this.name=name
}
Cat.prototype.say=function(){}

Cat.prototype.__proto__ = Animal.prototype  //这种写法虽然可以但是会消耗大量的页面性能,而且对IE的兼容也是IE11才可以

Cat.prototype = Object.creat(Animal.prototype) //这种方法只能支持到IE9

function FakeAnimal(){}                      //创建一个空的对象这样避免this.物种=物种添加到原型里,因为new会调用Aniaml里的临时对象里的this.物种=物种
FakeAnimal.prototype = Animal.prototype  //让这个假的对象拥有真的Animal原型
Cat.prototype =new FakeAniaml()    //利用new方法会让让FakeAniaml.__proto__指向FakeAniaml.prototype,也就是让Cat.prototype拥有FakeAniaml.prototype属性方法

var cat = new Cat()  
Cat.run()

下面用ES6 class的方法写出上边一样的效果

class Animal{
  constructor(物种){
    this.物种=物种
  }
  run(){}
}
class Cat extends Animal{				//extends让Cat继承Animal的原型
  constructor(){
    super('加菲猫')				//把父类的函数运行一下
    this.name =name
  }
  say(){}
}

ES6 class如果想在原型链里添加属性目前看来不行或许下个版本会添加新的方法

如何用new面向对象编写jQuery

function $(selector){
	if(this instanceof $ === false){			//判断是否用new instanceof 检测this是否在$函数原型链里
		return new $(selector)					
	}else{
		var array = document.querySelectorAll(selector)
		for(var i = 0;i<array.length;i++){
			this[i]=array[i]
		}
	}
}

$.prototype.on =function(){
	console.log('你调用了 on')
	return this
}
$.prototype.addClass =function(){
	console.log('你调用了 addClass')
	return this

jQuery('div').on().addClass()

defer和async的区别

Legend

img

<script>

让我们先定义什么<script>没有任何属性。将解析该html文件,直到该脚本文件被命中,此时解析将停止,并请求获取文件(如果是外部文件)。然后在恢复解析之前执行脚本。

img

<script async>

async在html解析期间下载该文件,并在完成下载时暂停执行该文件。

img

<script defer>

defer在html解析期间下载文件,并且只在解析器完成后执行。defer脚本也可以按照它们在文档中出现的顺序执行。

img

  • 我什么时候该用什么?

    通常,您希望使用异步在可能的情况下,推迟那就没有属性。以下是一些一般规则:

    • 如果脚本是模块化的并且不依赖于任何脚本,则使用异步.。
    • 如果脚本依赖于或依赖于其他脚本,则使用推迟.。
    • 如果脚本很小并且依赖于异步然后使用内联脚本没有任何属性以上异步剧本。

参考资料:http://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html

立即执行函数

所谓立即执行函数就是声明一个匿名函数,立即执行它

一般有三种写法:

   (function(){})();
   (function(){}());
   !function(){}();

首先我们需要搞清楚函数表达式和函数声明区别,ECMAScript规范中定义的相当模糊:

函数声明必须带有标示符(Identifier)(就是大家常说的函数名称),而函数表达式则可以省略这个标示符:
函数声明:
  function 函数名称 (参数:可选){ 函数体 }
函数表达式:
  function 函数名称(可选)(参数:可选){ 函数体 }

其实我们常用的区分方式是根据上下文,如果function fn(){}作为右值出现(赋值表达式右边)那么就是表达式,否则就是函数声明。有几种看起来不常规的方式需要我们注意

new function fn(){}; //表达式,因为在new 表达式中
(function(){}());//表达式,在分组运算符中

这样我们就能理解第二种写法了,就是利用分组运算符改变了语法树。同样第三种写法其实是利用了一元运算符后面跟表达式的原理,我们也可以写成

  +function(){}()
  -function(){}()
  ~function(){}()

知道了这些看个传入参数调用的立即执行函数

(function f(f){
    return typeof f();
})(function(){return 1;});// "number"

立即执行函数不再是以空括号()来调用了,同时传入了一个function作为参数传入调用。
当函数执行有命名冲突的时候,函数依次填入 变量=》函数=》参数,所以最后被填入的参数f会覆盖函数定义f,typeof f()是对参数的调用,参数是立即执行函数传入的function参数,返回数字1,typeof 1是 "number"。

表达式返回值

函数定义表达式返回的是函数对象本身,我们在调用alert或者console.log的时候会调用其toString方法

console.log(function(){alert('a');}) //function (){alert('a');}

函数调用表达式自然是返回函数的return结果,但在JavaScript中并不是所有的函数都有return语句,对于没有return语句的function,其调用表达式返回undefined,对于只写个return的坑爹做法同样也是返回undefined

(function(){})(); //undefined
(function(){return;})();//undefined

对象创建表达式本来也应该很简单,返回new的对象就可以了

typeof new Date(); //"object"

当使用function的构造函数创建对象(new XXX)的时候,如果函数return基本类型或者没有return(其实就是return undefined)的时候, new 返回的是对象的实例;如果 函数return的是一个对象,那么new 将返回这个对象而不是函数实例。

本文摘抄子彭玉芳简书

BFC神奇背后的原理

BFC 已经是一个耳听熟闻的词语了,网上有许多关于 BFC 的文章,介绍了如何触发 BFC 以及 BFC 的一些用处(如清除浮动,防止 margin 重叠等)。

一、BFC是什么?

  在解释 BFC 是什么之前,需要先介绍 Box、Formatting Context的概念。

Box: CSS布局的基本单位

Box 是 CSS 布局的对象和基本单位, 直观点来说,就是一个页面是由很多个 Box 组成的。元素的类型和 display 属性,决定了这个 Box 的类型。 不同类型的 Box, 会参与不同的 Formatting Context(一个决定如何渲染文档的容器),因此Box内的元素会以不同的方式渲染。让我们看看有哪些盒子:

  • block-level box:

    display 属性为 block, list-item, table 的元素,会生成 block-level box。并且参与 block fomatting context;

  • inline-level box:

    display 属性为 inline, inline-block, inline-table 的元素,会生成 inline-level box。并且参与 inline formatting context;

  • run-in box:

    css3 中才有, 这儿先不讲了。

Formatting context

  Formatting context 是 W3C CSS2.1 规范中的一个概念。它是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。最常见的 Formatting context 有 Block fomatting context (简称BFC)和 Inline formatting context (简称IFC)。
  CSS2.1 中只有 BFC 和 IFC, CSS3 中还增加了 GFC 和 FFC。

BFC 定义

  BFC(Block formatting context)直译为”块级格式化上下文”。它是一个独立的渲染区域,只有Block-level box参与, 它规定了内部的Block-level Box如何布局,并且与这个区域外部毫不相干。

BFC布局规则:

  1. 内部的Box会在垂直方向,一个接一个地放置。
  2. Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠
  3. 每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
  4. BFC的区域不会与float box重叠。
  5. BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
  6. 计算BFC的高度时,浮动元素也参与计算

二、哪些元素会生成BFC?

  1. 根元素
  2. float属性不为none
  3. position为absolute或fixed
  4. display为inline-block, table-cell, table-caption, flex, inline-flex
  5. overflow不为visible

三、BFC的作用及原理

1. 自适应两栏布局

代码:

<style>
    body {
        width: 300px;
        position: relative;
    }
 
    .aside {
        width: 100px;
        height: 150px;
        float: left;
        background: #f66;
    }
 
    .main {
        height: 200px;
        background: #fcc;
    }
</style>
<body>
    <div class="aside"></div>
    <div class="main"></div>
</body>

Untitled Image

根据BFC布局规则第3条:

每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。

  因此,虽然存在浮动的元素aside,但main的左边依然会与包含块的左边相接触。

  根据BFC布局规则第四条:

BFC的区域不会与float box重叠。

  我们可以通过触发main生成BFC, 来实现自适应两栏布局。

.main {
    overflow: hidden;
}

  当触发main生成BFC后,这个新的BFC为了不与浮动的aside重叠。因此会根据包含块的宽度,和aside的宽度,自动变窄。效果如下:
img

2. 清除内部浮动

代码:

  <style>
    .par {
        border: 5px solid #fcc;
        width: 300px;
    }
 
    .child {
        border: 5px solid #f66;
        width:100px;
        height: 100px;
        float: left;
    }
</style>
<body>
    <div class="par">
        <div class="child"></div>
        <div class="child"></div>
    </div>
</body>

Untitled Image

根据BFC布局规则第六条:

计算BFC的高度时,浮动元素也参与计算

  

解决方案一般有两种

1). 利用 clear属性,清除浮动

对父元素加一个class:floatfix

添加如下CSS

.floatfix{
    *zoom:1;
}
.floatfix:after{
    content:"";
    display:table;
    clear:both;
}

2). 使父容器形成BFC

为达到清除内部浮动,我们可以触发par生成BFC,那么par在计算高度时,par内部的浮动元素child也会参与计算。

代码:

  .par {
    overflow: hidden;
}

 效果如下:

Untitled Image

3. 防止垂直 margin 重叠

代码:

  <style>
    p {
        color: #f55;
        background: #fcc;
        width: 200px;
        line-height: 100px;
        text-align:center;
        margin: 100px;
    }
</style>
<body>
    <p>Haha</p>
    <p>Hehe</p>
</body>

 页面:

Untitled Image

 两个p之间的距离为100px,发送了margin重叠。
  根据BFC布局规则第二条:

Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠

折叠的结果:

  • 两个相邻的外边距都是正数时,折叠结果是它们两者之间较大的值。
  • 两个相邻的外边距都是负数时,折叠结果是两者绝对值的较大值。
  • 两个外边距一正一负时,折叠结果是两者的相加的和。

我们可以在p外面包裹一层容器,并触发该容器生成一个BFC。那么两个P便不属于同一个BFC,就不会发生margin重叠了

  代码:

<style>
    .wrap {
        overflow: hidden;
    }
    p {
        color: #f55;
        background: #fcc;
        width: 200px;
        line-height: 100px;
        text-align:center;
        margin: 100px;
    }
</style>
<body>
    <p>Haha</p>
    <div class="wrap">
        <p>Hehe</p>
    </div>
</body>

 效果如下:

Untitled Image

浮动和绝对定位不与任何元素产生 margin 折叠

原因:

浮动元素和绝对定位元素不与其他盒子产生外边距折叠是因为元素会脱离当前的文档流,违反了上面所述的两个margin是邻接的条件同时,又因为浮动和绝对定位会使元素为它的内容创建新的BFC,因此该元素和子元素所处的BFC是不相同的,因此也不会产生margin的折叠。

自己经验添加

父子元素margin合并可以给父元素添加border或者padding解决

高级解决父子元素margin合并问题:

.no-collapse::before{content:'';display:table;},.no-collapse::after{content:'';display:table;}

总结

  其实以上的几个例子都体现了BFC布局规则第五条:

BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。

  因为BFC内部的元素和外部的元素绝对不会互相影响,因此, 当BFC外部存在浮动时,它不应该影响BFC内部Box的布局,BFC会通过变窄,而不与浮动有重叠。同样的,当BFC内部有浮动时,为了不影响外部元素的布局,BFC计算高度时会包括浮动的高度。避免margin重叠也是这样的一个道理。

本篇博客转载地址

JS的 '=='/'==='区别 以及 if语句判断

什么是if语句

if结构语句是判断一个表达式的布尔值(boolean)然后根据判断的真伪执行不同语句,true是真,false是伪。

基本语法结构:

if(boolean){
	语句 //true
}else{
    语句 //false
}

举个栗子:

var a = 3
if(a > 5){
    alert("true")
}else{
    alert("false")
}

判断流程:

  1. 首先变量声明了a,它的值是3。
  2. 接着判断布尔值a > 5 是否为真。
  3. 3 > 5那么执行alert("true")。
  4. 如果如果改变a的值大于5那么会执行alert("false")

boolean判断结果

类型 结果
undefined false
null false
Boolean 直接判断
string ""(空字符串)为false,其他为true
Object true
Number +0,-0,Nan为false,其他为true

相等运算符

在编写代码过程中为什么不用==运算符的呢,下面简单了解一下==运算符。
相等运算符x==y 对比值时会先将x与y进行类型转换在用===比较。
下面让我们看看==运算符的判断图:
相等运算符
如图[]==0 //true很难让人理解。而且目前我也不知道在什么情况下必须使用==时不能用===来代替的。

严格相等运算符

下面让我们看看===运算符的判断图:
严格相等运算符
严格相等运算符比较类型是否相同而不会做奇怪的类型转换在比较。

严格不相等运算符

如字面意思严格相等运算符===的另一面是严格不相等运算符!==,两者判断逻辑正好相反。
例如:[] === 0 //false [] !== 0 //true

本篇博客引用图地址(里面还有if()相关图)

JavaScript模块化

JavaScript模块化编程-require.js

随着网页越来越复杂、代码量越来越巨大,网页已经趋近于桌面应用,因此JavaScript模块化编程显得尤为重要。理想情况下,开发者只需要关注核心业务逻辑的实现,其他都可以加载别人已经写好的模块。

一、什么是模块?

我们先来看一下通常情况下js的写法

var a = 'hello'
function foo(){
  // do something
}

在这个例子中,我们暴露了一下全局变量,如a,foo等,并且代码没有很好的分离开来,这样的代码,可以称为“面条式代码”,这些代码看起来杂乱无章,我们希望的是这些代码也可以跟html页面中的div一样,分割成一个一个的“豆腐块”,例如

// play.js
function foo1(){}
function foo2(){}

// tab.js
function tab(){}

函数foo1和函数foo2分割成了两个独立的部分,它们负责不同的功能,相互之间互不影响,那么,我们可以说foo1和foo2是两个不同的模块。当foo1和foo2在play.js这个文件里,例如表示该文件是某个播放功能的实现,而tab.js文件表示某个切换功能的实现,这两个文件表示不同功能,它们之间相互独立,互不影响,也可以认为是两个独立的模块。
因此我们可以认为一个模块就是一个独立的功能,它可以是一个函数,也可以是一个对象,还可以是一个文件。

二、产生的问题

但是这又出现了另外一个问题,暴露了foo1和foo2两个全局变量,无法保证不与其他模块产生冲突。为了解决这个问题,我们可以通过立即执行函数来处理

!function(){
  foo1(){}
  foo2(){}
}()

我们执行立即执行函数可以避免产生全局变量的问题,这样foo1()和foo2()就都是局部变量了。
不过又出现了另外一个问题,假如模块与模块之间有依赖关系怎么办呢?一个简单的例子就是当我们引入jQuery时进行编程,jQuery就可以认为是一个依赖,我们可以通过传入这个依赖来解决

!function($){
  foo1(){}
  foo2(){}
}(jQuery)

但是当模块与模块之间有较多的依赖,一个模块必须要等到所依赖的模块加载完成,才可以进行后面的代码,而且这个模块必须要写到所依赖的模块之后,上面的例子中这部分的代码必须写到引入的jQuery之后,否则就会出现错误。因此,浏览器端的模块,不能采用”同步加载”(synchronous),只能采用”异步加载”(asynchronous)。

三、AMD

AMD是”Asynchronous Module Definition”的缩写,意思就是”异步模块定义”。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

 require([module], callback);

第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。我们要介绍的require.js就是AMD规范实现的一个库。

四、require.js

根据之前描述,当多个模块产生依赖,首先,加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序(比如上例的jQuery文件要在foo文件的前面),依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。
require.js可以很好的解决这两个问题

  1. 实现js文件的异步加载,避免网页失去响应;
  2. 管理模块之间的依赖性,便于代码的编写和维护。

使用require.js

首先新建一个requirejs-demo的目录,安装require.js

img

也可以去官网下载
安装完成之后把它移动到scripts子目录下,
在index.html中引入

<script data-main="scripts/main" src="scripts/require.js"></script>

data-main属性的作用是,指定网页程序的主模块。在上例中,就是scripts目录下面的main.js,这个文件会第一个被require.js加载。由于require.js默认的文件后缀名是js,所以可以把main.js简写成main。
假设现在 main.js 依赖 hello.js,hello.js 依赖 nany.js,最终需要在页面上输出’hello,nany’,那么我们可以这样实现

// main.js
require(['./hello'],function(x){
   document.write(x.x.name)
})
// 表示main.js主模块依赖hello.js当hello.js加载完成之后,再执行main.js指定的回调函数
// hello.js
define(['./nany'],function(x){
    return {
        x
    }
});
 // 在hello.js中,它依赖nany.js这个模块,加载nany.js完毕后,将nany.js保存的对象返回给hello.js

require.js加载的模块,采用AMD规范。也就是说,模块必须按照AMD的规定来写。具体来说,就是模块必须采用特定的define()函数来定义。如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中。如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。第二个参数为回调函数。

// nany.js
define({
    name: 'hello, nany'
})

当我们打开index.html,可以看到
img

参考

  1. Javascript模块化编程(一):模块的写法
  2. Javascript模块化编程(二):AMD规范
  3. Javascript模块化编程(三):require.js的用法

DOM 事件

DOM 事件

什么是事件

事件的三个特点

  1. 小 A 订阅/关注/监听了 XXX
  2. XXX 发生了变化
  3. 小 A 得到通知

如何监听事件

  • DOM level 0 事件:button.onclick = function() //给对象button一个oncilck属性并且赋值function()

  • DOM level 2 事件: button.addEventListener('click', function(){}) ;

删除button函数方法是将function(){}赋予一个ID,然后在remove它。例如:

var fn = function(){
  console.log(1)
}
button.removeEventListener('click',fn)
  • 兼容IE和火狐方案:
if(typeof button.addEventLitener === 'function'){
  button.addEventListener('click',function(e){
    console.log(e)
  })
  }else if(typeof button.attachEvent === 'function'){
  button.attachEvent('onclick',function(){
    console.log(window.event)
  })
  }else{
    button.onclick = function(e){
      var fn1 = function(){
        console.log(window.event)
      }
      fi1.call(this,e)
      }
     }

button.onclick 事件是如何在浏览器运行的

当html中添加了button这个元素并且在JS中添加了button.onclick = function(){console.log('hi')};在页面点击这个button却能在控制台显示'hi'这是为什么呢?在JS中并没有调用这个function。原来是浏览器发现button.onclick有没有赋值,如果有就会在用户点击这个button后执行button.onclick.call(button//this,事件相关信息对象//pageXoffset, which)
在html中给一个元素设置id后这个id就会自动全局变量,并且可以被JS直接调用。元素ID = xxx 等同于var xxx = document.querySelector('#xxx') ;

button.addEventListener 事件是如何在浏览器运行的

在JS中编写点击事件用的button.addEventListener('click', function(){})那么浏览器会把这个点击事件放到click的监听队列里面。在用户点击button的时候浏览器会遍历click这个队列里是否有函数,如果有则按照队列先进先出调用添加function.call(){button,{}};函数。

 <!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
<body>
<button id=xx>click me</button>
  <script>

    xx.addEventListener('click',function(){
      console.log(this)
      console.log(arguments)
    })
    
    // 当用户点击button
    //  xx.onclick.call(button, MouseEvent)
    
    
  </script>
</body>
</html>

根据上边的代码用户点击button控制台会返回:

<button id=xx>click me</button>; //this
[MouseEvent, callee: function, Symbol(Symbol.iterator): function] //点击时的相关信息

上边两种监听事件区别是button.addEventListener队列代替了属性,这样好处是可以监听多个函数会很简单

事件机制:冒泡 & 捕获

基础知识

  1. 操作系统最先知道用户点击了鼠标,浏览器次之
  2. child 被点击了,意味着 parent 也被点击了
  3. 如果我同时监听了 child 和 parent,那么谁先通知我?这是个问题。

捕获阶段

parent 先通知,child 后通知
button.addEventListener('click', fn, true)

冒泡阶段

child 先通知,parent 后通知
onclick 就是在冒泡阶段被通知
button.addEventListener('click', fn)

阻止默认事件

方法一: 添加 onclick = “returnfalse”
<a href = "https://baidu.com" onclock = "return false">百度</href>

方法二: 在JS中添加 button.onclick = function(){return false};

<script>
   button.onclick = function(){return false};
</script>

方法三:JS中添加event.preventDefault()

<script>
   button.addEventListener('click',function(event){
     event.preventDefault()
   })
</script>

停止冒泡

<script>
   button.addEventListener('click',function(event){
     console.log(event)
     event.stopPropagation() //停止冒泡
   })
</script>

e / event 是哪来的?

Event接口表示在DOM中发生的任何事件.一些是用户生成的(例如鼠标或键盘事件),而其他由API生成(例如指示动画已经完成运行的事件,视频已被暂停等等)。

e.target V.S. e.currentTarget

JavaScript基础总结

##基本语法

语句:

var a = 1 + 2;这条语句先用var命令,声明了变量a,然后将1 + 2的运算结果赋值给变量a。其中1+2是表达式(expression)。一般用;号进行语句结束

变量,常量

变量就是对值的一种引用。使用变量之前一定要变量声明var;let,如var a = 1 + 2;其中a是变量 1 和 2都是常量。

标识符

标识符(identifier)是用来识别具体对象的一个名称。最常见的标识符就是变量名,以及后面要提到的函数名。JavaScript语言的标识符对大小写敏感,所以a和A是两个不同的标识符。其中有一些重要规则。

第一个字符,可以是任意Unicode字母(包括英文字母和其他语言的字母),以及美元符号($)和下划线(_)。
第二个字符及后面的字符,除了Unicode字母、美元符号和下划线,还可以用数字0-9。

数据类型

  • 数值(number):整数和小数(比如1和3.14)

  • 字符串(string):字符组成的文本(比如”Hello World”)

  • 布尔值(boolean):true(真)和false(假)两个特定值。短路逻辑。

  • undefined:表示“未定义”或不存在,即由于目前没有定义,所以此处暂时没有任何值,没有赋值的基本对象。

  • null:表示无值,即此处的值就是“无”的状态。没有赋值的对象。

  • 对象(object):各种值组成的集合

  • Symbol

对象(object)又有三个子类型:狭义的对象(object),数组(array),函数(function)
六种假值:false,0,''或者" ",NaN,undefined,null
其中console.log()任何值返回的都是undefined

运算符

运算符优先级顺序:

  1. ()
  2. ,
  3. =

函数(function)

每次调用函数都会有result=fcuntion xxx .call(this,[arguments]),this默认是window,arguments是一个为数组对象 ,函数里都有return.

result=fcuntion xxx .call(this,[arguments]){
  return undefined //return默认 undefined
}

函数作用域

作用域有就近原则由下往上,声明变量会被提前

浏览器

浏览器给我门提供了window和document API
主要分为两大类BOM 和 DOM

var 与 let 区别

var 是函数作用域的变量,可以同一作用域重复覆盖声明变量
let 是块级作用域变量,且同一个作用域不会重复声明变量
let 与for语句一起使用的时候会在for块级作用域里添加一个与圆括号相等的let声明
let 有死亡区域是声明之前不允许使用let变量
浏览器第一次阅览代码只看声明 function和var,let
let高级理解

var a =1                          
function f1(){                   
                  // 实际浏览器阅读第一次代码会把var a 放在这里
  if(true){                          
    }else{                 
     var a = 2    // a= 2 保留在这里 
  }
  console.log(a)
}
f1() //undefined
------------------分割线------------------------
var a =1
function f1(){
  if(true){
    }else{
     let a = 2  //let a 则还在这里
  }
  console.log(a)
}
f1() //1
------------------分割线------------------------
var a =1
let a =2 //error
------------------分割线------------------------
for(let i=1;i<6;i++){    //在花括号内侧会自动添加一个匿名与圆括号相等的 let i=圆括号let i
  setTimeout(function(){
    console.log(i)
  },1000)
}              //0,1,2,3,4,5
------------------分割线------------------------
let a =1
{
  console.log(a)
  let a = 2
}               //error 虽然let a 提升到block第一行但是在a=2之前如果调用a则报错

setTimeout与setInterval方法的区别:

setTimeout(a(),500),在500毫秒后只执行1次方法a(),而setInterval(a(),500)是每隔500秒执行一次
方法a()。也就是说如果不把前者放在循环里,就不能实现后者的循环功能。

带你理解 JS 容易出错的坑和细节(本篇博客转载自掘金,作者夕阳)

本文原载于掘金**,作者夕阳**(饥人谷学员),转载已获作者授权。

本文重在列出并解释说明 JS 中各种容易出错的坑和细节,供大家更加深入理解为什么 JS 会这样

执行环境(Execution context)

var 和 let 的正确解释

当执行 JS 代码时,会生成执行环境,只要代码不是写在函数中的,就是在全局执行环境中,函数中的代码会产生函数执行环境,只此两种执行环境。

接下来让我们看一个老生常谈的例子,var

b() // call b 
console.log(a) // undefined 
var a = 'Hello world' 
function b() {
    console.log('call b')
} 

想必以上的输出大家肯定都已经明白了,这是因为函数和变量提升的原因。通常提升的解释是说将声明的代码移动到了顶部,这其实没有什么错误,便于大家理解。但是更准确的解释应该是:在生成执行环境时,会有两个阶段。第一个阶段是创建的阶段,JS 解释器会找出需要提升的变量和函数,并且给他们提前在内存中开辟好空间,函数的话会将整个函数存入内存中,变量只声明并且赋值为 undefined,所以在第二个阶段,也就是代码执行阶段,我们可以直接提前使用。

在提升的过程中,相同的函数会覆盖上一个函数,并且函数优先于变量提升

b() // call b second
 function b() {
    console.log('call b fist')
}
function b() {
    console.log('call b second')
}
var b = 'Hello world'

var 会产生很多错误,所以在 ES6中引入了 letlet 不能在声明前使用,但是这并不是常说的 let 不会提升,let 提升了,在第一阶段内存也已经为他开辟好了空间,但是因为这个声明的特性导致了并不能在声明前使用。

作用域

function b() {
    console.log(value)
}

function a() {
    var value = 2
    b()
}

var value = 1
a() 

可以考虑下 b 函数中输出什么。你是否会认为 b 函数是在 a 函数中调用的,相应的 b 函数中没有声明 value 那么应该去 a 函数中寻找。其实答案应该是 1。

当在产生执行环境的第一阶段时,会生成 [[Scope]] 属性,这个属性是一个指针,对应的有一个作用域链表,JS 会通过这个链表来寻找变量直到全局环境。这个指针指向的上一个节点就是该函数声明的位置,因为 b 是在全局环境中声明的,所以 value 的声明会在全局环境下寻找。如果 b 是在 a 中声明的,那么 log 出来的值就是 2 了。

异步

JS 是门同步的语言,你是否疑惑过那么为什么 JS 还有异步的写法。其实 JS 的异步和其他语言的异步是不相同的,本质上还是同步。因为浏览器会有一个 Event Queue 存放异步通知,JS 在执行代码时会产生一个执行栈,同步的代码在执行栈中,异步的在 Event Queue 中。只有当执行栈为空时,JS 才会去 Event Queue 中查看是否有需要处理的通知,有的话拿到执行栈中去执行。

function sleep() {
  var ms = 2000 + new Date().getTime()
  while( new Date() < ms) {}
  console.log('sleep finish')
}

document.addEventListener('click', function() {
  console.log('click')
})

sleep()
console.log('finish')

以上代码如果你在 sleep 被调用期间点击,只有当 sleep 执行结束并且 log finish 后才会响应点击事件。所以要注意 setTimeout 并不是你设定多久 JS 就会准时的响应,并且 setTimeout 也有个小细节,第二个参数设置为 0 也许会有人认为这样就不是异步了,其实还是异步。这是因为 HTML5 标准规定这个函数第二个参数不得小于 4 毫秒,不足会自动增加。

类型

原始值

JS 共有 6 个原始值,分别为 Boolean, Null, Undefined, Number, String, Symbol,这些类型都是值不可变的。

有一个易错的点是:虽然 typeof null 是 object 类型,但是 Null 不是对象,这是 JS 语言的一个很久远的 Bug 了。

深浅拷贝

对于对象来说,直接将一个对象赋值给另外一个对象就是浅拷贝,两个对象指向同一个地址,其中任何一个对象改变,另一个对象也会被改变

var a = [1, 2]
var b = a
b.push(3)
console.log(a, b) // -> 都是 [1, 2, 3]

有些情况下我们可能不希望有这种问题,那么深拷贝可以解决这个问题。深拷贝不仅将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上。深拷贝有多种写法,有兴趣的可以看这篇文章**

函数和对象

this

this 是很多人会混淆的概念,但是其实他一点都不难,你只需要记住几个规则就可以了。

function foo() {
  console.log(this.a)
}
var a = 2
foo() 

var obj = {
  a: 2,
  foo: foo
}
obj.foo() 

// 以上两者情况 this 只依赖于调用函数前的对象,优先级是第二个情况大于第一个情况 // 以下情况是优先级最高的,this 只会绑定在 c 上 
var c = new foo()
c.a = 3 
console.log(c.a)

// 还有种就是利用 call,apply,bind 改变 this,这个优先级仅次于 new

以上几种情况明白了,很多代码中的 this 应该就没什么问题了,下面让我们看看箭头函数中的 this

function a() {
    return () => {
        return () => {
            console.log(this)
        }
    }
}
console.log(a()()()) 

箭头函数其实是没有 this 的,这个函数中的 this 只取决于他外面的第一个不是箭头函数的函数的 this。在这个例子中,因为调用 a 符合前面代码中的第一个情况,所以 this 是 window。并且 this 一旦绑定了上下文,就不会被任何代码改变。

下面我们再来看一个例子,很多人认为他是一个 JS 的问题

var a = {
    name: 'js',
    log: function() {
        console.log(this)
        function setName() {
            this.name = 'javaScript' 
                      console.log(this)
        }
        setName()
    }
}
a.log()

setName 中的 this 指向了 window,很多人认为他应该是指向 a 的。这里其实我们不需要去管函数是写在什么地方的,我们只需要考虑函数是怎么调用的,这里符合上述第一个情况,所以应该是指向 window。

闭包和立即执行函数

闭包被很多人认为是一个很难理解的概念。其实闭包很简单,就是一个能够访问父函数局部变量的函数,父函数在执行完后,内部的变量还存在内存上让闭包使用。

function a(name) {
    // 这就是闭包,因为他使用了父函数的参数 
       return function() {
        console.log(name)
    }
}
var b = a('js')
b() // -> js

现在来看一个面试题

function a() {
    var array = []

    for(var i = 0; i < 3; i++) {
        array.push(
            function() {
                console.log(i)
            }
        )
    }

    return array
}

var b = a()
b[0]()
b[1]()
b[2]()

这个题目因为 i 被提升了,所以 i = 3,当 a 函数执行完成后,内存中保留了 a 函数中的变量 i。数组中 push 进去的只是声明,并没有执行函数。所以在执行函数时,输出了 3 个 3。

如果我们想输出 0 ,1,2 的话,有两种简单的办法。第一个是在 for 循环中,使用 let 声明一个变量,保存每次的 i 值,这样在 a 函数执行完成后,内存中就保存了 3 个不同 let 声明的变量,这样就解决了问题。

还有个办法就是使用立即执行函数,创建函数即执行,这样就可以保存下当前的 i 的值。

function a() {
    var array = []

    for(var i = 0; i < 3; i++) {
        array.push(
            (function(j) {
                return function() {
                    console.log(j)
                }
            }(i))
        )
    }

    return array
} 

立即执行函数其实就是直接调用匿名函数

function() {} ()

但是以上写法会报错,因为解释器认为这是一个函数声明,不能直接调用,所以我们加上了一个括号来让解释器认为这是一个函数表达式,这样就可以直接调用了。

所以我们其实只需要让解释器认为我们写了个函数表达式就行了,其实还有很多种立即执行函数写法

true && function() {} ()
new && function() {} ()

立即执行函数最大的作用就是模块化,其次就是解决上述闭包的问题了。

原型,原型链和 instanceof 原理

原型可能很多人觉得很复杂,本章节也不打算重复复述很多文章都讲过的概念,你只需要看懂我画的图并且自己实验下即可

function P() {
    console.log('object')
}

var p = new P()

img

原型链就是按照 __proto__ 寻找,直到 Object。instanceof 原理也是根据原型链判断的

p instanceof P // true
p instanceof Object // true 

表单的基础知识

在 HTML 中,表单是由 form 元素来表示的,而在 JavaScript 中,表单对应的则是 HTMLFormElement 类型。HTMLFormElement 的属性和方法。

acceptCharset: 服务器能够处理的字符集;等价于 HTML 中的 accept-charset 特性。
action: 接受请求的 URL;等价于 HTML 中的 action 特性。
elements: 表单中所有控件的集合(HTMLCollection)。
enctype: 请求的编码类型;等价于 HTML 中的 enctype 特性。
length: 表单中控件的数量。
method: 要发送的 HTTP 请求类型,通常是"get"或"post";等价于 HTML 的 method 特性。
name: 表单的名称;等价于 HTML 的 name 特性。
reset(): 将所有表单域重置为默认值。
submit(): 提交表单。
target: 用于发送请求和接收响应的窗口名称;等价于 HTML 的 target 特性。

<!-- 通用提交按钮 -->
<input type="submit" value="Submit Form">

<!-- 自定义提交按钮 -->
<button type="submit">Submit Form</button>

<!-- 图像按钮 -->
<input type="imgage" src="xxx.jpg">

<!-- 通用重置按钮 -->
<input type="reset" value="Resrt Form">

<!-- 自定义重置按钮 -->
<button type="reset">Reset Form</button>
<div>
    <label for="username">姓名</label>  
    <input id="username" name="username" type="text">   
</div>
<div>
  <label for="age">年龄</label> 
  <input id="age" name="age" type="text">  
</div>

//等价于

<div>
  <label>
    姓名
    <input name="username" type="text">
  </label>
</div>
<div>
  <label>
    年龄
    <input type="text" name="age">
  </label>
</div>


当点击姓名和年龄时,文本框就会被选中,两个方法都可以获得同样的效果,通常使用后一种。

Javascript Promise基础知识

我们在之前的文章中简述了一下JS中的回调函数以及实现方式浅谈JavaScript的回调函数,其中谈到了promise实现异步编程,在了解promise之前,我们先来看一下一般实现异步编程的方法。

回调函数

在ajax中,我们经常用到回调函数来表示当请求成功之后所进行的操作,例如

ajax({
  method: 'GET',
  url: '/xxx',
  success: function(responseText){
    console.log(responseText)
  }
  error: function(status){
   console.log('请求失败,状态码为' + status)
  }
})

function ajax(options){
  let {method, url, success, error} = options
  if(!method){ throw new Error('Error!没有传入method') } 
  url = url || location.href
  let xhr = new XMLHttpRequest()
  xhr.open(method, url)
  xhr.onreadystatechange = function(){
    if(xhr.readyState === 4){
      if(xhr.status>=200 && xhr.status < 400){
        success && success.call(null, xhr.responseText, xhr)
      }else if(xhr.status >= 400){
        error && error.call(null, xhr.status, xhr)
      }
    }
  }
  xhr.send()
}

上面的例子中,通过自己封装的一个ajax方法,当请求成功/失败之后,通过回调函数,实现成功/失败的事件,这样看似乎没有什么问题,但是当请求成功时,需要执行多个回调函数,该怎样呢?
正常情况下,可以这样实现

ajax({
  method: 'GET',
  url: '/xxx',
  success: function(responseText){
    success1()
    success2()
  }
  error: function(status){
    console.log('请求失败,状态码为' + status)
  }
})

在成功的调用后,我们可能需要把返回的结果用在另一个Ajax请求中,也可能请求多次,这样就可能会出现回调函数之间层层嵌套的情况(函数连环套),当这种嵌套的回调函数变得难以理解,开发人员需要仔细分析哪些代码用于应用的业务逻辑,而哪些代码处理异步函数调用的,代码结构支离破碎。错误处理也分解了。

Promise

为了降低异步编程的复杂性,开发人员一直寻找简便的方法来处理异步操作。其中一种处理模式称为promise(注意:promise是一种处理模式),它代表了一种可能会长时间运行而且不一定必须完整的操作的结果。这种模式不会阻塞和等待长时间的操作完成,而是返回一个代表了承诺的(promised)结果的对象。

jQuery中的ajax对象返回了一个类似promise的对象,并提供了then方法来实现异步处理

let promise = $.ajax({ method: 'GET', url: '/xxx'})
promise.then(success1).then(success2)

then方法提供了两个参数,resolvedHandler,rejectedHandler。resolvedHandler 回调函数在promise对象进入完成状态时会触发,并传递结果;rejectedHandler函数会在拒绝状态下调用。then方法会返回另一个promise对象,以便于形成promise管道。后来ES6对其进行了规范,并提供了原生的Promise对象。

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise对象有以下两个特点

  1. 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Fulfilled(已成功)和Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Fulfiled(resolve)和从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 Resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

ES6规定,Promise是一个构造函数,可以通过它来创建Promise实例

let promise = new Promise(function(resolve,reject){
  if( /* 异步操作成功 */){
    resolve()
  }else{
    reject()
  }
})

Promise构造函数接受一个函数作为参数,该函数的两个参数也是函数,分别是resolve和reject。它们由 JavaScript 引擎提供,不用自己部署。

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 Pending 变为 Resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 Pending 变为 Rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise实例生成以后,可以用then方法分别指定Resolved状态和Rejected状态的回调函数。

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为Resolved时调用,第二个回调函数是Promise对象的状态变为Rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。

针对于我们一开始的例子,用Promise可以这样来实现

function ajax(options){
  return new Promise(function(resolve, reject){ // 1
    let {method, url} = options
    if(!method){ throw new Error('Error!没有传入method') } 
    url = url || location.href
    let xhr = new XMLHttpRequest()
    xhr.open(method, url)
    xhr.onreadystatechange = function(){
      if(xhr.readyState === 4){
        if(xhr.status>=200 && xhr.status < 400){
          resolve.call(null, xhr.responseText) // 2
        }else if(xhr.status >= 400){
          reject.call(null, xhr) // 3
        }
      }
    }
    xhr.send()
  })
}
 ajax({ method: 'GET', url:'/x'}).then(success1, error).then(success2,error)

有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

参考

  1. 阮一峰 ES6 Promise
  2. JavaScript异步编程的Promise模式

本篇博客完全摘录自plainnany:原文地址

自动加载,懒加载,预加载

自动加载的主要方法

  1. 如何判断一个元素是否出现在窗口可视范围(浏览器的上边缘和下边缘之间,肉眼可视)。写一个函数 isVisible实现

    function isVisible($node){
        var winHeight = $(window).height()     // 获取视口高度
        var scrollTop = $(window).scrollTop()  // 获取视口垂直滚动距离从上向下
        var $offset = $node.offset().top 	   // 获取选择的元素top,它相对于文档偏移的距离
        var $height = $node.outHeight(true)	   // 获取选择的元素,它包含margin的高度
        
        if(winHeight + scrollTop > $offset && $offset + $height > scrollTop){
                return true;
            }else{
                return false;
            }
        }
    }
  2. 当窗口滚动的时候判断一个元素是否出现在视口范围.每次出现都打印出true.用代码实现:

    var $div = $('div')
    $(window).on('scroll',function(){
        $div.not('.show').each(function(){
            if(isVisible($(this))){
                showDiv($(this))
            }else{
               console.log("false") 
            }
        })
    })
    
    function showDiv($div){
        $div.each(function(){
            console.log("true")
        })
    }
    
    function isVisible($node){
        var winHeight = $(window).height()
        var scrollTop = $(window).scrollTop()
        var $offset = $node.offset.top
        var $height = $node.outHeight(true)
        
        if(winHeight + scrollTop > $offset && $offset + $height > scrollTop){
        console.log('winHeight:',winHeight,'scrollTop:',scrollTop,'$offset:',$offset,'$height:',$height)
            return true
        }else{
            return false
        }
    }
  3. 当窗口滚动时,判断一个元素是不是出现在窗口可视范围。在元素第一次出现时在控制台打印 true,以后再次出现不做任何处理。用代码实现

        var $div = $('div');
    
        $(window).on('scroll',function(){
            $div.not('.show').each(function(){
                if(isVisible($(this))){
                    showDiv($(this))
                }else{
                    console.log('false')
                }
            })
    
        })
    
        function showDiv ($div) {
            $div.each(function(){
                $(this).addClass('show')
                console.log('true')
            })
        }
    
        function isVisible($node){
            var winHeight = $(window).height();
            var scrollTop = $(window).scrollTop();
            var $offset = $node.offset().top;
            var $height = $node.outerHeight(true);
    
            if(winHeight + scrollTop > $offset && $offset + $height > scrollTop){
                console.log('winHeight:',winHeight,'scrollTop:',scrollTop,'$offset:',$offset,'$height:',$height)
                return true;
            }else{
                return false;
            }
        }

图片懒加载的原理是什么?

  • 自动加载是利用ajax向服务器交换信息,并且不用重新加载网页而更新网页,自动加载的目的主要是对于大型网站节省流量,让用户看到一部分之后在加载另一部分.
  • 对于这个问题,我们想到了一个解决办法:直到该图片出现在用户的视线范围内时,才加载该图片。
  • 关键的步骤:
    • ①判断图片是否可见:如果:屏幕滚动的高度+窗口高度 > 图片到页面顶部的距离,那么该图片即是可见 *的;
    • ②转换图片的可见状态:修改图片原本的src值。我们可以给每一个图片的src设一个相同的初始值,这样只会发起一次请求,如果不设置,可能会出现“X”就很难看。当图片是可见时,再修改图片的src属性,这时,图片的内容就能被加载出来了。

用DOM制作的一个simple-demo实现效果

源代码地址

所需要的一些知识点

服务器

npm i -g http-server
http-server -c-1

outline: 1px solid red; 显示边线与border不一样的是不占用位置

页面加载页面如果IMG高度设置的是height:100%那么加载的时候会闪一下,原因是因为页面加载时图片还没读取完高度是不知道的。消除这个BUG的小技巧是给这个IMG加一个DIV 做容器。然后设置这个div的padding-top:100%,然后用position定位img。

<input disabled="value"> //禁用input功能 无法点击等等.

window.onresize 事件会在窗口或框架被调整大小时发生。

ul.insertAdjacentHTML('beforeend','reuqest.responseText')

split() 方法使用指定的分隔符字符串将一个String对象分割成字符串数组,以将字符串分隔为子字符串,以确定每个拆分的位置。

button.disabled = true/false 按钮失效

list.insertAdjacentHTML('beforeend',"text") 将text内容传送到html 末尾

object.getBoundingClientRect()可以获取到object的相对于视口左上角的绝对定位top bottom left right

document.documentElement.clientHeight 获取当前视口的高度

html中img src=""相当于img src="当前页面地址" 如果想让浏览器不自动添加地址可以在src="about:blank"

预加载有两种方法:<link rel="prefetch" href="xxxx">

var Imageinload = new Image();Imageinload.src="xxxx"

$(selector).not(#xx)选择selector中ID=xx的元素

$(selector).each(function(){}) 方法规定为每个匹配元素规定运行的函数。

参考博客

米弥轮 赖加载

this

this的重要性

this是JavaScript中最为关键的语法点,一般在简单项目里,this可有可无,但大部分的开发任务中,this起着关键作用。

this的由来

以下面JS代码为例,调用该自身对象的其它属性的值。

var obj = { 
name: 'cheche',
say: function(){
      console.log('你好,'+obj.name)
       }
 }
obj.say()   // 'cheche'

假如我们要在控制台打印出你好,cheche,通过上面的JS代码虽然用变量获取可以实现,但通过写obj.name,这样严重依赖了变量名,如果变量名obj变了,那么里面用到变量名的地方都得改变,上面代码的写法显然不可取。
为了不依赖变量名,可以给obj.say()传入一个参数,

var obj = {
name: 'cheche',
say: function(person,问候语){
       console.log(问候语+','+person.name)
     }
}
obj.say(obj,'早')   // '早,cheche'

这样的写法算是比较友好的,没有依赖变量名。但是得传入两个参数,那能不能只传一个参数?
如果只传一个参数便是这种形式obj.say('早'),但obj.say(obj,'早')obj.say('早')这二者很难区分,所以JS之父布兰登·艾克把第一个参数隐藏起来,但隐藏起来之后,怎么知道person.name是什么,于是就引入了this作为它的第一个参数。那么上面的代码就变成了这样,

var obj = {
name: 'cheche',
say: function(问候语){
     console.log(问候语+','+this.name)
     }
}
obj.say('早')      // '早,cheche'

其中obj,say('早')形式上等价于obj.say.call(obj,'早'),前者是一种语法糖的形式,后面这种才是计算机真正认为的,浏览器认为obj === this,所以我们可以认为this就是函数call的第一个参数。

this的概念

this的数据类型

var obj = {
  foo: function(){
    console.log(this)
  }
}
var bar = obj.foo
obj.foo()   //obj
bar()	   //undefined => Window

上面代码里,调用obj.foo()打印出来的是obj这个对象自身,因为obj.foo()形式上等于obj.foo.call(obj),而call的第一个参数就是this,所以我们知道了this就是一个对象,即使你指定的this是数字1,它也会变为一种对象的形式。
img
在调用bar()时,并没有指定this,形式上相当于bar.call(undefined),这时浏览器会默认this等于window对象。但在nodejs里,this === global,global是个全局对象。
不要以为bar = obj.foo,就认为bar() === obj.foo(),函数是独立存在的,不会进行补全。

this这个参数

当函数未调用时,this只是一个参数,无法确认this是什么。

var fn = function(p1){
console.log(this)
console.log(p1)
}

这是你无法确认this和p1是什么,因为他们只是参数。当你调用之后,便可以指定参数了。

this的确定

既然我们知道,函数传的第一参数就是this,那我们该如何确定它?

  1. 通过console.log(this)
    这是最简单的方法,但是缺乏说服力。
  2. 读DOM或jQuery的源代码
    水平不够,读不懂
  3. 查对应API的文档
    html:<button id="xxx">点我</button>
    js:xxx.addEventListener('click',function(){console.log(this)}) //点击button
    

通过查EventTarger.addEventListener文档,可以知道this值是触发事件元素的引用。所以,this === xxx,即等于button元素。

改变this的值

var obj = {
 fn : function(){
console.log(this)}
}
setTimeout(obj.fn,1000)  //window对象

当向setTimeout()传递一个函数时,该函数的this会指向一个错误的值,在非严格模式下,this是window对象,而在严格模式下,this是undefined。那我们该如何让this为obj这个对象呢?有三种方法。

  1. 用包装函数
    setTimeout(function(){obj.fn()},1000),这样强制指定this为obj

  2. bind

    setTimeout(obj.fn.bind(obj),1000)

    obj.fn.bind(obj)会返回一个新的函数,在形式上等于

    function(){obj.fn()}

    可以理解为在

    obj.fn

    外套一层,重新写个call覆盖原来的this。

  3. this的高级用法

var a = [1,2,3]
var b = a.join()
var c = [4,5,6]
var d = a.join.call(c)
console.log(b)  // '1,2,3'
console.log(d)  // '4,5,6'

array.join()是个数组的API,是将数组所有元素连接到字符串中,默认以逗号隔开。那么a.join(),里面的join是怎么获取到数组a的,它的原理是什么?我们可以做出以下猜想。

var a = [1,2,3]
var a.join = function(){
var result = ''
for(var i=0;i<this.length;i++){
  if(i !== this.lenght -1){  //最后一个不用加逗号
   result += this[i] + ','
  }else{
    result +=this[i] }
}                            

我们做出猜想,join函数里有用了this,而这个this等于数组a。如果把this的传入改为数组c,那么join函数会把数组c变成字符串d。也就是说数组c调用了数组a的方法。
如果c不是一个数组,而是一个伪数组,同样可以用a的方法。a的slice方法操作了this,把c当作this给a的slice操作就行了。slice()是一个截取数组的方法

var a = [1,2,3]
var c = {0:4,1:5,2:6,length:3}
var e = a.slice.call(c,0,2)
console.log(e) // [4,5]
  1. 打出想要的this
html:
<button id=xxx name="curry"
javascript:
var obj = {
name: 'allen',
say: function(){
    xxx.onclick = function(){console.log(this.name)}
    }
}
obj.say()  //xxx.name

此时this是xxx这个按钮,我们想打出’cheche’,怎么办?

  • 古老方法
    将函数外的this赋值给一个变量,然后把函数里的this改成那个变量就行了。

    var obj = {
    name: 'cheche',
    var _this = this
    say: function(){
       xxx.onclick = function(){console.log(_this.name)}
      }
    }
    obj.say()  // 'cheche'
  • bind

    var obj = {
    name: 'cheche',
    say: function(){
       xxx.onclick = function(){console.log(_this.name)}
      }.bind(this)
    }
    obj.say()  // 'cheche'

此时,bind传入的this是函数外的this,可以理解为bind()是最里面的this传入,当然以它传入为准。

  • 箭头函数
    var obj = {name: 'cheche',say: function(){xxx.onclick = ( ) =>  {console.log(this.name)}}obj.say()  // 'cheche'

箭头函数没有隐藏的this,它自身并不会传入this,所以箭头函数里的this不是被点击的元素,而是它外面的this。

箭头函数无法用call()指定this

this的特殊用法

在new关键字调用构造函数时,构造函数里的this表示的是一个实例对象。

下边this因为

function Arr(id){
 this.id = id
}
var s = new Arr(1)

此时这个this就是对象`s’。

下边代码this指向的是被点击button元素

var button = document.querySelector('button');
var name = 'jack';
var object = {
    name: 'lucy',
    sayHi: function() {
        var that = this;
        button.onclick = function() {
            console.log(that.name);
        }
    },

}
object.sayHi();   //btn

Git、GitHub、Node.js 服务器

远程登录 & 远程仓库

远程登录另一台机器

SSH 原理与运用14

  1. 用密码登录 ssh user@host

  2. 用 ssh key 登录

    1. 首先你要准备两个 key

      ssh-keygen -t rsa -b 4096 -C "[email protected]"
      
      1. 一个公钥(yue4)(public key)
      2. 一个私钥(private key)
      3. 复制公钥 cat ~/.ssh/id_rsa.pub
    2. 把 public key 给远程机器

      • ssh-copy-id user@host
      • public key 可以随便给多个 host

退出一台机器

  1. exit
  2. logout
  3. ctrl + D

git 基础(之后会讲高级)

配置

alias ga="git add"
alias gc="git commit -v"
alias gp="git push"
alias gl="git pull"
alias gcl="git clone"
alias gst="git status -sb"
git config --global user.name xxx #方便产品经理找(怼)你
git config --global user.email yyy #方便产品经理找(怼)你
git config --global push.default simple 
git config --global core.quotepath false #防止文件名变成数字
git config --global core.editor "vim" #使用vim编辑提交信息

这些配置都是在写 ~/.gitconfig 文件而已

代码演示

mkdir git-demo
cd git-demo
git init
touch 1.txt # 编辑第一行文本
git status -sb
git add .
git status -sb
git commit -v
git remote add origin git@xxxxxxxx # 将当前仓库与远程仓库建立联系
git push -u origin master # -u 的意思是将本地分支与远程分支建立联系

git init

创建 .git 目录(本地仓库)

git add

多行文字 纳入 git 控制范围(stage、历史的舞台)

git commit

存入 .git 目录

git log

展示历史

git remote add xxx yyy

添加一个远程仓库,名字为 xxx,地址为 yyy

如果你要修改 yyy,可以使用 git remote set-url xxx zzz

git push

将 .git 上传到另一个目录

git clone

git clone 的作用
\1. 新建目录 xxx
\2. 解压远程 .git 目录到 xxx/.git


git clone 会帮你新建目录的!


git pull

更新本地仓库(.git)和本地文件

原则

  1. git push 之前必须 git pull
  2. git pull 之前必须 git commit
  3. git commit 之前有时必须 git add

中级语法

git stash
git branch
git checkout
git merge
git reset
git reflog

Node.js 服务器

我们已经知道了

  1. 请求(request)
  2. 响应(response)

那么 Node.js 如何接收请求,发出响应呢?

  1. request 对象包含请求的所有信息
  2. response 对象包含响应的所有信息

其他 API 只需看 文档8

演示代码15

细节

  1. 如何默认访问 index.html
  2. 文件后缀无用,HTTP 协议只看 Content-Type
  3. HTML、CSS、JS、JSON…… 所有所有的响应,都只是字符串

其他

  1. 如何中断命令行程序
  2. 如何让命令行程序在后台运行
  3. node index.js > output.log 2>&1 &
  4. node index.js >> output.log 2>&1 &

GitHub

把代码存到 GitHub

细节

  1. ssh 协议和 http 协议 的区别
  2. 生成 ssh-key 并上传
  3. 上传本地 .git 到 GitHub
  4. 不要在 git 目录里面再次 git initgit clone,除非你知道你在做什么。

GitHub Pages

如何预览 GitHub 上的页面

GitHub Issues

你可以用 Issues 在一分钟内搭建一个博客,功能包括:

  1. 评论
  2. Markdown 编写
  3. 可以点赞

JavaScript学习总结之数组

Array类型

"可以保存任何类型的数据,并且大小可以调整的.“

一、创建实例

1、使用Array构造函数

var color = new Array();

2、使用数组字面量表示法

var color = ["red","blue","yellow"];

  • 其中需要注意的是:强烈建议不要使用 var option = [,,,,];

3、元素的读取和设置使用【】+下标

4、数组长度使用.length

二、检测数组

对于一个网页或者一个全局作用域而言,使用instanceof操作符就能得到满足的结果,但是对于网页包含多个框架,从而会存在两个以上不同版本的Array构造函数。所以引进了Array.isArray()方法,具体用法如下:

if(Array.isArray(value)) { }

三、转换方法

如前所述,所有对象都具有toLocaleString()、toString()和valueOf()方法。

1、toString()方法:返回由数组中每个值得字符串形式拼接而成的一个以逗号分隔的字符串;

2、toLocaleString()方法:经常也会返回与toString()和valueOf()方法相同的值,但唯一不同之处在于:为了取得每一项的值,调用的是每一项的toLocaleString()方法,而不是toString()方法,请看下面的例子

var person1 = {
   toLocaleString : function () {
          return "Nikolaos";
  },
  toString : function() {
         return "Nicholas";
  }
};
var person2 = {
  toLocaleString : function () {
          return "Grigorios";
  },
  toString : function() {
         return "Greg";
  }
};

var people = [person1,person2];
alert(people);  //Nicholas,Greg
alert(people.toString());  //Nicholas,Greg
alert(people.toLocaleString());  //Nikolaos,Grigorios

4、前三种方法,在默认情况下都会返回以逗号分隔的字符串。而如果使用join()方法,则可以使用不同的分隔符来构建这个字符串。join()方法只接受一个参数,即用作分隔符的字符串,然后返回包含所有数组项的字符串。

四、栈和队列方法

1、栈方法

function Stack(){
     var items=[];//用数组来保存栈中的数据;
     this.push=function(element){
      items.push(element);
     };
     this.pop=function(){
      return items.pop();
     };
     this.peek=function(){
      return items[items.length-1];
     };
     this.isEmpty=function(){
      return items.length == 0;
     };
     this.size=function(){
      return items.length;
     }
     this.clear=function(){
      items=[];
     }
     this.toString=function(){
      return items.toString();
     };
   }

主要是两个方法:push():添加到数组的末尾;pop():从末尾移除最后一项;

2、队列方法

function Queue(){
     var items=[];//用数组来保存栈中的数据;
     this.enqueue=function(element){
      items.push(element);
     };
     this.dequeue=function(){
      return items.shift();
     };
     this.font=function(){
      return items[0];
     };
     this.isEmpty=function(){
      return items.length == 0;
     };
     this.size=function(){
      return items.length;
     }
     this.clear=function(){
      items=[];
     }
     this.toString=function(){
      return items.toString();
     };
   }

主要两个方法:push()方法和shift()方法:从数组的前端取得项;

五、重排序方法

1、reverse()方法:返回数组项的逆序;

2、sort()方法:

sort(function compare(value1,value2){
  return value1 - value2; //升序
  return value2 - value1; //降序
});

六、操作方法

1、concat()方法可以基于当前数组中的所有项创建一个新数组,将接收到的参数添加到这个副本的末尾;

var colors = ["red","green","blue"];
var colors2 = colors.concat("yellow",["black","brown"]);
alert(colors2); // red,green,blue,yellow,black,brown;

2、slice()方法可以基于一个或多个项创建一个新数组。可以接收两个参数,一个起始位置,一个结束为止,当只有一个参数时,从起始位置到数组末尾项;

var colors = ["red","green","blue","yellow"];
var colors2 = colors.slice(1);
var colors3 = colors.slice(1,2);
alert(colors2); //green,blue,yellow;
alert(colors3); // green,blue;

3、splice()方法:删除、插入、替换,返回一个包含从原始数组中删除的项的数组。

删除:splice(起始位置,删除项数);

插入:splice(起始位置,0,...,...);

替换:splice(起始位置,1,...,...);

七、位置方法

1、indexOf(起始位置,寻找的元素):从数组开头开始向后找;

2、lastIndexOf(起始位置,寻找的元素):从数组末尾开始向前找;

这两个方法都返回要查找的项在数组中的位置,或者在没有找到的情况下返回-1;

八、迭代方法

ECMAScript5为数组定义了5个迭代方法。每个方法都接受两个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域对象--影响this的值。传入这些方法中的函数会接受三个参数:数组项的值、该项在数组中的位置和数组对象本身。

1、every(function(item,index,array){}) 每一项都返回true,结果才为true

var numbers = [1,2,3,4,5,4,3,2,1];
var everyResult = numbers.every(function(item,index,array){
   return (item>2);
});
alert(everyResult); //false;

2、some(function(item,index,array){}) 对任一项返回true,结果就为true;

var someResult = numbers.some(function(item,index,array){
     return (item>2);
});
alert(someResult); //true;

3、filter(function(item,index,array){}) 返回该函数会返回true的项组成的数组

var numbers = [1,2,3,4,5,4,3,2,1];
var filterResult = numbers.filter(function(item,index,array){
   return (item>2);
});
alert(filterResult); //[3,4,5,4,3];

4、forEach(function(item,index,array){}) 对数组每一项执行函数,没有返回值

5、map(function(item.index,array){}) 返回每次函数调用结果的数组

var numbers = [1,2,3,4,5,4,3,2,1];
var mapResult = numbers.map(function(item,index,array){
   return item*2;
});
alert(mapResult); //[2,4,6,8,10,8,6,4,2];

九、归并方法

ECMAScript5还信增了两个归并数组的方法:reduce()和reduceRight()。这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。这连个方法都接收两个参数:一个在每一项上调用的函数和(可选)作为归并基础的初始值。传入的函数接收4个参数:前一个值、当前值、项的索引和数组对象本身。

1、reduce(function(prev,cur,index,array){}),从数组的第一项开始,逐个遍历到最后。使用reduce()方法可以执行求数组中所有值之和的操作。比如:

var values = [1,2,3,4,5];
var sum = values.reduce(function(prev,cur,index,array){
       return prev + cur;
});
alert(sum); //15;

2、reduceRight(function(prev,cur,index,array){}) 从数组的最后一项开始hi,向前遍历到第一项

本篇转载地址

block,inline和inline-block概念和区别

总体概念

block和inline这两个概念是简略的说法,完整确切的说应该是 block-level elements (块级元素) 和 inline elements (内联元素)。block元素通常被现实为独立的一块,会单独换一行;inline元素则前后不会产生换行,一系列inline元素都在一行内显示,直到该行排满。
大体来说HTML元素各有其自身的布局级别(block元素还是inline元素):
常见的块级元素有 DIV, FORM, TABLE, P, PRE, H1~H6, DL, OL, UL 等。
常见的内联元素有 SPAN, A, STRONG, EM, LABEL, INPUT, SELECT, TEXTAREA, IMG, BR 等。
block元素可以包含block元素和inline元素;但inline元素只能包含inline元素。要注意的是这个是个大概的说法,每个特定的元素能包含的元素也是特定的,所以具体到个别元素上,这条规律是不适用的。比如 P 元素,只能包含inline元素,而不能包含block元素。
一般来说,可以通过display:inline和display:block的设置,改变元素的布局级别。
block,inline和inlinke-block细节对比

display:block

block元素会独占一行,多个block元素会各自新起一行。默认情况下,block元素宽度自动填满其父元素宽度。
block元素可以设置width,height属性。块级元素即使设置了宽度,仍然是独占一行。
block元素可以设置margin和padding属性。

display:inline

inline元素不会独占一行,多个相邻的行内元素会排列在同一行里,直到一行排列不下,才会新换一行,其宽度随元素的内容而变化。
inline元素设置width,height属性无效。
inline元素的margin和padding属性,水平方向的padding-left, padding-right, margin-left, margin-right都产生边距效果;但竖直方向的padding-top, padding-bottom, margin-top, margin-bottom不会产生边距效果。

display:inline-block

简单来说就是将对象呈现为inline对象,但是对象的内容作为block对象呈现。之后的内联对象会被排列在同一行内。比如我们可以给一个link(a元素)inline-block属性值,使其既具有block的宽度高度特性又具有inline的同行特性。

console详解

console详解

在JS调试中一般我只会用console.log()进行调试。但是对于console其他的功能却不知道。于是我翻阅了MDN中关于console的详细介绍,让我对console这个对象有更深入的了解。

它是window.console,用console可以简单的进行调用window.console。所以console其实是浏览器内置对象。

如何查询console都有哪些功能呢?

可以用for(var key in console){console.log(key)};来进行查看(此方法能查看所用浏览器支持哪些功能)
还可以在mdn中进行查看

console.log(),console.debug(),console.info()

console.log()这个方法很熟悉了,向web控制台输出一条消息,MDN中也给出了基本语法:

console.log(obj1 [, obj2, ..., objN);
console.log(msg [, subst1, ..., substN);

console.debugconsole.infoconsole.log使用方法一样。

console.assert()

它接收至少两个参数,第一个参数是断言是否为false,如果断言为false那么控制台会弹出一个erro并将逗号之后的第二个参数作为错误描述输出并且返回一个undefined。
如果第一个参数断言为true,那么控制台直接返回一个undefined。
console.assert(true === false, '断言false');//Assertion failed:断言false;undefined
console.assert(true, '断言false'); //undefined

console.clear()

清楚控制台所有信息

console.count()

输出 count() 被调用的次数

function greet(name){
console.count(name);
return '我叫: ' + name;
}

---
greet('Bruce');
// Bruce: 1
// '我叫: Bruce'
greet('George');
// George: 1
// '我叫: George' 
greet('Bruce');
// Bruce: 2
// '我叫: Bruce'

console.dir()

console.dir()方法用来对一个对象进行检查,并以易于阅读和打印的格式显示

var Obj={
name:'cyz',
age:'26'
like:'football'
};
console.dir(Obj); 

var Arr = [1,2,3];
console.dir(Arr);

console.error(),console.warn()

console.error(),console.warn()这两个方法是输出错误和警告信息,用法和console.log()几乎一样,只不过console.error()会有一个红色错误标记而console.warn()则会有一个黄色警告标记。

console.table()

console.table()是将输入的对象或者数组以表格的形式输出

console.time(),console.timeEnd()

console.time(),console.timeEnd()操作一个可执行的函数需要的时间以console.time()开始,console.timeEnd()结束。例如:

console.time('计时器1');
 for(var a=0;a<300;a++){
    for(var b=0;b<300;b++){}
 }
 console.timeEnd('计时器1');
 // 计时器1:x.xxxms
 undefined

console.group()

console.group(‘分组号1’) 是将控制台的信息进行分组,可以把这些信息量大的分组折叠和展开查看。

console.profile(),console.profileEnd()

console.profile(),console.profileEnd()是用来新建一个性能测试器,可以评估代码的性能,可以传一个参数,为生成的性能测试器的名字。

function profile(){
  for (var i = 0; i<500;i++){}
  }
  console.profile('测试1');
  profile()
  console.profileEnd();
  

console.trace()

console.trace()是用来追踪函数的调用过程,在复杂的架构中可以查找到对应调用的函数路径。

本篇博客参考了阮一峰的相关文章以及MDN中相关的文章。

手机web调试

标题如何在电脑上启动一个http server

  1. npm i -g node-static
  2. 输入static --host-address=自己IP地址,会得到一个serving地址,这个地址只有本机能够使用
  3. 找到电脑的局域网IP
  4. 边预览边改代码

手机调试基础常用方法

因为手机没有很好的调试工具所以需要在代码中进行调试

  1. 手机页面调试过程中border有可能会影响页面效果,此时建议使用outline方法
  2. alert()
  3. 自制一个页面底部固定的console.log
  4. 边预览边改代码
let div = document.createElement('div')
div.id = 'console'
document.body.appendChild(div)
div.style.position = 'fixed'
div.style.width = '100%'
div.style.height = '100px'
div.style.bottom = '0'
div.style.left = '0'
div.backgroundColor = '#ccc'
div.style.fontsize = '30px'
console.log = function(thing) {
	div.innerText = JSON.stringify(thing)
}

4.进阶方法利用window.onerroralert错误信息**(优先用此方法)**

window.onerror = function(message,page,row,column){
	alert('出错了\n'+message+'\n'+'行数: '+row+'\n'+'列数: '+column+'\n')
}

测试哪些手机地址

5.手机由于会触发touchstart,touchmove,touchend事件于是存在300ms延迟,如果要消除这延迟可以用FastClick事件进行处理。

if('addEleventListener' in document){
	document.addEleventListener('DOMcontentLoaded',function(){
		FastClick.attach(document.body);
	},false)
}
$(function(){
	FastClick.attach(document.body);
})

JS中的异步

所谓立即执行函数就是声明一个匿名函数,立即执行它

一般有三种写法:

   (function(){})();
   (function(){}());
   !function(){}();

首先我们需要搞清楚函数表达式和函数声明区别,ECMAScript规范中定义的相当模糊:

函数声明必须带有标示符(Identifier)(就是大家常说的函数名称),而函数表达式则可以省略这个标示符:
函数声明:
  function 函数名称 (参数:可选){ 函数体 }
函数表达式:
  function 函数名称(可选)(参数:可选){ 函数体 }

其实我们常用的区分方式是根据上下文,如果function fn(){}作为右值出现(赋值表达式右边)那么就是表达式,否则就是函数声明。有几种看起来不常规的方式需要我们注意

new function fn(){}; //表达式,因为在new 表达式中
(function(){}());//表达式,在分组运算符中

这样我们就能理解第二种写法了,就是利用分组运算符改变了语法树。同样第三种写法其实是利用了一元运算符后面跟表达式的原理,我们也可以写成

  +function(){}()
  -function(){}()
  ~function(){}()

知道了这些看个传入参数调用的立即执行函数

(function f(f){
    return typeof f();
})(function(){return 1;});// "number"

立即执行函数不再是以空括号()来调用了,同时传入了一个function作为参数传入调用。
当函数执行有命名冲突的时候,函数依次填入 变量=》函数=》参数,所以最后被填入的参数f会覆盖函数定义f,typeof f()是对参数的调用,参数是立即执行函数传入的function参数,返回数字1,typeof 1是 "number"。

表达式返回值

函数定义表达式返回的是函数对象本身,我们在调用alert或者console.log的时候会调用其toString方法

console.log(function(){alert('a');}) //function (){alert('a');}

函数调用表达式自然是返回函数的return结果,但在JavaScript中并不是所有的函数都有return语句,对于没有return语句的function,其调用表达式返回undefined,对于只写个return的坑爹做法同样也是返回undefined

(function(){})(); //undefined
(function(){return;})();//undefined

对象创建表达式本来也应该很简单,返回new的对象就可以了

typeof new Date(); //"object"

当使用function的构造函数创建对象(new XXX)的时候,如果函数return基本类型或者没有return(其实就是return undefined)的时候, new 返回的是对象的实例;如果 函数return的是一个对象,那么new 将返回这个对象而不是函数实例。

本片博客摘抄子彭玉芳简书

popover组件基础知识及Demo

@Keyframes定义和用法

通过 @Keyframes 规则,您能够创建动画。

创建动画的原理是,将一套 CSS 样式逐渐变化为另一套样式。

在动画过程中,您能够多次改变这套 CSS 样式。

以百分比来规定改变发生的时间,或者通过关键词 "from" 和 "to",等价于 0% 和 100%。

0% 是动画的开始时间,100% 动画的结束时间。

为了获得最佳的浏览器支持,您应该始终定义 0% 和 100% 选择器。

注释:请使用动画属性来控制动画的外观,同时将动画与选择器绑定。

animation,语法

animation: name duration timing-function delay iteration-count direction;
描述
animation-name 规定需要绑定到选择器的 keyframe 名称。。
animation-duration 规定完成动画所花费的时间,以秒或毫秒计。
animation-timing-function 规定动画的速度曲线。
animation-delay 规定在动画开始之前的延迟。
animation-iteration-count 规定动画应该播放的次数。
animation-direction 规定是否应该轮流反向播放动画。

mdn更详细的使用方法介绍

CSS :visited 选择器

选择已访问的链接,并设置其样式:

a:visited
{ 
background-color:yellow;
}

CSS text-decoration 属性

描述
none 默认。定义标准的文本。
underline 定义文本下的一条线。
overline 定义文本上的一条线。
line-through 定义穿过文本下的一条线。
blink 定义闪烁的文本。
inherit 规定应该从父元素继承 text-decoration 属性的值。

#CSS :hover 选择器

选择鼠标指针浮动在其上的元素,并设置其样式:
a:hover
{
background-color:yellow;
}

选择未访问、已访问、悬浮和活动链接,并设置它们的样式:
a:link {color:blue;}
a:visited {color:blue;}
a:hover {color:red;}
a:active {color:yellow;}


#CSS3 :nth-child() 选择器

规定属于其父元素的第二个子元素的每个 p 的背景色:
p:nth-child(2)
{
background:#ff0000;
}

如果不给父元素的第一个子元素添加:nth-child(1)那么child(2)就会继承child(1)的CSS属性


CSS cursor 属性

实例
一些不同的光标:

span.crosshair {cursor:crosshair;}

span.help {cursor:help;}

span.wait {cursor:wait;}

描述
url 需使用的自定义光标的 URL。注释:请在此列表的末端始终定义一种普通的光标,以防没有由 URL 定义的可用光标。
default 默认光标(通常是一个箭头)
auto 默认。浏览器设置的光标。
crosshair 光标呈现为十字线。
pointer 光标呈现为指示链接的指针(一只手)
move 此光标指示某对象可被移动。
e-resize 此光标指示矩形框的边缘可被向右(东)移动。
ne-resize 此光标指示矩形框的边缘可被向上及向右移动(北/东)。
nw-resize 此光标指示矩形框的边缘可被向上及向左移动(北/西)。
n-resize 此光标指示矩形框的边缘可被向上(北)移动。
se-resize 此光标指示矩形框的边缘可被向下及向右移动(南/东)。
sw-resize 此光标指示矩形框的边缘可被向下及向左移动(南/西)。
s-resize 此光标指示矩形框的边缘可被向下移动(南)。
w-resize 此光标指示矩形框的边缘可被向左移动(西)。
text 此光标指示文本。
wait 此光标指示程序正忙(通常是一只表或沙漏)。
help 此光标指示可用的帮助(通常是一个问号或一个气球)。

CSS :focus 选择器

实例

选择获得焦点的输入字段,并设置其样式:

input:focus
{ 
background-color:yellow;
}

选择未访问、已访问、悬浮和活动链接,并设置它们的样式:

a:link    {color:blue;}
a:visited {color:blue;}
a:hover   {color:red;}
a:active  {color:yellow;}

文字内容标签

<em>强调文本</em><br>

<strong>加粗文本</strong><br>

<dfn>定义项目</dfn><br>

<code>一段电脑代码</code><br>

<samp>计算机样本</samp><br>

<kbd>键盘输入</kbd><br>

<var>变量</var>

JavaScript相关内容

简单实现点击按钮显示内容再点击同样按钮消失内容,点击document位置消逝内容方法

$('.popover').on('click', 'button', function(e) {
  let $button = $(e.currentTarget)
  let $popover = $button.parent()
  $popover.toggleClass('active')
  e.stopPropagation()
})
$(document).on('click',function(e){
  $('.popover').removeClass('active')
})

popover组件代码

上边方法会给document添加很多监听不是很完美

  let close = function (e) {
        $popup.removeClass('active')
        $popup.siblings('.active').removeClass('active')
        $('body').off('click', close)
    }
   let open = function (e) {
        $popup.addClass('active')
        $popup.siblings('.active').removeClass('active')
        $('body').on('click', close)
    }
$('.buttons').on('click', 'a', function (e) {
    let $a = $(e.currentTarget)
    let $index = $a.index()
    let $popup = $a.parent().next().children().eq($index)
    if ($popup.hasClass('active')) {
        close(e)
    } else {
        open(e)
        e.stopPropagation(e)
    }

})

将close和open函数声明在click事件外这样可以避免出现重复多个close和open声明。


toggleClass() 定义和用法

toggleClass() 对设置或移除被选元素的一个或多个类进行切换。

该方法检查每个元素中指定的类。如果不存在则添加类,如果已设置则删除之。这就是所谓的切换效果。

不过,通过使用 "switch" 参数,您能够规定只删除或只添加类。

语法

$(selector).toggleClass(class,switch)

###event.stopPropagation() 组织冒泡

hasClass('active')判断是否有active

通长用法是if(element,hasClass('active')){}来进行使用


jQuery 动画 - animate() 方法

jQuery animate() 方法用于创建自定义动画。

语法:

$(selector).animate({params},speed,callback);

必需的 params 参数定义形成动画的 CSS 属性。

可选的 speed 参数规定效果的时长。它可以取以下值:"slow"、"fast" 或毫秒。

可选的 callback 参数是动画完成后所执行的函数名称。

下面的例子演示 animate() 方法的简单应用;它把

元素移动到左边,直到 left 属性等于 250 像素为止:


假如点击button然后对父容器添加监听body,是先冒泡到body还是先监听body。答案是先监听body

button的display是 inline-block

制作popover组件的代码

DOM 获取元素

  1. 从标签名获取元素

var a = document.getElementsByTagName('p')
var a = document.getElementsByTagName('button')

  1. 从类名获取元素

var a = document.getElementsByClassName('xxx')

  1. 从ID名获取元素

var a = document.getElementById('xxx')

  1. 类选择器获取元素

返回文档中第一个类名为'xxx'的元素
var a = document.querySelector('xxx')

返回文档中所有类名为'xxx'的元素
var a = document.querySelectorAll('xxx')

  1. 获取父元素

var parent = a.parentElement

  1. 获取子元素

var b = a.getElementsByTagName('xxx')
var b = a.getElementsByClassName('xxx')
var b = a.getElementById('xxx')

  1. 获取第一个或最后一个子元素

var b = a.firstElementChild
var b = a.lastElementChild

  1. 获取上一个或下一个兄弟元素

ar a2 = a1.nextElementSibling
var a1 = a2.previousElementSibling

事件委托

为什么要有事件委托

  1. 监听还不存在的元素或者已经存在的元素
  2. 减少监听器的数量,减少内存消耗

监听增加元素的方法是在其增加的位置对它进行监听

Element.addEventListener('click',function(e){
  this.remove()
})

这个方法会在元素增加多少就会增加多少个监听器。所以会增加内存消耗。建议在少量增加元素的情况下使用

事件委托方法

事件委托监听方法是对增加元素的parent进行监听。
取号抽号方法demo

CSS各种居中方法

1. 水平居中

1.1 行内元素:

text-align: center;

1.2 块级元素:

margin: 0 auto;

这两种方法都是用来水平居中的,前者是针对父元素进行设置而后者则是对子元素。他们起作用的首要条件是子元素必须没有被float影响,否则一切都是无用功。

1.3 多于一个块级元素:

  1. 子元素设置为inline-block类型,同时父元素设置属性 text-align: center;

代码:

.inline-block-center {
  text-align: center;
}
.inline-block-center div {
  display: inline-block;
  text-align: left;
  1. 使用flex boxflex-center { display: flex; justify-content: center;}

2.垂直居中

2.1 行内元素

单行行内元素

  1. 可以设置padding-top,padding-bottom
  2. 如果无法使用padding属性,则将height和line-height设为相等也可以垂直居中。

关于line-height和height可参考css行高line-height的一些深入理解

多行行内元素

  1. 可以将元素转为table样式,再设置vertical-align:middle;

    .center-table {
      display: table;
      height: 250px;
      background: white;
      width: 240px;
      margin: 20px;
    }
    .center-table p {
      display: table-cell;
      margin: 0;
      background: black;
      color: white;
      padding: 20px;
      border: 10px solid white;
      vertical-align: middle;
    }
    
  2. 使用flex布局

    .flex-center-vertically {
      display: flex;
      justify-content: center;
      flex-direction: column;
      height: 400px;
    }
    

这里要注意!!flex布局要求父元素必须显示设置heights

  1. 在容器中放置伪元素

并使用vertical-align使文本垂直对齐该伪元素

.ghost-center {
  position: relative;
}
.ghost-center::before {
  content: " ";
  display: inline-block;
  height: 100%;
  width: 1%;
  vertical-align: middle;
}

2.2 块级元素

  1. 已知高度

    .parent {
      position: relative;
    }
    .child {
      position: absolute;
      top: 50%;
      height: 100px;
      margin-top: -50px; /* account for padding and border if not using box-sizing: border-box; */
    }
    
  2. 元素是未知高度

    .parent {
      position: relative;
    }
    .child {
      position: absolute;
      top: 50%;
      transform: translateY(-50%);
    }
    
  3. 用flex布局

    .parent {
      display: flex;
      flex-direction: column;
      justify-content: center;
    }
    

3. 垂直和水平居中

3.1 固定大小元素

.parent {
  position: relative;
}

.child {
  width: 300px;
  height: 100px;
  padding: 20px;

  position: absolute;
  top: 50%;
  left: 50%;

  margin: -70px 0 0 -170px;
}

首先给父元素写上positon:relative,这么做是为了给子元素打上position:absolute的时候不会被定位到外太空去。接下去,写上子元素的height和width,这个似乎是必须的
,某些浏览器在解析的时候如果没有这2个值的话会出现意想不到的错位。接着就是整个方法的核心,给子元素再打上top:50%;left:50%以及margin-top:一半的height值的的负数;
margin- left:一半的weight值的负数。整理一下之后,可能你会给你的子元素写上这样的css(当然,父元素也要先写上width和height)

3.2 未知大小元素

如果当前元素大小未知,可以使用translate将元素在x和y两个方向反向偏移自身的50%的大小,使其居中显示

.parent {
  position: relative;
}
.child {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

3.3 使用flex居中

.parent {
  display: flex;
  justify-content: center;
  align-items: center;
}

Reference:

Centering in CSS: A Complete Guide 完整讨论了不同情况下的居中方案

Get HTML & CSS Tips In Your Inbox:一个作弊工具生成居中代码

本篇博客转载地址

jQuery概念及基础知识

  1. 选择网页元素
    $('#items').lenght === 0 //用长度判断文档中是否含有#items
    xxx.length-1 //获取最后一个元素的小技巧

  2. 改变结果集

var allp = $('p')
var allDiv = $('div')
var allpAndDiv = allp.add(allDiv)
allpAndDiv
// 得到所有的P和div
  1. 链式操作
    就是重复使用了this这个指向属性

this 是什么?
你不懂JS:this与对象原型

  1. 元素的操作:取值和赋值
    $('div').attr('class','sc') //给div添加属性class='sc' 如果已经存在相同的属性则会覆盖 $('div').attr('class') //如果这样调用则显示class的值 "sc"
    $('div').prop('id','sd') //和.attr差不多区别在于不是标准的属性则不会生效
    $('</div>') //jQuery是xml。这个代码相当于给HTML添加了一个div元素

jQuery文档
阮一峰jQuery设计**
jQuery全面总结


一些jQuery的AIP

$(selector).add(element,context) 方法把元素添加到已存在的元素组合中
$(selector).remove()方法移除被选元素,包括所有的文本和子节点(该方法也会移除被选元素的数据和事件。)
$(selector).addClass(context) 添加一个Class=‘context’
$(selector).removeClass(context)删除一个Class=‘context’
$(selector).sibilings() 获得匹配元素集合中每个元素的兄弟元素,
$(selector).attr(attribute,value) 如果不输入value值则获取属性对应的值,如果输入value则对匹配的元素添加属性和值
$(selector).hasClass(class) 方法检查被选元素是否包含指定的 class。
$(selector).each(function(index,element))遍历一个jQuery对象为每个匹配元素规定运行的函数
$(selector).prop({property:value, property:value,...})方法设置或返回被选元素的属性和值。但是对于非标准的html的属性则不会显示在html中
$(selector).toggleClass(class,switch)对设置或移除被选元素的一个或多个类进行切换
$(selector).toggle(function1(),function2(),functionN(),...)方法用于绑定两个或多个事件处理器函数,以响应被选元素的轮流的 click 事件
$(selector).css() 为匹配的元素添加css属性
$(selector).eq() 选择器选取带有指定 index 值的元素。
.closest(selector)最靠近的 从自身元素开始,在DOM树上逐级向上匹配,并返回最先匹配的祖先元素。
$(selector).index() 方法返回指定元素相对于其他指定元素的 index 位置。
$(selector).prepend(content)方法在被选元素的开头(仍位于内部)插入指定内容
$(selector).is(selector)is() 根据选择器、元素或 jQuery 对象来检测匹配元素集合,如果这些元素中至少有一个元素匹配给定的参数,则返回 true
$(selector).fadeIn(speed,callback)方法使用淡入效果来显示被选元素,假如该元素是隐藏的。
$(selector).fadeOut(speed,callback)方法使用淡出效果来显示被选元素,假如该元素是隐藏的。

topbar基础组件

html css js 关注点分离

状态变化 html+css 由js操控状态变换

.stopPropagation()  //阻止冒泡
.toggleClass()  //有指定的class就移除,没有就添加指定的class
.currentTarget() //获取用户选取元素
.hasClass() //检查被选元素是否包含指定的class

JavaScript操作SVG的一些知识

前阵子学习了一下SVG(Scalable Vector Graphics),希望能借此弥补自己在图形艺术上的不足,当然最后也没有得到什么提高,不过也扩充了一些网页前段技术知识。通过做了一些小的设计项目,也发现SVG可以弥补一些HTML元素的不足,比如倾斜、弧线、动画、复用等等。

虽然SVG和HTML一样都属于XML的一种方言,一些基本的JavaScript对HTML的DOM操作都适用于SVG,但是在实际运用中还是因为这样那样的细微区别遇到了不大不小的麻烦。所以通过此篇文章记录下遇到的问题和解决的方法。

获取SVGDocument

当使用JavaScript在页面上对HTML进行操作的使用,一个非常重要的对象就是document了。无论是getElementByIdgetElementsByTagName,异或是createElement,它们都是document对象上的方法。而且所有其它任何DOM对象都被包含在该对象之内。

一般而言,一个HTML文件,或者说一个网页都对应一个document对象,所以如果SVG是直接嵌套在HTML的内容中的话,它们就会共用一个document对象,因此可以直接通过该对象来获取到SVG元素对象。

比如下边的代表,在浏览器上打开,就会看到一个蓝色的圈而非绿色的圈,因为JavaScript通过document获得了circle对象,并重新设置了其fill属性。

<html>
<head>
    <title>Nested SVG</title>
</head>
<body>
 
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 
    version="1.1" width="20" height="20">
<circle id="c" cx="10" cy="10" r="7" fill="green"/>
</svg>
<script type="text/javascript">
 
var c = document.getElementById('c');
c.setAttribute('fill','blue');
 
</script>
</body>
</html>

不过大多时候,SVG并不会直接嵌套在HTML之中重现出来,更多的会选择将其作为图片通过img标签引进,或者当做背景图片在CSS中通过url引入。对于这种情况,SVG只是单纯的当做图片来使用而且一个XML类型的文档,所以也就无法使用JavaScript来操作它们。

还有一类方法则是通过object、embed或者iframe标签将SVG文件引入到HTML页面上呈现出来。对于该种情况, 这些标签实际上会把通过datasrc属性指定的内容相对独立的引入到页面上来,也就是其中的内容会有完全属于自己的document对象。所以使用原来的document对象就无法取得通过上述标签引入进来的SVG文档中元素,更不用说去修改上边的属性了。

好在当使用JavaScript获取到这些元素对象的时候,它们都一个方法可以获取所引用的SVG文档的document对象,那就是getSVGDocument()
getSVGDocument

当然这些都是需要浏览器支持才行的,对于目前主流最新浏览器来说这些都是标配的方法。如果使用object或iframe引入SVG文档,除了getSVGDocument(),还可以使用contentDocument属性来获取其引入文档对应的document对象。区别在于如果是引入的不是SVG文件,而是XML或者HTML等等,contentDocuement依然会返回对象,而getSVGDocument()则返回null

contentDocument

获取了SVG的document之后,就可以像往常那样获取内部元素属性、绑定事件等等。还可以定义一个document为参数的方法形成局部变量,要对某个引入SVG文档进行操作时就获取该文档的document对象传入,想获取主文档的对象时就使用window.document即可。

function setup (document) {
    // do something with svg docuemnt
}
 
setup(document.getElementById('svg-embed').getSVGDocument());

当然了使用上边一系列属性和方法都有一个大前提:要满足同源策略(Same-origin policy)。若引入的SVG文档是来自于其它站点的,那么浏览器就会禁止获取document对象。

blocked_accessing

操作SVG的元素

SVG作为XML的方言,不同于HTML松散的标签结构和格式,它严格遵循XML的语法格式,所以开始标签都要有对应的结束标签,所有标签都要被svg标签包含在内。另外在HTML经常被忽略的一个知识就是:XML是有命名空间(namespace)的。命名空间在通过JavaScript创建SVG元素对象的时候就引起了一些麻烦。

一般的在HTML中若想通过JavaScript创建一个元素对象的话,代码类似如下:

var inp = document.createElement('input');
inp.type = 'button';
inp.value = 'button';
inp.name = 'button';
 
var con = document.getElementById('container');
con.appendChild(inp);

但是使用相同的方法,创建SVG元素并添加到SVG文档中的话, 该元素并不会呈现出来。

<svg xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    version="1.1" width="20" height="20">
<script type="text/javascript">
    var c = document.createElement('circle');
    c.cx = 10;
    c.cy = 10;
    c.r = 7;
    c.fill = 'green';
 
    document.rootElement.appendChild(c);
</script>
</svg>

这是因为创建SVG元素需要指定命名空间,就像需要在svg标签上设定xmlns为http://www.w3.org/2000/svg。正确的构造方式是调用[`createElentNS()`](https://developer.mozilla.org/en-US/docs/Web/API/document.createElementNS)方法,并将”http://www.w3.org/2000/svg”作为第一参数传入。

此外,不同于HTML元素对象可以直接对一些属性赋值,SVG元素对象都需要通过调用setAttribute()方法来设定属性值。因为大部分属性都是SVGAnimatedLength类型,即使要通过属性赋值也要写成类似c.r.baseVal.value = 7,多层访问其下属性。不过像fillstroke等默认都是undefined,所以使用setAttribute()是更好的选择。

下述代码就可以在页面上呈现是一个半径为7px的绿色的圆点。

<svg xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    version="1.1" width="20" height="20">
    <script type="text/javascript">
 
        var c = document.createElementNS('http://www.w3.org/2000/svg','circle');
        c.setAttribute('cx', 10);
        c.setAttribute('cy', 10);
        c.r.baseVal.value = 7;
        c.setAttribute('fill', 'green');
 
        document.rootElement.appendChild(c);
    </script>
</svg>

除了元素有命名空间,有些属性也有其特定的命名空间。比如在HTML极为常用的a标签的,在SVG中又有存在,但是对于其href属性,在SVG之中就必须加上xmlns:前缀来指定其命名空间了。对于设置这些属性也需要用setAttributeNS()方法并将”http://www.w3.org/1999/xlink“作为第一参数传入。

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
    version="1.1" width="20" height="20">
    <script type="text/javascript">
 
        var a = document.createElementNS('http://www.w3.org/2000/svg','a');
        a.setAttributeNS('http://www.w3.org/1999/xlink',
                     'xlink:href', 
                     'http://blog.iderzheng.com/');
 
        var c = document.createElementNS('http://www.w3.org/2000/svg','circle');
        c.setAttribute('cx', 10);
        c.setAttribute('cy', 10);
        c.r.baseVal.value = 7;
        c.setAttribute('fill', 'green');
 
        a.appendChild(c);
 
        document.rootElement.appendChild(a);
    </script>
</svg>

现在可以通过点击绿点进入到博客主页了。

不仅是a标签,对于其它标签,例如:use、image,若要设置xlink:href属性时都应使用命名空间的方式,否则就不会起作用。对于其它该命名空间下的属性也是如此,例如xlink:titlexlink:show。这里就不一一列举,具体关于xlink的可参看此处

如果你碰到了其它在JavaScript中因XML命名空间引起的问题,也欢迎留言补充。

SVG与窗口坐标系的转换

SVG比HTML的一大优势在于前者支持平移、缩放、切变等变换(Transform)。虽然现在CSS3也支持这些变换,但是毕竟SVG是向量图,它在缩放的时候不会丢失像素。而且SVG可以通过use标签设置tranform属性来进行快速复用。而HTML的标签就没有这样的“复用性”,每个在页面上呈现出来的内容都严格对应一个标签元素。

不仅是transform属性可以变化坐标,一些元素例如svg,也可以通过设定viewBox来改变自身的坐标系以影响呈现的内容。这就引发了一些问题坐标系转换的问题:比如鼠标点击在页面上的时候获取到的是基于窗口坐标系以像素为单位的位置,如何转变到SVG的坐标系的点以确定被点击的对象或者进行其它操作呢?

一种方法可以是自己记录下窗口和SVG图的比例,然后根据比例进行转换。这可能可以一定程度地解决平移和缩放带来的变换问题,但是对于旋转就无能为力了。而且当窗口进行了滚动或者拖拽,都需要重新计算这些比例。

其实在每个SVG元素对象上,都有一个getScreenCTM()的方法,它会返回一个SVGMatrix来表示元素的坐标系所做过的变换。此外SVG还有一种SVGPoint类型,它有x和y两个属性可以表示任一一个点,同时它还有一个matrixTransform()方法可以将点跟某个SVGMatrix相乘得到相应矩阵变换后的点。通过这些再加上一些线性代数的知识,就可以轻松的进行坐标系的变换了。

要注意的是SVGPoint只能通过svg元素对象的createSVGPoint()来创建,不能用new SVGPoint()这样的方式。

function click(e) {
    // rootElement is specific to SVG document
    // documentElemnt is to any XML document include HTML
    // they both can retrieve the root element of a document 
    var r = document.rootElement || document.documentElement,
    pt = r.createSVGPoint(),
    im = r.getScreenCTM().inverse(); // inverse of tranforma matrix
 
    // set point with window coordination
    pt.x = e.clientX;
    pt.y = e.clientY;
 
    // convert point to SVG coordination
    var p = pt.matrixTransform(im);{
}

本片博客摘抄自iderzheng

这一代人的学习(如何学习前端)

希望这篇博客可以在自己懒惰迷茫的时候能激励自己

如何避免懒惰

1.懒惰是怎样形成的

生活当中经常会给自己指定一个短期的目标,例如:坚持长跑,坚持做饭~~~,但是无法长期坚持下去,到最后总会给自己找各种借口理由懒惰下去。长此以往会养成懒惰的习惯,当制定目标的时间到了却没有完成往往会选择性忘记它。

2.提高意志力的一些心得体会

首先要坚持慢跑,每周至少跑步5天,坚持两周能够迅速提高意志力和行动力。最近坚持跑步半个月发现自己精神状态提高不少,最明显的是学习可以一口气从原来1个半小时提高到3小时,跑步前学习1个半小时就会有些**走神无意识的就想看看视频玩玩游戏。并且以前作息规律不太良好,跑步后作息规律也得到了极大的改善。

3.寻找志同道合的朋友

给自己指定学习计划后学寻找一些有同样目标的人,在学习的过程中可以互相激励坚持下去,并且当别人有问题能够帮助解答也能够给与自己足够的信心和成就感。

4.适当给与足够的放松时间

如果给自己制定的是一个长期的目标那么一定要再过程中适当的给自己足够的放松时间。一道自己喜爱的菜肴如果每天不间断的品尝它那么终有一天会厌倦甚至恶心,这样的道理我认为适用在任何事情上。所以在制定学习计划的时候也要将放松的时间计划进去。

学习中存在的问题

  • 焦虑: 选择多,新知识更新快

  • 过去的学习方式: 读书,看报,上学不适合如今前端学习

  • 时间碎片化: 没有整块时间

  • 没有学成归来: 终生学习,跨界学习

如何成为金子:要有两个领域前20%(编程,演讲,音乐,美术设计等等),剩下也要尽可能多的涉及但是不用了若指掌。

如何提高学习效率

  • 跟人学(寻找一到两个牛人)

  • 学概念(HTML ,CSS,JS,管理上司)

  • 过一道(表达一次,博客,演讲)

  • 碎片化(以项目为导向学习相关知识)

  • 时间零碎 (马上,厕上,床上)

  • 长期目标(3~6个月)

  • 小目标(3~7天)

《HTML 5 中哪些元素是可以省略的》

学习过程中的总结

HTML是什么?

  1. HTML 全称 : HyperText Markup Language,中文名 : 超级文本标记语言,可以创建网页的标准语言。
  2. HTML可以在网页上实现作者想要展现的内容(文字 图片 视频 游戏)

示例代码:

<!DOCTYPE html>                 <!--声明web用哪个HTML版本(HTML5)进行的编写,需在第一行写入-->
<html lang="zh-hans">           <!--声明web文字内容用的简体中文-->
<head>                          <!--属于web头部说明标签,说明文档属性-->
     <meta charset="utf-8">     <!--告诉浏览器及服务器用的utf-8字符集-->
<title>嗯我很帅</title>          <!--会在浏览器的标题栏中显示-->
</head>
<body>                          <!-- 页面显示内容区域 -->
     <h1>文字内容</h1>
      <p>我特别帅哈哈</p>
</body>
</html> 

在查看HTML5规格文档中发现html5元素详细说明中有这么一条

Tag omission in text/html:(text/html 中的标记省略)

现在我用示例代码中 <body> 元素做一个简单的测试如下:


我们会发现在文档中省略<body></body>元素并没有出现不正确显示的内容,当我用Chrome浏览器检查时发现浏览器将省略的元素自动填补。

总结

在 html5 里面有的元素是可以省略不写的

  1. 可以省略全部标记的元素有 :
    html,head,body,colgroup,tbody
  2. 不允许写结束标签的元素有 :
    area,base,br,col,command,embed,hr,img,input,keygen,link,meta,paran,source,track,wbr。
    这些标签都是单标签。
  3. 可以省略结束标记的元素有 :
    li,dt,dd,p,rt,rp,optgroup,option,colgroup,thead,tbody,tfoot,tr,td,th。

对于刚入门学习的人来说还是不要省略,避免出现BUG或者添加代码时造成困难。

闭包

闭包

闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常用方式,就是在一个函数内部创建另一个函数。

魂斗罗例子

现在假设我们在玩魂斗罗。通过秘籍调出了 30 条命,每当我们死亡一次就会减一条命。

有个原则就是尽量不要创建全局变量,并且我们的 lives 变量不能暴露出来防止被别人改动,所以我们用函数封装起来。

function person() {
  let lives = 30
}

然后再给它添加一个 die 函数,触发后生命减 1。由于不能写在全局作用域里,所以我们把它写到 person 里面。

function person() {
  let lives = 30
  function die() {
    lives -= 1
    return lives
  }
}

但是问题来了,根据 js 的作用域原理,我们在函数外面是没办法调用函数里面的变量的。这时候就要用到闭包了。

函数特性

在讲闭包之前我们先仔细探究一下函数的运行机制。

我们都知道函数接受两个参数对象,一个是隐藏的 this 对象(只有在 call 和 apply 方法里可以当做第一个参数传入),另一个是 arguments 类数组对象(里面保存着除 this 之外的所有参数)。

可以把函数具象成一个房间。这个房间有一扇前门,一扇后门。前门就是 arguments 对象,所有的实参值都在这里赋给形参,然后进入函数。后门就是 return,所有返回值都从这里出去。

现在再看上面的例子,我们要从 person 函数外调用里面的 die 函数,理所当然的应该把 die 函数当做返回值送出 person 这个房间。

魂斗罗例子续

在经过一些判断(被子弹打到、碰到敌人、掉到沟里)后,我们的生命减 1 了。

function person() {
  let lives = 30
  return die = function () {
    lives -= 1
    return lives
  }
}
...
if ( ... ) {
  person()() // 29
  person()() // 29
}
...

但是这样又有个问题,每次调用都会重置 lives 为 30,导致 die 触发后 lives 一直是 29。

可以添加一个中间变量 die,跟 die 函数同名是为了增加可读性。

let die = person()
die() // 29
die() // 28

这样每次死亡只要调用一下 die() 就可以了。

面向对象写法

上面的功能也可以用面向对象来写

let person = {
  lives: 30,
  die: function () {
    this.lives -= 1
    return this.lives
  }
}
person.die() // 29
person.die() // 28

用这种方法写例子可以。但是我们每次进游戏,都会创建一个新的人物,这就要用到构造函数来创建对象,涉及到了构造函数的私有变量和特权方法。另一篇博客会详细介绍函数的私有变量和特权方法。

本篇博客转载自http://www.lclscofield.com

更为详细的博客地址

彭玉芳闭包博客地址

jQuery学习总结之动画篇

jQuery中的动画大致分为三类:

  • 基本动画方法hide()和show()到fadeIn()和fadeOut(),然后到slideUp()和slideDown()方法。
  • 自定义动画animate()
  • 交互动画方法toggle()、slideToggle()、fadeTo()和fadeToggle()。

1、show()和hide()方法

  • hide()方法,会将该元素的display样式改为“none”。
  • hide()方法在将”内容“的display属性值设置为"none"之前,会记住原先得display属性值("block"或"inline")。
  • show()和hide()方法可以传入一个速度参数,这个参数可以是关键字”slow“,”fast“,”normal“,还可以具体的一个单位为毫秒的数字。
  • show()和hide()方法会同时改变”内容“的高度、宽度和不透明度。

2、fadeIn()和fadeOut()方法

  • 与show()和hide()方法不同的地方是,只改变元素的不透明度。

3、slideUp()和slideDown()方法

  • 与前面两类不同的是,只改变元素的高度。

4、自定义动画方式animate()

animate(params,speed,callback)

(1)params:一个包含样式属性及值得映射,比如{property1:"value"}。
(2)speed:速度参数,可选。
(3)callback:在动画完成时执行的函数,可选。

  • 为了让元素动起来,就要更改元素的"top","left","bottom"和"right"样式属性,必须先把元素的position样式设置为"relative"或"absolute"。

  • 可以实现累加、累减动画效果

    $(this).animate({left:"+=200px"},300);
    
  • 动画的回调函数适用于jQuery中所有的动画效果方法,可以对非动画方法,例如css()等,实现排队,不会立即执行。


5、停止动画和判断是否处于动画状态

stop([clearQueue],[gotoEnd])
  • 参数clearQueue和gotoEnd都是可选参数,为Boolean值。clearQueue代表是否清空未执行的动画队列,gotoEnd代表是否直接将正在执行的动画跳转到末状态。

  • 如果直接使用stop()方法,则会立即停止当前正在进行的动画,如果接下来还有动画等待继续进行,则以当前状态开始接下来的动画。

  • 经常会遇到一种情况,在为一个元素绑定hover事件之后,用户把光标移入元素时会触发动画效果,当这个动画效果还没结束时,用户就将光标移出这个元素了,那么光标移出的动画效果将会放进队列之中,等待光标移入的动画结束后再执行。如果光标移入移出得过快就会导致动画效果与光标的动作不一致。要解决这个问题,只需要在光标的移入、移出动画之前加入stop()方法、就能解决这个问题。

    $(function(){
            $(".panel").hover(function(){
                $(this).stop().animate({height:"150px",width:"300px"},200);
            },function(){
                $(this).stop().animate({height:"22px",width:"60px"},300);
            })
         });
    

    此时只用一个不带参数的stop()方法就显得力不从心了。因为stop()方法只会停止正在进行的动画,如果动画有几个阶段,还是会出现上述的不一致问题,所以可以把第一个参数(clearQueue)设置为true,此时程序会把当前元素接下来尚未执行完的动画队列都清空。


  • 判断元素是否处于动画状态可以使用以下的方法:

    if($(element).is(":animated")){}
    
  • 要想延迟动画的执行可以使用delay()方法,传入一个时间参数,值得注意的是,该方法只能用于位于队列中的动画方法。


6、交互式的动画方法

  • toggle()方法可以切换元素的可见状态。
  • slideToggle()方法通过高度变化来切换匹配元素的可见性。
  • fadeTo()方法可以把元素的不透明度以渐进方式调整到指定的值。
  • fadeToggle()方法通过不透明度改变来切换匹配元素的可见性。

本篇转载地址

webpack:从入门到真实项目配置(本篇转载自掘金,作者夕阳)

本文原载于掘金,作者夕阳.

该文使用的 Webpack 版本为 3.6.0,本文分两部分。第一步是简单的使用 webpack,第二部分通过一个真实项目来配置 webpack,没有使用任何的 CLI,都是一步步配置直到完成生产代码的打包。这是本项目对应的仓库**,每个小节基本都对应了一次 commit。

这是本文的大纲,如果觉得有兴趣你就可以往下看了

img

Webpack 到底是什么

自从出现模块化以后,大家可以将原本一坨代码分离到个个模块中,但是由此引发了一个问题。每个 JS 文件都需要从服务器去拿,由此会导致加载速度变慢。Webpack 最主要的目的就是为了解决这个问题,将所有小文件打包成一个或多个大文件,官网的图片很好的诠释了这个事情,除此之外,Webpack 也是一个能让你使用各种前端新技术的工具。

img

简单使用

安装

在命令行中依次输入

mkdir  webpack-demo
cd webpack-demo
// 创建 package.json,这里会问一些问题,直接回车跳过就行
npm init 
//  推荐这个安装方式,当然你也安装在全局环境下
// 这种安装方式会将 webpack 放入 devDependencies 依赖中
npm install --save-dev webpack

然后按照下图创建文件

img

在以下文件写入代码

// sum.js
// 这个模块化写法是 node 环境独有的,浏览器原生不支持使用
module.exports = function(a, b) {
    return a + b
}
// index.js
var sum = require('./sum')
console.log(sum(1, 2))
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
    <script src="./build/bundle.js"></script>
</body>
</html>

现在我们开始配置最简单的 webpack,首先创建 webpack.config.js 文件,然后写入如下代码

// 自带的库
const path = require('path')
module.exports = {
    entry:  './app/index.js', // 入口文件
    output: {
      path: path.resolve(__dirname, 'build'), // 必须使用绝对地址,输出文件夹
      filename: "bundle.js" // 打包后输出文件的文件名
    }
  }

现在我们可以开始使用 webpack 了,在命令行中输入

node_modules/.bin/webpack

没问题的话你应该可以看到类似的样子

img

可以发现原本两个 JS 文件只有 100B,但是打包后却增长到 2.66KB,这之中 webpack 肯定做了什么事情,我们去 bundle.js 文件中看看。

把代码简化以后,核心思路是这样的

var array = [(function () {
        var sum = array[1]
        console.log(sum(1, 2))
    }),
    (function (a,b) {
        return a + b
    })
]
array[0]() // -> 3

因为 module.export 浏览器是不支持的,所以 webpack 将代码改成浏览器能识别的样子。现在将 index.html 文件在浏览器中打开,应该也可以看到正确的 log。

我们之前是在文件夹中安装的 webpack,每次要输入 node_modules/.bin/webpack 过于繁琐,可以在 package.json 如下修改

"scripts": {
    "start": "webpack"
  },

然后再次执行 npm run start,可以发现和之前的效果是相同的。简单的使用到此为止,接下来我们来探索 webpack 更多的功能。

Loader

Loader 是 webpack 一个很强大功能,这个功能可以让你使用很多新的技术。

Babel

Babel 可以让你使用 ES2015/16/17 写代码而不用顾忌浏览器的问题,Babel 可以帮你转换代码。首先安装必要的几个 Babel 库

npm i --save-dev babel-loader babel-core babel-preset-env

先介绍下我们安装的三个库

  • babel-loader 用于让 webpack 知道如何运行 babel
  • babel-core 可以看做编译器,这个库知道如何解析代码
  • babel-preset-env 这个库可以根据环境的不同转换代码

接下来更改 webpack-config.js 中的代码

module.exports = {
// ......
    module: {
        rules: [
            {
            // js 文件才使用 babel
                test: /\.js$/,
             // 使用哪个 loader
                use: 'babel-loader',
            // 不包括路径
                exclude: /node_modules/
            }
        ]
    }
}

配置 Babel 有很多方式,这里推荐使用 .babelrc 文件管理。

// ..babelrc
{
    "presets": ["babel-preset-env"]
}

现在将之前 JS 的代码改成 ES6 的写法

// sum.js
export default (a, b) => {
    return a + b
}
// index.js
import sum from './sum'
console.log(sum(1, 2))

执行 npm run start,再观察 bundle.js 中的代码,可以发现代码被转换过了,并且同样可以正常 输出3。

当然 Babel 远不止这些功能,有兴趣的可以前往官网自己探索。

处理图片

这一小节我们将使用 url-loader 和 file-loader,这两个库不仅可以处理图片,还有其他的功能,有兴趣的可以自行学习。

先安装库

npm i --save-dev url-loader file-loader

创建一个 images 文件夹,放入两张图片,并且在 app 文件夹下创建一个 js 文件处理图片
,目前的文件夹结构如图

img

// addImage.js
let smallImg = document.createElement('img')
// 必须 require 进来
smallImg.src = require('../images/small.jpeg')
document.body.appendChild(smallImg)

let bigImg = document.createElement('img')
bigImg.src = require('../images/big.jpeg')
document.body.appendChild(bigImg)

接下来修改 webpack.config.js 代码

module.exports = {
// ...
    module: {
        rules: [
            // ...
            {
            // 图片格式正则
                test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
                use: [
                  {
                    loader: 'url-loader',
                    // 配置 url-loader 的可选项
                    options: {
                    // 限制 图片大小 10000B,小于限制会将图片转换为 base64格式
                      limit: 10000,
                    // 超出限制,创建的文件格式
                    // build/images/[图片名].[hash].[图片格式]
                      name: 'images/[name].[hash].[ext]'
                   }
                  }
                ]
            }
        ]
    }
  }

运行 npm run start,打包成功如下图

img

可以发现大的图片被单独提取了出来,小的图片打包进了 bundle.js 中。

在浏览器中打开 HTML 文件,发现小图确实显示出来了,但是却没有看到大图,打开开发者工具栏,可以发现我们大图的图片路径是有问题的,所以我们又要修改 webpack.config.js 代码了。

module.exports = {
    entry:  './app/index.js', // 入口文件
    output: {
      path: path.resolve(__dirname, 'build'), // 必须使用绝对地址,输出文件夹
      filename: "bundle.js", // 打包后输出文件的文件名
      publicPath: 'build/' // 知道如何寻找资源
    }
    // ...
  }

最后运行下 npm run start,编译成功了,再次刷新下页面,可以发现这次大图被正确的显示了。下一小节我们将介绍如何处理 CSS 文件。

处理 CSS 文件

添加 styles 文件夹,新增 addImage.css 文件,然后在该文件中新增代码

img {
    border: 5px black solid;
}
.test {border: 5px black solid;}

这一小节我们先使用 css-loader 和 style-loader 库。前者可以让 CSS 文件也支持 impost,并且会解析 CSS 文件,后者可以将解析出来的 CSS 通过标签的形式插入到 HTML 中,所以后面依赖前者。

npm i --save-dev css-loader style-loader

首先修改 addImage.js 文件

import '../styles/addImage.css'

let smallImg = document.createElement('img')
smallImg.src = require('../images/small.jpeg')
document.body.appendChild(smallImg)

// let bigImg = document.createElement('img')
// bigImg.src = require('../images/big.jpeg')
// document.body.appendChild(bigImg)

然后修改 webpack.config.js 代码

module.exports = {
// ...
    module: {
      rules: [
        {
            test: /\.css$/,
            use: ['style-loader',
                {
                    loader: 'css-loader',
                    options: {
                        modules: true
                       }
                }
            ]
        },
      ]
    }
  }

运行下 npm run start,然后刷新页面,可以发现图片被正确的加上了边框,现在我们来看一下 HTML 的文件结构

img

从上图可以看到,我们在 addImage.css 文件中写的代码被加入到了 style 标签中,并且因为我们开启了 CSS 模块化的选项,所以 .test 被转成了唯一的哈希值,这样就解决了 CSS 的变量名重复问题。

但是将 CSS 代码整合进 JS 文件也是有弊端的,大量的 CSS 代码会造成 JS 文件的大小变大,操作 DOM 也会造成性能上的问题,所以接下来我们将使用 extract-text-webpack-plugin插件将 CSS 文件打包为一个单独文件

首先安装 npm i --save-dev extract-text-webpack-plugin

然后修改 webpack.config.js 代码

const ExtractTextPlugin = require("extract-text-webpack-plugin")

module.exports = {
// ....
    module: {
      rules: [
        {
          test: /\.css$/,
          // 写法和之前基本一致
          loader: ExtractTextPlugin.extract({
          // 必须这样写,否则会报错
                fallback: 'style-loader',
                use: [{
                    loader: 'css-loader',
                    options: { 
                        modules: true
                    }
                }]
            })
        ]
        }
      ]
    },
    // 插件列表
    plugins: [
    // 输出的文件路径
      new ExtractTextPlugin("css/[name].[hash].css")
    ]
  }

运行下 npm run start,可以发现 CSS 文件被单独打包出来了

img

但是这时候刷新页面会发现图片的边框消失了,那是因为我们的 HTML 文件没有引用新的 CSS 文件,所以这里需要我们手动引入下,在下面的章节我们会通过插件的方式自动引入新的文件。

接下来,会用一个项目来继续我们的 webpack 学习,在这之前,先 clone 一下项目。该项目原地址是 这里**,因为使用的 webpack 版本太低,并且依赖的库也有点问题,故我将项目拷贝了过来并修改了几个库的版本号。

请依次按照以下代码操作

git clone https://github.com/KieSun/webpack-demo.git
cd webpack-demo
// 切换到 0.1 标签上并创建一个新分支
git checkout -b demo 0.1
// 查看分支是否为 demo,没问题的话就可以进行下一步
gst

如何在项目中使用 webpack

项目中已经配置了很简单的 babel 和 webpack,直接运行 npm run start 即可

img

这时候你会发现这个 bundle.js 居然有这么大,这肯定是不能接受的,所以接下来章节的主要目的就是将单个文件拆分为多个文件,优化项目。

分离代码

先让我们考虑下缓存机制。对于代码中依赖的库很少会去主动升级版本,但是我们自己的代码却每时每刻都在变更,所以我们可以考虑将依赖的库和自己的代码分割开来,这样用户在下一次使用应用时就可以尽量避免重复下载没有变更的代码,那么既然要将依赖代码提取出来,我们需要变更下入口和出口的部分代码。

// 这是 packet.json 中 dependencies 下的
const VENOR = ["faker",
  "lodash",
  "react",
  "react-dom",
  "react-input-range",
  "react-redux",
  "redux",
  "redux-form",
  "redux-thunk"
]

module.exports = {
// 之前我们都是使用了单文件入口
// entry 同时也支持多文件入口,现在我们有两个入口
// 一个是我们自己的代码,一个是依赖库的代码
  entry: {
  // bundle 和 vendor 都是自己随便取名的,会映射到 [name] 中
    bundle: './src/index.js',
    vendor: VENOR
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].js'
  },
  // ...
 }

现在我们 build 一下,看看是否有惊喜出现

img

img

真的有惊喜。。为什么 bundle 文件大小压根没变。这是因为 bundle 中也引入了依赖库的代码,刚才的步骤并没有抽取 bundle 中引入的代码,接下来让我们学习如何将共同的代码抽取出来。

抽取共同代码

在这小节我们使用 webpack 自带的插件 CommonsChunkPlugin。

module.exports = {
//...
  output: {
    path: path.join(__dirname, 'dist'),
    // 既然我们希望缓存生效,就应该每次在更改代码以后修改文件名
    // [chunkhash]会自动根据文件是否更改而更换哈希
    filename: '[name].[chunkhash].js'
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
    // vendor 的意义和之前相同
    // manifest文件是将每次打包都会更改的东西单独提取出来,保证没有更改的代码无需重新打包,这样可以加快打包速度
      names: ['vendor', 'manifest'],
      // 配合 manifest 文件使用
      minChunks: Infinity
    })
  ]
};

当我们重新 build 以后,会发现 bundle 文件很明显的减小了体积

img

但是我们使用哈希来保证缓存的同时会发现每次 build 都会生成不一样的文件,这时候我们引入另一个插件来帮助我们删除不需要的文件。

npm install --save-dev clean-webpack-plugin

然后修改配置文件

module.exports = {
//...
  plugins: [
  // 只删除 dist 文件夹下的 bundle 和 manifest 文件
    new CleanWebpackPlugin(['dist/bundle.*.js','dist/manifest.*.js'], {
    // 打印 log
      verbose: true,
      // 删除文件
      dry: false
    }),
  ]
};

然后 build 的时候会发现以上文件被删除了。

因为我们现在将文件已经打包成三个 JS 了,以后也许会更多,每次新增 JS 文件我们都需要手动在 HTML 中新增标签,现在我们可以通过一个插件来自动完成这个功能。

npm install html-webpack-plugin --save-dev

然后修改配置文件

module.exports = {
//...
  plugins: [
  // 我们这里将之前的 HTML 文件当做模板
  // 注意在之前 HTML 文件中请务必删除之前引入的 JS 文件
    new HtmlWebpackPlugin({
      template: 'index.html'
    })
  ]
};

执行 build 操作会发现同时生成了 HTML 文件,并且已经自动引入了 JS 文件

img

按需加载代码

在这一小节我们将学习如何按需加载代码,在这之前的 vendor 入口我发现忘记加入 router 这个库了,大家可以加入这个库并且重新 build 下,会发现 bundle 只有不到 300KB 了。

现在我们的 bundle 文件包含了我们全部的自己代码。但是当用户访问我们的首页时,其实我们根本无需让用户加载除了首页以外的代码,这个优化我们可以通过路由的异步加载来完成。

现在修改 src/router.js

// 注意在最新版的 V4路由版本中,更改了按需加载的方式,如果安装了 V4版,可以自行前往官网学习
import React from 'react';
import { Router, Route, IndexRoute, hashHistory } from 'react-router';

import Home from './components/Home';
import ArtistMain from './components/artists/ArtistMain';

const rootRoute = {
  component: Home,
  path: '/',
  indexRoute: { component: ArtistMain },
  childRoutes: [
    {
      path: 'artists/new',
      getComponent(location, cb) {
        System.import('./components/artists/ArtistCreate')
          .then(module => cb(null, module.default))
      }
    },
    {
      path: 'artists/:id/edit',
      getComponent(location, cb) {
        System.import('./components/artists/ArtistEdit')
          .then(module => cb(null, module.default))
      }
    },
    {
      path: 'artists/:id',
      getComponent(location, cb) {
        System.import('./components/artists/ArtistDetail')
          .then(module => cb(null, module.default))
      }
    }
  ]
}

const Routes = () => {
  return (
    <Router history={hashHistory} routes={rootRoute} />
  );
};

export default Routes;

然后执行 build 命令,可以发现我们的 bundle 文件又瘦身了,并且新增了几个文件

img

将 HTML 文件在浏览器中打开,当点击路由跳转时,可以在开发者工具中的 Network 一栏中看到加载了一个 JS 文件。

首页

img

点击右上角 Random Artist 以后

img

自动刷新

每次更新代码都需要执行依次 build,并且还要等上一会很麻烦,这一小节介绍如何使用自动刷新的功能。

首先安装插件

npm i --save-dev webpack-dev-server

然后修改 packet.json 文件

"scripts": {
    "build": "webpack",
    "dev": "webpack-dev-server --open"
  },

现在直接执行 npm run dev 可以发现浏览器自动打开了一个空的页面,并且在命令行中也多了新的输出

img

等待编译完成以后,修改 JS 或者 CSS 文件,可以发现 webpack 自动帮我们完成了编译,并且只更新了需要更新的代码

img

但是每次重新刷新页面对于 debug 来说很不友好,这时候就需要用到模块热替换了。但是因为项目中使用了 React,并且 Vue 或者其他框架都有自己的一套 hot-loader,所以这里就略过了,有兴趣的可以自己学习下。

生成生产环境代码

现在我们可以将之前所学和一些新加的插件整合在一起,build 生产环境代码。

npm i --save-dev url-loader optimize-css-assets-webpack-plugin file-loader extract-text-webpack-plugin

修改 webpack 配置

var webpack = require('webpack');
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin')
var CleanWebpackPlugin = require('clean-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')

const VENOR = ["faker",
  "lodash",
  "react",
  "react-dom",
  "react-input-range",
  "react-redux",
  "redux",
  "redux-form",
  "redux-thunk",
  "react-router"
]

module.exports = {
  entry: {
    bundle: './src/index.js',
    vendor: VENOR
  },
  // 如果想修改 webpack-dev-server 配置,在这个对象里面修改
  devServer: {
    port: 8081
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].[chunkhash].js'
  },
  module: {
    rules: [{
        test: /\.js$/,
        use: 'babel-loader'
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        use: [{
            loader: 'url-loader',
            options: {
                limit: 10000,
                name: 'images/[name].[hash:7].[ext]'
            }
        }]
    },
    {
        test: /\.css$/,
        loader: ExtractTextPlugin.extract({
            fallback: 'style-loader',
            use: [{
            // 这边其实还可以使用 postcss 先处理下 CSS 代码
                loader: 'css-loader'
            }]
        })
    },
    ]
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: ['vendor', 'manifest'],
      minChunks: Infinity
    }),
    new CleanWebpackPlugin(['dist/*.js'], {
      verbose: true,
      dry: false
    }),
    new HtmlWebpackPlugin({
      template: 'index.html'
    }),
    // 生成全局变量
    new webpack.DefinePlugin({
      "process.env.NODE_ENV": JSON.stringify("process.env.NODE_ENV")
    }),
    // 分离 CSS 代码
    new ExtractTextPlugin("css/[name].[contenthash].css"),
    // 压缩提取出的 CSS,并解决ExtractTextPlugin分离出的 JS 重复问题
    new OptimizeCSSPlugin({
      cssProcessorOptions: {
        safe: true
      }
    }),
    // 压缩 JS 代码
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    })
  ]
};

修改 packet.json 文件

"scripts": {
    "build": "NODE_ENV=production webpack -p",
    "dev": "webpack-dev-server --open"
  }

执行 npm run build

img

可以看到我们在经历了这么多步以后,将 bundle 缩小到了只有 27.1KB,像 vendor 这种常用的库我们一般可以使用 CDN 的方式外链进来。

补充

webpack 配置上有些实用的小点在上文没有提到,统一在这里提一下。

module.exports = {
  resolve: {
  // 文件扩展名,写明以后就不需要每个文件写后缀
    extensions: ['.js', '.css', '.json'],
 // 路径别名,比如这里可以使用 css 指向 static/css 路径
    alias: {
      '@': resolve('src'),
      'css': resolve('static/css')
    }
  },
  // 生成 source-map,用于打断点,这里有好几个选项
  devtool: '#cheap-module-eval-source-map',
}

后记

如果你是跟着本文一个个步骤敲下来的,那么大部分的 webpack 配置你应该都是可以看懂了,并且自己应该也知道如何去配置。谢谢大家看到这里,这是本项目对应的仓库**,每个小节基本都对应了一次 commit。

文章较长,有错误也难免,如果你发现了任何问题或者我有任何表述的不明白的地方,都可以留言给我。

HTML 中常用的 meta 元素

meta标签提供了 HTML 文档的元数据。元数据不会显示在客户端,但是会被浏览器解析。META元素通常用于指定网页的描述,关键词,文件的最后修改时间,作者及其他元数据。元数据可以被使用浏览器(如何显示内容或重新加载页面),搜索引擎(关键词),或其他 Web 服务调用。

meta 的属性有:name属性、http-equiv属性 、content属性和charset属性 。

name属性:

name 属性规定元数据的名称。

name 属性规定content属性的信息/值的名称。

注意:如果设置了 http-equiv 属性,则不应该设置 name 属性。

语法

<meta name="value">

属性值

application-name 规定页面所代表的 Web 应用程序的名称。

author 规定文档的作者的名字。

<meta name = "author" content = "作者的姓名">

description 规定页面的描述。搜索引擎会把这个描述显示在搜索结果中。

<meta name = "description" content = "对页面的描述语言”>

generator 规定用于生成文档的一个软件包(不用于手写页面)。

<meta name = "generator" content = "编辑软件的名称">

keywords 规定一个逗号分隔的关键词列表 - 相关的网页(告诉搜索引擎页面是与什么相关的)。

提示:总是规定关键词(对于搜索引擎进行页面分类是必要的)。

<meta name = "keywords" content = "具体的关键字">

referrer 控制所述HTTP的内容Referer附加到从本文件发送的任何请求的HTTP标头。

<meta name="referrer">
no-referrer 不要发送一个HTTP Referer标头。
origin 发送起源的文件。
no-referrer-when-downgrade 发送原点作为引荐先验的,多的安全目的地(https-> HTTPS),但不发送引荐一个不太安全的目的地(https-> HTTP)。这是默认的行为。
origin-when-crossorigin 执行相同来源的请求时,发送一个完整的URL(从参数剥离),但只送起源的其他情况下的文件。
unsafe-URL perfoming来源相同的请求时,发送一个完整的URL(从参数剥离)。

creator 限定,在自由格式中,文件的创建者的名称。请注意,它可以是机构的名称。如果有不止一个,有几个要素都应该使用。

googlebot 这是一个代名词robots,但只有其是googlebot,索引爬虫对谷歌搜索。

publisher 在一个自由的格式,文件的发布者的名称。请注意,它可以是该机构的名称。

robots(定义搜索 引擎爬虫的索引方式):content的参数有all(默认 ),none,index,follow,nofollow。

<meta name="robots" content="none">

slurp 这是一个代名词robots,但只有其是slurp,索引爬虫对雅虎搜索。

viewport 这给出了关于的初始大小的大小提示视口。

<meta name="viewport" content="width=device-width, initial-scale=1.0">
可能值 描述
width 一个正整数或者字符串 device-width 限定了宽度,以像素为单位,视口的
height 一个正整数或者字符串 device-height 定义了高度,以像素,视口的
initial-scale 一个0.0 到10.0之间的正数 定义设备宽度(之间的比率device-width以纵向模式或device-height横向模式)和视口的大小。
maximum-scale 一个0.0 到10.0之间的正数 定义了变焦的最大值; 它必须大于或等于minimum-scale或行为是不确定的。
minimum-scale 一个0.0 到10.0之间的正数 定义了变焦的最低值; 它必须小于或等于maximum-scale或行为是不确定的。
user-scalable 一个布尔值(yes 或者no) 如果设置为no,用户无法在网页中放大。默认值是yes。

scheme 该属性定义了描述元数据的方案。scheme是一种上下文,它可以对内容值进行正确的解释,就像一种格式。

http-equiv属性:

http-equiv 属性提供了content属性的信息/值的 HTTP 头。

http-equiv 属性可用于模拟一个 HTTP 响应头。

语法

<meta http-equiv="content-type|default-style|refresh">

属性值

content-type 规定文档的字符编码。

<meta http-equiv="content-type" content="text/html; charset=utf-8">

default-style 规定要使用的预定义的样式表。

<meta http-equiv="default-style" content="the document's preferred stylesheet">

注释:上面 content 属性的值必须匹配同一文档中的一个 link 元素上的 title 属性的值,或者必须匹配同一文档中的一个 style 元素上的 title 属性的值。

refresh 定义文档自动刷新的时间间隔。

<meta http-equiv="refresh" content="300">

set-cookie 设置cookie,如果网页过期,那么旧cookie将被删除。

<meta http-equiv="set-cookie" content="cookievalue=xxx;expires=Monday,12 Jan 2020 20:20:20 GMT;path=/">

content 属性:

定义与 http-equiv 或 name 属性相关的元信息。

content 属性给出了与 http-equiv 或 name 属性相关的值。

语法

<meta content="text">

属性值

text :meta 信息的内容。

charset属性:

charset 属性规定 HTML 文档的字符编码。

提示:charset 属性可以通过任意元素上的 lang 属性来重写。

语法

<meta charset="character_set">

属性值

character_set :规定 HTML 文档的字符编码。

常用的值:

UTF-8 - Unicode 字符编码

ISO-8859-1 - 拉丁字母表的字符编码

<meta charset ="UTF-8">

在理论上,可以使用任何字符编码,但并不是所有浏览器都能够理解它们。某种字符编码使用的范围越广,浏览器就越有可能理解它。

本篇博客转载地址

Flex基础

常见布局

  • normal flow(正常流)
  • float + clear(浮动+清楚浮动)
  • position relative + absolute (相对定位+绝对定位)
  • display inline-block (改变元素属性)
  • -margin(负外边距)

Flex布局特性

  1. 块级布局侧重垂直方向、行内布局侧重水平方向,flex布局是与方向无关的

  2. flex布局可以实现空间自动分配自动对齐(全称:the flexible box Module)

  3. flex适用于简单的线性布局,更复杂的布局要交给grid布局(目前还没发布)

    语法

    display: flex;

    display: inline-flex;

    设为Flex布局后,子元素的loat,clearvertical-align属性将失效。

Flex基础概念

采用Flex布局的元素,称为Flex容器(flex container),简称"容器"。它的所有子元素自动成为容器成员,称为Flex项目(flex item),简称"项目"。

flex基础分解图

  • 上图水平是主轴(main axis)垂直是侧轴(cross axis)。主轴的开始位置叫做main start,结束位置叫做main end;侧轴的开始位置叫做cross start,结束位置叫做cross end

  • 项目默认沿主轴排列。单个项目占据的主轴空间叫做main size,占据的侧轴空间叫做cross size

    Flex container容器属性

    flex-direction: 决定了主轴的方向(即项目的排列方向)

    .box{
         display:flex;
         flex-direction:column-revers|column|row-reverse|row;
         }
    
    属性名 说明
    row(默认) 主轴方向为水平,起点在左端
    row- reverse 主轴方向为水平,起点在右端
    column 主轴方向为垂直,起点在上端
    column-reverse 主轴方向为垂直,起点在下端

    flex-direction排序图

    单独添加flex-direction: row; //默认是不会换行

    flex-wrap: 定义如果一条轴线排不下,如何换行

    .box{
         display:flex;
         flex-direction:now  
         flex-wrap:wrap;
        }
    
    属性名 说明
    nowrap(默认) 不换行
    wrap 换行
    wrap-reverse 换行反转


    flex-flow(以上两个属性的简写)

    flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap

    .box{
        display:flex;
        flex-flow: row nowrap;
        }
    

    justify-content:定义了项目在主轴上的对齐方式

    .box{
        display:flex;
        justify-content:flex-start | flex-end | center | space-between | space-around;
    }
    
    属性名 说明
    flex-start(默认) 项目沿着主轴方向的起始位置靠齐
    flex-end 项目沿着主轴方向的结束位置靠齐,和flex-start相反
    space-between 项目在主轴方向上两端对齐,其中的间隔相等
    space-around 每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍


align-items定义项目在交叉轴上如何对齐

.box{
    display:flex;
    align-items:flex-start | flex-end | center | baseline | stretch;
}
属性名 说明
flex-start 项目沿着侧轴上的起点对齐
flex-end 项目沿着侧轴上的终点对齐
center 项目在侧轴方向上居中对齐
stretch(默认) 如果项目未设置高度或高度为auto,将占满整个容器的高度。


align-content多行多列内容对齐方式

注意如果项目只有一行,属性不起作用

.box{
    display:flex;
    align-content:flex-start | flex-end | center | space-between | space-around | stretch;
}
属性名 说明
flex-start 项目与侧轴的起点对齐
flex-end 项目与侧轴的终点对齐
center 项目与侧轴的中点对齐
space-between 与侧轴两端对齐,轴线之间的间隔平均分布
space-around 每行轴线两侧的间隔都相等。所以轴线之间的间隔比轴线与边框的间隔大一倍
stretch(默认) 轴线占满整个测轴


flex item项目属性

  • order属性:定义项目的排列顺序。数值越小,排列越靠前,默认为0

.item{order: <integer>;}

  • flex-grow属性:定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大

.item{flex-grow:<number>;}

flex-grow为零的话则不变化,不能为負值。如果所有项目的flex-grow属性都为1,则它们将等分剩余空间(如果有的话)。如果一个项目的flex-grow属性为2,其他项目都为1,则前者占据的剩余空间将比其他项的2倍。

  • flex-shrink属性:定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小

.item{flex-shrink:<number>;}

如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。负值对该属性无效。

  • flex-basis属性:定义了在分配多余空间之前,项目占据的主轴空间(main size)

浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。它可以设为跟width或height属性一样的值,则项目将占据固定空间

.item{flex-basis: <length>| auto;

  • flex属性:flex-grow,flex-shrink和flex-basis的简写

默认值为0 1 auto。后两个属性可选。该属性有两个快捷值:auto(1 1 auto) 和 none (0 0 auto)。

  • align-self属性:允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性

默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。

.item{align-self:auto | flex-start | flex-end | center | baseline | stretch;}

该属性可能取6个值,除了auto,其他都与align-items属性完全一致。

最近发现一篇很详细关于flex box 布局的博客链接
阮一峰flex实战布局教程

Ajax 基础知识

2005年,Jesse James Garrett 发表了一篇在线文章,题为” Ajax:A new Approach to Web Applications “。他在这篇文章里介绍了一种技术,用他的话说,就叫 Ajax,是对 Asynchronous JavaScript + XML 的简写。这一技术能够向服务器请求额外的数据而无须重载(刷新)页面,会带来更好的用户体验。

XMLHttpRequest 对象

Ajax 技术的核心是 XMLHttpRequest 对象,这是由微软首先引入的一个特性,其他浏览器提供商后来都提供了相同的实现。

responseText:作为响应主体被返回的文本。
responseXML:如果响应的内容类型是”text/xml“或”application/xml“,这个属性中保存包含着响应数据的 XML DOM 文档。
status:响应的 HTTP 状态。
statusText:HTTP 状态的说明。

var request = new XMLHttpRequest();
request.onload = function(){
  console.log(this.responseText);
}
request.open("GET","/xxx");
request.send();

这就是一个简单的 Ajax。

readyState 属性

属性值如下:

0:未初始化。尚未调用 open() 方法。
1:启动。已经调用 open() 方法,但尚未调用 send() 方法。
2:发送。已经调用 send() 方法,但尚未接收到响应。
3:接收。已经接收到部分响应数据。
4:完成。已经接收到全部响应数据,而且已经可以在客户端使用了。

只要 readyState 属性的值由一个值变成另一个值,都会触发一次 readystatechange 事件。

var request = new XMLHttpRequest();
request.onreadystatechange = function(){
  console.log(this.readyState); // 1 2 3 4
};
request.open('GET','/xxx');
request.send()

HTTP 头部信息

Accept:浏览器能够处理的内容类型。
Accept-Charset:浏览器能够显示的字符集。
Accept-Encoding:浏览器能够处理的压缩编码。
Accept-Language:浏览器当前设置的语言。
Connection:浏览器与服务器之间连接的类型。
Cookie:当前页面设置的任何 Cookie。
Host:发出请求的页面所在的域。
Referer:发出请求的页面的 URI。
User-Agent:浏览器的用户代理字符串。

使用 setRequestHeader() 方法可以设置自定义请求头部信息。这个方法接受两个参数:头部字段的名称和头部字段的值。要成功发送请求头部信息,必须在调用 open() 方法之后且调用 send() 方法之前调用 setRequestHeader() 。

var request = new XMLHttpRequest();
request.onload = function(){
  console.log(this.responseText)
};
request.open('GET','/xxx');
request.setRequestHeader('MyHeader','MyValue');
request.send()

img

其他详细的AJAX知识地址

制作手机画板Demo重要组成部分

制作手机画板Demo重要组成部分

  1. touchstart开始触摸点
  2. touchmove触摸移动点
  3. touchend触摸结束点

touch属性

  • 概要

一个 TouchList,其会列出所有当前在与触摸表面接触的 Touch 对象,不管触摸点是否已经改变或其目标元素是在处于 touchstart 阶段。

  • 语法

var touches = touchEvent.touches;

  • 返回值是touches

一个 TouchList,其会列出所有当前在与触摸表面接触的 Touch 对象,不管触摸点是否已经改变或其目标元素是在处于 touchstart 阶段。

  • 如何调取touch的属性
$(Element).on('touchstart',function(event){
     console.log(event)
}
someElement.addEventListener('touchstart',function(event){
    console.log(event)
})

touches[0]Touch返回值主要属性介绍

  • clientX,clientY 是相对于浏览器窗口的X,Y位置
  • screenX,screenY 是相对于屏幕的X,Y位置
  • pageX,pageY 是相对于页面的X,Y位置
  • 调用方法如下:
someElement.addEventListener('touchstart',function(event){
    let {clientX,clientY} = e.touches[0] //ES6新增语法糖
    //下边两行代码等同于上边代码
    let clientX = e.touches[0].clientX
    let clientY = e.touches[0].clientY
})

Canvas

  • Canvas默认display:inline 需要更改为display:block.

  • Canvas默认有宽高比例,因此需要手动设置宽高,并且不能在CSS中添加,因为会不等比的缩放它。最好是在html中直接添加

JavaScript相关属性

Element.setAttribute()

设置指定元素上的一个属性值。

如果属性已经存在,则更新该值; 否则将添加一个新的属性用指定的名称和值。

要获取属性的当前值,使用 getAttribute();

要删除一个属性,调用removeAttribute()

Element.clientWidth

Element.clientWidth 属性表示元素的内部宽度,以像素计。该属性包括内边距,但不包括垂直滚动条(如果有)、边框和外边距。需要这样调用它document.documentElement.clientWidth

详细的介绍地址:阮一封

Javascript中的querySelector、style、getComputedStyle方法

querySelector()得到的是<第一个><满足选择条件>的元素

querySelecorAll()返回的就是一个数组了,包含了<满足选择条件的>所有元素,所以对于querySelecorAll()返回的元素不能直接操作,否则就会报错。需要在在后边加[0]

style<只能>获取到内嵌样式的值即(style="margin-left: 0; ")style属性中的css样式,这就是style的地盘。那getComputedStyle()那就不一样了简直是内外通吃,只要css样式被应用,其值就能被getComputedStyle()取到,那style岂不是小弟了嘛,不不,getComputedStyle()虽然取值功夫相当了得,但要想设置css样式,那对不起他做不到了,这就要靠style

setAttribute("width", "600")DOM添加CSS属性

parseInt()该函数从待转换的字符的第一个字符开始逐个读取字符,直到遇到非数字.然后将读取到的字符转换成数字...

Element.classList获取classname 列表

new

大部分讲 new 的文章会从面向对象的思路讲起,但是我始终认为,在解释一个事物的时候,不应该引入另一个更复杂的事物。

今天我从「省代码」的角度来讲 new。


想象我们在制作一个策略类战争游戏,玩家可以操作一堆士兵攻击敌方。

我们着重来研究一下这个游戏里面的「制造士兵」环节。

一个士兵的在计算机里就是一堆属性,如下图:

我们只需要这样就可以制造一个士兵:

var 士兵 = {
ID: 1, // 用于区分每个士兵
兵种:"美国大兵",
攻击力:5,
生命值:42,
行走:function(){ /走俩步的代码/},
奔跑:function(){ /狂奔的代码/ },
死亡:function(){ /Go die/ },
攻击:function(){ /糊他熊脸/ },
防御:function(){ /护脸/ }
}

兵营.制造(士兵)
制造一百个士兵

如果需要制造 100 个士兵怎么办呢?

循环 100 次吧:

var 士兵们 = []
var 士兵
for(var i=0; i<100; i++){
士兵 = {
ID: i, // ID 不能重复
兵种:"美国大兵",
攻击力:5,
生命值:42,
行走:function(){ /走俩步的代码/},
奔跑:function(){ /狂奔的代码/ },
死亡:function(){ /Go die/ },
攻击:function(){ /糊他熊脸/ },
防御:function(){ /护脸/ }
}
士兵们.push(士兵)
}

兵营.批量制造(士兵们)
哎呀好简单。

质疑

上面的代码存在一个问题:浪费了很多内存。

行走、奔跑、死亡、攻击、防御这五个动作对于每个士兵其实是一样的,只需要各自引用同一个函数就可以了,没必要重复创建 100 个行走、100个奔跑……
这些士兵的兵种和攻击力都是一样的,没必要创建 100 次。
只有 ID 和生命值需要创建 100 次,因为每个士兵有自己的 ID 和生命值。
改进

看过我们的专栏以前文章(JS 原型链)的同学肯定知道,用原型链可以解决重复创建的问题:我们先创建一个「士兵原型」,然后让「士兵」的 proto 指向「士兵原型」

var 士兵原型 = {
兵种:"美国大兵",
攻击力:5,
行走:function(){ /走俩步的代码/},
奔跑:function(){ /狂奔的代码/ },
死亡:function(){ /Go die/ },
攻击:function(){ /糊他熊脸/ },
防御:function(){ /护脸/ }
}
var 士兵们 = []
var 士兵
for(var i=0; i<100; i++){
士兵 = {
ID: i, // ID 不能重复
生命值:42
}

/实际工作中不要这样写,因为 proto 不是标准属性/
士兵.proto = 士兵原型

士兵们.push(士兵)
}

兵营.批量制造(士兵们)
优雅?

有人指出创建一个士兵的代码分散在两个地方很不优雅,于是我们用一个函数把这两部分联系起来:

function 士兵(ID){
var 临时对象 = {}

临时对象.proto = 士兵.原型

临时对象.ID = ID
临时对象.生命值 = 42

return 临时对象
}

士兵.原型 = {
兵种:"美国大兵",
攻击力:5,
行走:function(){ /走俩步的代码/},
奔跑:function(){ /狂奔的代码/ },
死亡:function(){ /Go die/ },
攻击:function(){ /糊他熊脸/ },
防御:function(){ /护脸/ }
}

// 保存为文件:士兵.js
然后就可以愉快地引用「士兵」来创建士兵了:

var 士兵们 = []
for(var i=0; i<100; i++){
士兵们.push(士兵(i))
}

兵营.批量制造(士兵们)
JS 之父的关怀

JS 之父创建了 new 关键字,可以让我们少写几行代码:

只要你在士兵前面使用 new 关键字,那么可以少做四件事情:

不用创建临时对象,因为 new 会帮你做(你使用「this」就可以访问到临时对象);
不用绑定原型,因为 new 会帮你做(new 为了知道原型在哪,所以指定原型的名字为 prototype);
不用 return 临时对象,因为 new 会帮你做;
不要给原型想名字了,因为 new 指定名字为 prototype。
这一次我们用 new 来写

function 士兵(ID){
this.ID = ID
this.生命值 = 42
}

士兵.prototype = {
兵种:"美国大兵",
攻击力:5,
行走:function(){ /走俩步的代码/},
奔跑:function(){ /狂奔的代码/ },
死亡:function(){ /Go die/ },
攻击:function(){ /糊他熊脸/ },
防御:function(){ /护脸/ }
}

// 保存为文件:士兵.js
然后是创建士兵(加了一个 new 关键字):

var 士兵们 = []
for(var i=0; i<100; i++){
士兵们.push(new 士兵(i))
}

兵营.批量制造(士兵们)
new 的作用,就是省那么几行代码。(也就是所谓的语法糖)

注意 constructor 属性

new 操作为了记录「临时对象是由哪个函数创建的」,所以预先给「士兵.prototype」加了一个 constructor 属性:

士兵.prototype = {
constructor: 士兵
}
如果你重新对「士兵.prototype」赋值,那么这个 constructor 属性就没了,所以你应该这么写:

士兵.prototype.兵种 = "美国大兵"
士兵.prototype.攻击力 = 5
士兵.prototype.行走 = function(){ /走俩步的代码/}
士兵.prototype.奔跑 = function(){ /狂奔的代码/ }
士兵.prototype.死亡 = function(){ /Go die/ }
士兵.prototype.攻击 = function(){ /糊他熊脸/ }
士兵.prototype.防御 = function(){ /护脸/ }
或者你也可以自己给 constructor 重新赋值:

士兵.prototype = {
constructor: 士兵,
兵种:"美国大兵",
攻击力:5,
行走:function(){ /走俩步的代码/},
奔跑:function(){ /狂奔的代码/ },
死亡:function(){ /Go die/ },
攻击:function(){ /糊他熊脸/ },
防御:function(){ /护脸/ }
}
完。

本篇博客转载自知乎前端学习指南
本篇博客转载地址

ajax验证账户密码

ajax基础知识

验证流程:

  1. 阻止submit的默认事件,前端添加error变化状态
  2. 后端将前端提交的信息进行处理,保留有用的信息
  3. 后端验证用户名密码是否正确,将错误信息保存到一个errors={}对象中
  4. 如果正确,将响应的内容传到前端,弹出欢迎登录
  5. 如果不正确,因为response.end()不接受对象只接受字符串,所以用JSON.stringify(object)将errors对象转化成JSON字符串
  6. 将后端错误的信息进行遍历,将对应的错误信息传送到前端对应的显示错误位置上。

form创建HTML表单

form标签是用于创建用户输入的HTML表单
form表单包含以下元素

<input>
<textarea>
<button>
<select>
<option>
<optgroup>
<fieldset>
<label>

form的应用方式

<form action="/yyy" method="GET" id="loginForm">
		<lable>
			用户名
			<input name="username" type="text">
			<span class="errorMessage">错误提示</span>
		</lable>
		<lable>
			密码
			<input type="password" name="password">
			<span class="errorMessage">错误提示</span>
			<input type="submit">
		</lable>
	</form>
  • action: 提交路径
  • method: 请求类型,GET/PSOT,提交数据用POST,获取数据用GET。
  • type: 输入框类型
  • name: 查询参数

还有两个技巧如果想点击用户名密码文字让相应的input获得焦点那么可以在<lable for='关联词'> 相应的在 <input id="关联词">或者是像form的应用方式代码那样让lable标签包住input。这样也可以达到相应的效果

浏览器请求与响应

HTTP请求

  1. GET /路径 HTTP/1.1 协议版本
  2. Host: baidu.com key vaule
    Content-Type:application/x-www-form-urlencoded
  3. 回车
  4. name=ff&age=18

HTTP 响应

  1. HTTP/1.1 状态码200 ok
    Content-Type:text/html;charset=utf-8
  2. key:value
  3. 回车
  4. <--! DOCTYPE >

ajax特质是发送&获取数据都不会刷新页面

loginForm.addEventListener('submit',function(e){
  e.preventDefault()
  let username = loginForm.username.value
  let password = loginForm.password.value
  let request = new XMLHttpRequest()
  request.onload = function(){
    alert('得到响应')
  }
  /*如果是GET获取请求*/
  request.open('GET',`/login?username=${username}&password=${password}`)//请求方式和路径
  requset.setRequestHeader('key','value') //设置第四部份内容RequsetHeader 可以不写
  requset.send()
  /*如果是POST提交请求*/
  request.open('POST',`/login`) //请求的方式和路径
  request.send(`username=${username}&password=${password}`)//写入第四部分内容,给服务器提交信息
})

XMLHttpRequest 对象有一个重要属性 readyState

  • 0:被代理创建,但是并没有被调用open

  • 1:open()被调用

  • 2:send()被调用并且请求头部(请求第一部分)状态已经可以获得(请求第二部分)

  • 3:下载中responseText属性已经包含部分数据

  • 4:下载完成后出发onload事件

onreadystatechange这个事件可以用来判断是否下载完毕

request.onreadystatechange=function(){    
    if(request.readyState === 4){
        console.log('下载完毕')
    }
}

前端表单验证

Let valid = true

if($.trim(username) ===''){
    $('input[name="username"]').addClass('error').next('.errorMessage').text('用户名不能为空')
  	valid = false
}
if(password ===''){
    $('input[name="password"]').addClass('error').next('.errorMessage').text('密码不能为空')
  	valid = false
}
if(valid === false){
    return  		//终止函数运行
}

后端表单验证

后端映射的前端代码

let request = new XMLHttpRequest()
		request.onreadystatechange=function(){
			if(request.readyState === 4){
				if(request.status >= 400){
					let {errors} = JSON.parse(request.responseText)
					for(var key in errors){
						let value = errors[key]
						$('input[name=${key}]').addClass('error').next('.errorMessage').text(value)
					}
					
				}else{
					location.href="/home" //跳转首页
				}
				
			}
        }

后端验证代码

 let errors = {}

        if(username.trim() === ''){
          errors['username'] = '用户名不能为空'
        }else if(username === 'cheyongzhi'){
          if(password === ''){
            errors['password'] = '密码不能为空'
          }else if(password !== '123123'){
            errors['password'] = '密码错误'
          }
        }else{
          errors['username'] = '用户名不存在'
        }
        if(password === ''){
          errors['password'] = '密码不能为空'
        }

        if(Object.keys(errors).length > 0){
          response.statusCode = 412
          var string = JSON.stringify({errors:errors})
          response.end(string)
        }else{
          response.statusCode = 200
          response.end('你好' + username) //response.end(JSON.stringify({userId:1}))
        }
      })

其它知识点

  • JSON.stringify(object)能将一个对象转化成JSON语法字符串

  • JSON.parse(text[, reviver])方法解析一个JSON字符串,构造由字符串描述的JavaScript值或对象。可以提供可选的reviver函数以在返回之前对所得到的对象执行变换

  • Object.keys(obj)方法会返回一个由一个给定对象的自身可枚举属性组成的数组

  • ES6语法:

let { foo: baz } = { foo: "aaa", bar: "bbb" };
baz // "aaa"
foo // error: foo is not defined
上面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo。

typeof,instanceof,constructor它们的用法和区别

typeof和instanceof的区别

typeof和instanceof都可以用来判断变量,它们的用法有很大区别:
typeof会返回一个变量的基本类型,只有以下几种:number,boolean,string,object,undefined,function;例:
alert(typeof(1));//number
alert(typeof("abc"));//string
alert(typeof(true));//boolean
alert(typeof(m));//undefined
如果我们想要判断一个变量是否存在,可以使用typeof:(不能使用if(a) 若a未声明,则报错)
if(typeof a != 'undefined'){
//变量存在
}
instanceof返回的是一个布尔值,如:
var a = {};
alert(a instanceof Object); //true
var b = [];
alert(b instanceof Array); //true
需要注意的是,instanceof只能用来判断对象和函数,不能用来判断字符串和数字等,如:
var b = '123';
alert(b instanceof String); //false
alert(typeof b); //string
var c = new String("123");
alert(c instanceof String); //true
alert(typeof c); //object
另外,用instanceof可以判断变量是否为数组

大家都知道js中可以使用typeof来判断变量的基本类型,如:
alert(typeof '111'); // "string"
alert(typeof 22); // "number"
alert(typeof a); // "undefined"
alert(typeof undefined); // "undefined"
alert(typeof []); // "object"
但是这个方法不适用于来判断数组,因为不管是数组还是对象,都会返回object,这就需要我们需求其他的方法。
有几种方法可以拿来判断:

1、constructor属性

这个属性在我们使用js系统或者自己创建的对象的时候,会默认的加上,例如:
var arr = [1,2,3]; //创建一个数组对象
arr.prototype.constructor = Array; //这一句是系统默认加上的
所以我们就可以这样来判断:
var arr = [1,2,3,1];
alert(arr.constructor === Array); // true

2、instanceof

instanceof是检测对象的原型链是否指向构造函数的prototype对象的,所以我们也可以用它来判断:

var arr = [1,2,3];
alert(arr instanceof Array); // true

最后,为了给大家一个结果,现写出一个终极解决方案:
判断数组终极解决方案
var arr = [1,2,3];
function isArrayFn(obj){ //封装一个函数
if (typeof Array.isArray === "function") {
return Array.isArray(obj); //浏览器支持则使用isArray()方法
}else{ //否则使用toString方法
return Object.prototype.toString.call(obj) === "[object Array]";
}
}
alert(isArrayFn(arr));// true

本篇博客摘抄地址

BOM初步认识

什么是BOM

BOM:Browser Object Model 浏览器对象模型
BOM是JavaScript组成之一,它是浏览器窗口的API;
BOM的核心对象是window,所以BOM是全局对象,window是浏览器提供给JS的API。

BOM的常用API

window.name = xxx 会将当前网页的名字声明为‘xxx’字符串,所以在JS中不要声明name,因为var name = true等同于window.name = true那么name === 'true';typeof name === 'string'不能全局变量的字符串有哪些可以在控制台输入window回车查看。

window.open语句: let windowObjectReference = window.open(strUrl, strWindowName, [strWindowFeatures]); 示例:window.open('http://www.baidu.com','_blank','left=100,top=100,width=100,height=100'//打开网页
window.close() //关闭网页
window.opener.close() //被打开的网页关闭
window.history.back() || window.history.forward() || window.history.go(-1||2) //后退 前进 跳转
window.location.reload(false/true) //重新加载当前页面
window.location = 'xxx' === window.location.href = 'xxx'//会在当前路径下的'xxx'路径
window.location.protocol //获取网页协议
window.location.host //获取域名
window.location.port //获取端口
window.location.pathname //获取路径
window.location.search //获取查询字符串
window.location.hash //获取锚点
window.navigator.(language 语言||userAgent 当前是什么浏览器) //获取浏览器的相关信息
window.pageX/YOffset //网页滚动位置
window.confirm('xxx') //弹出确认和取消
window.scrollTo(500,500) //网页滚动到(500,50)
window.outerWidth/outerHeight //获取完整窗口大小
innerWidth/innerHeight //获取文档显示区大小
resizeTo(width,height) //新窗口大小
location.replace(“url”) //当前页面打开不可后退
clearInterval(timer);time=null //定时器一次性
cleartimeout(timer);time=null //定时器周期性
clearInterval(timer); timer=null //停止定时器周期性
clearTimeout(timer); timer=null //停止定时器一次性
btn.addEventListener(“事件名”,函数对象) //添加事件监听
btn.remove //删除事件监听
EventListener(“事件名”,函数对象)
e.stopPropagation() //阻止冒泡
e.preventDefault //取消事件(阻止默认行为)


BOM概述

BOM是browser object model的缩写,简称浏览器对象模型

BOM提供了独立于内容而与浏览器窗口进行交互的对象,主要处理浏览器窗口和框架,不过通常浏览器特定的 JavaScript 扩展都被看做 BOM 的一部分。这些扩展包括:

1.弹出新的浏览器窗口

2.移动、关闭浏览器窗口以及调整窗口大小

3.提供 Web 浏览器详细信息的定位对象

4.提供用户屏幕分辨率详细信息的屏幕对象

5.对 cookie 的支持

6.IE 扩展了BOM,加入了ActiveXObject 类,可以通过 JavaScript 实例化 ActiveX 对象

由于BOM主要用于管理窗口与窗口之间的通讯,因此其核心对象是window

BOM由一系列相关的对象构成,并且每个对象都提供了很多方法与属性

BOM缺乏标准, 是各个浏览器厂商根据DOM在各自浏览器上的实现(表现为不同浏览器定义有差别,实现方式不同);JavaScript语法的标准化组织是ECMA,DOM的标准化组织是W3C

BOM最初是Netscape浏览器标准的一部分

img

window对象是BOM的顶层(核心)对象,不是JS对象,所有对象都是通过它延伸出来的,也可以称为window的子对象。JavaScript是通过访问BOM(Browser Object Model)对象来访问、控制、修改客户端(浏览器),由于BOM的window包含了document,window对象的属性和方法是直接可以使用而且被感知的,因此可以直接使用window对象的document属性,通过document属性就可以访问、检索、修改XHTML文档内容与结构。因为document对象又是DOM(Document Object Model)模型的根节点。可以说,BOM包含了DOM(文档对象模型),浏览器提供出来给予访问的是BOM对象,从BOM对象再访问到DOM对象,从而js可以操作浏览器以及浏览器读取到的文档。

BOM的组成

一、window对象

window对象是BOM的核心。它具有双重角色,既是通过js访问浏览器窗口的一个接口,又是一个Global(全局)对象。这意味着在网页中定义的任何对象,变量和函数,都以window作为其global对象。

全局的window对象

JavaScript中的任何一个全局函数或变量都是window的属性

window与self对象

self对象与window对象完全相同,self通常用于确认就是在当前的窗体内

window的子对象

1.document 对象

2.frames 对象

3.history 对象

4.location 对象

5.navigator 对象

6.screen 对象

window对象的函数和属性索引

窗体控制

moveBy() 函数可相对窗口的当前坐标把它移动指定的像素

moveTo() 函数可把窗口的左上角移动到一个指定的坐标

resizeBy() 函数用于根据指定的像素来调整窗口的大小

resizeTo() 函数用于把窗口大小调整为指定的宽度和高度

screenLeft 和 screenTop属性返回窗口相对于屏幕的X和Y坐标

screenX 和 screenY 属性返回窗口相对于屏幕的X和Y坐标

pageXOffset 和 pageYOffset 属性设置或返回当前页面相对于窗口显示区左上角的 X 位置。pageYOffset 设置或返回当前页面相对于窗口显示区左上角的 Y 位置

outerWidth 和 outerHeight 属性设置或返回一个窗口的外部宽度(高度),包括所有界面元素(如工具栏/滚动条)

窗体滚动轴控制

scrollBy() 函数可把内容滚动指定的像素数

scrollTo() 函数可把内容滚动到指定的坐标

提示:上面几个函数的名字最后都带有To或By,to是绝对的意思(从整体而言),by是相对的意思(从原先的位置而言)

窗体焦点控制

focus() 函数可把键盘焦点给予一个窗口

blur() 函数可把键盘焦点从顶层窗口移开

新建窗体

open() 函数用于打开一个新的浏览器窗口或查找一个已命名的窗口

close() 函数用于关闭浏览器窗口

createPopup() 函数用来创建一个弹出窗口

opener 属性是一个可读可写的属性,可返回对创建该窗口的 Window 对象的引用

closed 属性可返回一个布尔值,该值声明了窗口是否已经关闭

对话框

alert() 函数用于显示带有一条指定消息和一个 确认 按钮的警告框

confirm() 函数用于显示一个带有指定消息和确认及取消按钮的对话框

prompt() 函数用于显示可提示用户进行输入的对话框

状态栏

window.defaultStatus 属性可设置或返回窗口状态栏中的默认文本。该属性可读可写

window.status 属性可设置或返回窗口状态栏中的文本

print() 函数用于打印当前窗口的内容

时间等待与间隔

setTimeout() 函数用于在指定的毫秒数后调用函数或计算表达式

clearTimeout() 函数可取消由 setTimeout() 方法设置的 timeout

setInterval() 函数可按照指定的周期(以毫秒计)来调用函数或计算表达式

clearInterval() 函数可取消由 setInterval() 设置的 timeout

二、document对象

document对象用于表现HTML页面当前窗体的内容,是window对象的属性,包含了一个节点对象(该对象包含每个单独页面的所有HTML元素,这就是W3C的DOM对象,由于document对象内容比较我,我们将在后面专门撰写文章进行讲解)。

三、frames对象

frames对象用于表现HTML页面当前窗体的中的框架集合,是window对象的属性。如果页面使用框架,将产生一个框架集合frames,在集合中可用数字(从0开始,从左到右,逐行索引)或名字索引框架。

window:当前框架

self 属性可返回对窗口自身的只读引用

Top 属性返回当前窗口的最顶层浏览器窗口

parent 属性返回当前窗口的父窗口

四、history对象

history对象用于窗体中的导航,是window对象的属性,浏览者通常可以使用浏览器的前进与后退按钮访问曾经浏览过的页面。JavaScript的history对象记录了用户曾经浏览过的页面,并可以实现浏览器前进与后退相似的导航功能;可以通过back函数后退一个页面,forward函数前进一个页面,或者使用go函数任意后退或前进页面,还可以通过length属性查看history对象中存储的页面数。

go() 函数可加载历史列表中的某个具体的页面

back() 函数可加载历史列表中的前一个 URL(如果存在)

forward() 函数可加载历史列表中的下一个 URL

length 属性声明了浏览器历史列表中的元素数量

五、location****对象

location对象用于获取或设置窗体的URL并可以用于解析URL。它既是window对象的属性又是document对象的属性,包含8个属性,其中7个都是当前窗体的URL的一部分,剩下的也是最重要的一个是href属性,代表当前窗体的URL。8个属性都是可读写的,但是只有href与hash的写才有意义。

assign() 函数加载一个新的文档

replace() 函数可用一个新文档取代当前文档

reload() 函数用于刷新当前文档

hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)

host 属性是一个可读可写的字符串,可设置或返回当前 URL 的主机名称和端口号

hostname 属性是一个可读可写的字符串,可设置或返回当前 URL 的主机名

href 属性是一个可读可写的字符串,可设置或返回当前显示的文档的完整 URL

pathname 属性是一个可读可写的字符串,可设置或返回当前 URL 的路径部分

port 属性是一个可读可写的字符串,可设置或返回当前 URL 的端口部分

protocol 属性是一个可读可写的字符串,可设置或返回当前 URL 的协议

search 属性是一个可读可写的字符串,可设置或返回当前 URL 的查询部分(问号 ? 之后的部分)

navigator对象

navigator对象通常用于检测浏览器与操作系统的版本,是window对象的属性。由于navigator没有统一的标准,因此各个浏览器都有自己不同的navigator版本。常用的navigator函数和属性有:

taintEnabled() 函数可返回一个布尔值,该值声明了当前浏览器是否启用了 data tainting

appCodeName 属性是一个只读字符串,声明了浏览器的代码名

appName 属性可返回浏览器的名称

appVersion 属性可返回浏览器的平台和版本信息。该属性是一个只读的字符串

cookieEnabled 属性可返回一个布尔值,如果浏览器启用了 cookie,该属性值为 true。如果禁用了 cookie,则值为 false

platform 属性是一个只读的字符串,声明了运行浏览器的操作系统和(或)硬件平台

userAgent 属性是一个只读的字符串,声明了浏览器用于 HTTP 请求的用户代理头的值

javaEnabled() 函数可返回一个布尔值,该值指示浏览器是否支持并启用了 Java。如果是,则返回 true,否则返回 false

screen对象

screen对象用于获取用户的屏幕信息,是window对象的属性。

width 属性声明了显示浏览器的屏幕的宽度,以像素计

height 属性声明了显示浏览器的屏幕的高度,以像素计

availWidth 属性声明了显示浏览器的屏幕的可用宽度,以像素计。在 Windows 这样的操作系统中,这个可用高度不包括分配给半永久特性(如屏幕底部的任务栏)的垂直空间

availHeight 属性声明了显示浏览器的屏幕的可用高度,以像素计。在 Windows 这样的操作系统中,这个可用高度不包括分配给半永久特性(如屏幕底部的任务栏)的垂直空间

colorDepth 属性返回目标设备或缓冲器上的调色板的比特深度

pixelDepth 属性返回显示屏幕的颜色分辨率(比特每像素)

JavaScript中的回调函数

在JavaScript中我们常听说回调函数,回调函数是一段可执行的代码段,它作为一个参数传递给其他的代码,其作用是在需要的时候方便调用这段(回调函数)代码。通俗来说,回调函数就是就是一个可执行的函数,在需要的地方调用它。
我们知道,对象可以作为参数传递给函数,而函数实际上也是对象,那么函数也可以作为参数传递给函数,在实际使用中,我们可以很容易找到回调函数的例子,例如jQuery中

$('div').click(function(){     
  console.log('hello')   
})

其实click中的function就是个匿名的回调函数,当然,回调函数也可以是具名的

$('div').click(callback)
function callback(){
  console.log('hello')
}

这个回调函数不会立即执行,在传参的过程中,此时传入了回调函数的指针,它并不会立即执行,而是需要等到被调用的时候才会执行。

关于回调函数的this

在回调函数调用时this的执行上下文并不是回调函数定义时的那个上下文,而是调用它的函数所在的上下文。
例如

var obj = {
	sum: 0,
	add: function(num1, num2){
		this.sum = num1 + num2;
	}
};

function add(num1, num2, callback){
	callback(num1, num2);
};

add(1,2, obj.add);
console.log(obj.sum);			//=>0
console.log(window.sum);		//=>3

上述代码调用回调函数的时候是在全局环境下,因此this指向的是window,所以sum的值是赋值给windows的。

允许多重回调函数

一个典型的例子就是jQuery中ajax的使用

function successCallback(){
    //在发送之前做点什么
}     

function successCallback(){
  //在信息被成功接收之后做点什么
}

function completeCallback(){
  //在完成之后做点什么
}

function errorCallback(){
    //当错误发生时做点什么
}

$.ajax({
    url:"http://xxx.com/",
    success:successCallback,
    complete:completeCallback,
    error:errorCallback

})

通过回调函数获取数据

在进行跨域的时候,我们需要获取对应接口的数据并对数据进行相应的处理,那么就可以通过接口所提供的回调函数通过传参来获取数据。 例如JSONP跨域时,提供的接口为http://platform.sina.com.cn/slide/album_tech?jsoncallback=func&app_key=1271687855&num=3&page=4

<script>
  function func(data){
    console.log(data)   //  获取数据
  }
</script>
<script src="http://platform.sina.com.cn/slide/album_tech?jsoncallback=func&app_key=1271687855&num=3&page=4"></script>

一个回调函数中可以嵌入另一个回调函数,对于这种情况出现多层嵌套时,代码会难以阅读和维护,这个时候可以采用命名回调函数的方式调用,或者采用模块化管理函数。

异步编程

我们知道,JavaScript的执行模式分为两种,异步(Asynchronous)和同步(Synchronous),同步模式指的是代码按照顺序自上而下执行,生活中排队就是同步的一个例子,异步模式指的是代码顺序与执行顺序不保证一致,就好比正好好排队的时候,突然有一个人插队了(不提倡不提倡),这就是异步,但是在浏览器端,异步模式是很重要的,耗时很长的操作都应该异步执行,避免浏览器失去响应,最好的例子就是Ajax操作。在服务器端,”异步模式”甚至是唯一的模式,因为执行环境是单线程的,如果允许同步执行所有http请求,服务器性能会急剧下降,很快就会失去响应。而回调函数就是实现异步编程的一个方式。

定时器就是异步实现的一个例子。

var a = 1
setTimeout(function () {
  console.log(a)   // a打印出来是多少呢? 
}, 1000)

我们看到1s之后传入一个匿名的回调函数,并打印出a的值,你也许会说,a是1呀!可是你怎么知道后续的a会不会被修改呢?假如a的值在1000行的代码处被修改为233或者其他的值,此时a的值就不确定了,不管是setTimeout,还是setInterval,当我们没有完整的看完代码——也就是代码在之后执行的时候没有确定a的值的时候,a在1s后的值在目前的代码片段是无法确定的,异步编程的特点在于当前可执行的代码不阻塞后面代码的执行,而回调函数是实现异步编程的基本方法。

除了定时器,还有事件监听、http请求、promise对象中使用回调函数都可以实现异步编程。

参考

理解与使用Javascript中的回调函数
Javascript异步编程的4种方法

本篇博客摘抄地址

vim操作手册

Alias:是一种函数,功能是设置命令的别名
vim 文件名 回车 :进入出境vim编辑此文件
set mouse=:切换鼠标操作
ese : q :退出
esc : wq:保存并退出
esc : q!:不保存并退出
x:删除光标所在位置之后的一个字母
i :在光标前插入文本
A:在一行后添加文本
a:在光标后插入文本
$:代表行末
de:从当前光标当前位置直到单词末尾,包括最后一个字符。
d$:删除该光标处后一段内容
dw:从当前光标当前位置直到下一个单词起始处,不包括它的第一个字符。
在光标处输入 2w 使光标向前移动两个单词。
在光标处输入 3e 使光标向前移动到第三个单词的末尾。
在光标处输入 0 (数字零) 移动光标到行首。
d(3)w:删除相临3 个大写字母单词(输入数字几删除几个单词)
(2)dd :删除2行内容(输入数字几删除几行内容)
u:恢复让一步操作(撤销最后一次执行的命令)
U:恢复到该行的原始状态(撤销这一行中所做的改动)
ctrl+r:撤销以前的撤销命令,恢复以前的操作结果
p:粘贴前一步删除的内容到光标之后。如删除为一整行则粘贴在光标的下一行
r:替换光标所在位置的字符
ce:以替换当前光标到单词的末尾的内容
c$:替换当前光标到行末的内容。
更改命令格式为:

  • c (任何数字)任何动作参数
    例:c2$(将更改两行内容)

修改命令的格式为:

  • d (任何数字)任何动作参数
    例:d3$(删三行内容)

ctrl+G: 显示当前编辑文件中当前光标所在行位置以及文件状态信息。
G:使当前光标跳转到文件最后一行。
gg:使当前光标跳转到文件第一行。
输入行号+G:跳转到输入的行号
ctrl+o:退回上一步
/(需要查找的字符):查找该字符
n:查找同上一次的字符
N:相反方向查找同上一次的字符
?(需要查找的字符):逆向查找该字符

注:如果查找已经到达文件末尾,查找会自动从文件头部继续查找

%:查找配对的括号
:#,#s/old/new/g(:行号,行号s/需要改的字符/改后的字符/g):替换多少行的匹配字符串
:%s/old/new/g:替换整个文件中的每个匹配字符串
:%s/old/new/gc:整个文件中的每个匹配串,并且对每个匹配串提示是否进行替换。
:s/old/new:在一行内替换头一个字符串

在VIM中执行外部命令的方法:

:! 然后紧接着输入一个外部命令可以执行该外部命令。
例::!ls 查看当前目录
:!rm 删除

提示:所有的 : 命令都必须以敲 <回车> 键结束。

w文件名:将对文件的改动保存到文件中
v+移动光标:可选取内容(可对选取内容进行操作作。比如d删除)
v+移动光标+:w 文件名 : 可保存选择内容到文件中
r 文件名:交创建的文件提取进来
:r !ls :当前所有目录提取到光标下

o:光标下方插入新的一行并进入插入模式
O:光标上方插入新的一行并进入插入模式
R:可连续替换多个字符

提示:替换模式与插入模式相似,不过每个输入的字符都会删除一个已有的字符。

y:复制文本
p:粘贴文本
:set xxx 可以设置 xxx 选项
ic (Ignore Case,忽略大小写)
:nohlsearch:移除匹配项的高亮显示
:set noic:禁用忽略大小写
:set ic:查找时忽略大小写
:set is:查找短语时显示部分匹配
:set hls:高亮显示所有的匹配短语
在选项前加上 no 可以关闭选项
例: :set noic 关闭忽略大小写

获取帮助信息

启动帮助系统:

  • 按下 键 (如果键盘上有的话)
  • 按下 键 (如果键盘上有的话)
  • 输入 :help <回车>

输入 CTRL-W CTRL-W 可以使您在窗口之间跳转。
输入 :q <回车> 可以关闭帮助窗口。

CTRL-D :查看可能的补全结果

:进行命令行补全

什么是rc:

rc:run commands(运行命令)
添加alise 都要source ~/.bashrc
source:资源

响应式

rem是什么?

rem(font size of the root element)是指相对于根元素的字体大小的单位。简单的说它就是一个相对单位。看到rem大家一定会想起em单位,em(font size of the element)是指相对于父元素的字体大小的单位。它们之间其实很相似,只不过一个计算的规则是依赖根元素一个是依赖父元素计算。

动态 REM 方案(移动端方案)

  1. meta:vp 阻止缩放
  2. 尽量不要写 width / height,改用 max / min
  3. flex 布局
  4. media query
  5. 实际页面的宽度/10 == ? == html font size
  6. 10rem === 实际页面的宽度

例如:

document.write(`
 <style>
   html{font-size:${document.documentElement.clientWidth/10}px;}
 </style>
`)

添加上面代码后就可以在css中使用rem单位了,注意因为/10所以换算比成10.

  1. 浏览器禁止 980 像素的缩放
  2. 设置 html {font-size: 页面宽度 / 10 px}
  3. 10 rem == 页面宽度
  4. 所有单位都用 rem == 所有长度都以页面宽度为基准
  5. 页面可以兼容任何手机屏幕

解决什么问题?

如何做?

让 html font size = 屏幕宽度 / 10

1px 问题

1 在普通屏幕
CSS 1px == 设备的 1px
2 在 Retina
CSS 1px == 设备的 2px
border-width == 设备的 1px
border-width: 0.5px == 设备的 1px (兼容性有问题)

页面整体缩放 50% <meta ... initial-scale=0.5border-width: 1px == 设备的 1px

副作用所有 div 都变为原来的 50%

所有长度都以 rem 为基准

rem 变为原来的 2 倍

  1. 获取设备像素比(1/2/3)
  2. initial scale = 1/像素比
  3. 让 rem 变为 rem * 像素比
  4. border-top: 1px solid red;
var dpr, rem, scale;
var docEl = document.documentElement;
var fontEl = document.createElement('style');
var metaEl = document.querySelector('meta[name="viewport"]');

dpr = window.devicePixelRatio || 1;    //获取设备像素比
rem = docEl.clientWidth * dpr / 10;    
scale = 1 / dpr;


// 设置viewport,进行缩放,达到高清效果
metaEl.setAttribute('content', 'width=' + dpr * docEl.clientWidth + ',initial-scale=' + scale + ',maximum-scale=' + scale + ', minimum-scale=' + scale + ',user-scalable=no');

// 设置data-dpr属性,留作的css hack之用
docEl.setAttribute('data-dpr', dpr);

// 动态写入样式
docEl.firstElementChild.appendChild(fontEl);
fontEl.innerHTML = 'html{font-size:' + rem + 'px!important;}';

// 给js调用的,某一dpr下rem和px之间的转换函数
window.rem2px = function(v) {
    v = parseFloat(v);
    return v * rem;
};
window.px2rem = function(v) {
    v = parseFloat(v);
    return v / rem;
};

window.dpr = dpr;
window.rem = rem;

阿里rem介绍
腾讯rem介绍

3D Hover 实例

3D Hover

persperctive

persperctive是CSS样式中调整视距,它需要在父容器使用如persperctive: 1000px;,子元素中才能配合transform:translateZ(10px);

BoundingClientRect()

语法:rectObject = object.getBoundingClientRect();

BoundingClientRect()是用来获取元素边界的位置信息。例如:

let position = divmy.getBoundingCilentRect(); 控制台会返回一个例如:

[object ClientRect] {
  bottom: 194, //从窗口到元素底部距离
  height: 194, //元素内容的高度
  left: 0,     //从窗口到元素左部距离
  right: 242,  //从窗口到元素右部距离
  top: 0,      //从窗口到元素顶部距离
  width: 242   //元素内容的宽度
}

client

client使用来获取元素在当前视口的距离(并不是元素在页面的距离)。

page

page使用来获取元素在当前页面的距离

3D Hover 例子

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
<body>
<div class="parent">
  <div class="child">banner</div>
</div>
</body>
  <style>
    .parent {
  width: 400px;
  border: 1px solid;
  padding: 80px;
  perspective: 1000px;                                        //添加视距距离
  margin: 100px auto;
}
.child {
  background:#2aa;
  display:flex;
  align-items:center;
  justify-content:center;
  font-size:50px;
  color:white;
  height: 300px;
  border: 1px solid #2aa;
  transition: transform 0.1s;                                          //过度动画时间(添加该效果会很顺滑)
  transform-style: preserve-3d;                                     //增加3D效果
}
.child::after{
  content:'banner';
  transform:translateZ(100px);
  text-shadow:5px 5px 15px #000;
  opacity:0.8;                                                         //透明度
  
}
  </style>
  <script>
    var divparent = document.querySelector('.parent');    //通过选择器获取parent元素
var divchild = document.querySelector('.child');          //通过选择器获取child元素
divparent.addEventListener('mousemove', function(e){      //添加对divparent元素进行事件监听(鼠标移动)
  //console.log(e.clientX,e.clientY);                     //client可以获取鼠标位置信息
  var parentPosition = divparent.getBoundingClientRect(); //获取divparent在屏幕中的位置信息
  var parentX = parentPosition.left;                      //获取divparent元素左边部的位置信息
  var parentY = parentPosition.top;                       //获取divparent元素顶边部的位置信息
  var deltaX = e.clientX - parentX;                       //使X轴的位置在divparent左上角位置
  var deltaY = e.clientY - parentY;                       //使y轴的位置在divparent左上角位置
  var centerX = deltaX - parentPosition.width/2;          //使X轴的位置在divparent中间位置
  var centerY = deltaY - parentPosition.height/2;         //使y轴的位置在divparent中间位置
  var degreeY = centerX / 100 * 5;                        //将移动的位置像素位置距离转换成角度
  var degreeX = -centerY / 100 * 5;                       //将移动的位置像素位置距离转换成角度(由于X轴的角度变换是根据Y轴决定并且Y轴负值是Y轴顺时针旋转所以前边需要添加负号)
  divchild.style.transform='rotateX(' + degreeX + 'deg) rotateY(' + degreeY + 'deg)' //将获取的鼠标位置信息添加到CSS中的divchild元素中
});
  </script>
</html>

CSS transform 属性允许你修改CSS视觉格式模型的坐标空间。
使用它,元素可以被转换(translate)、旋转(rotate)、缩放(scale)、倾斜(skew)。
transition:transform 1s;过度动画1秒

代码测试地址

JavaScript 数据类型

JavaScript 是一种弱类型或者说动态语言。这意味着你不用提前声明变量的类型,在程序运行过程中,类型会被自动确定。

数据类型

最新的 ECMAScript 标准定义了7种数据类型:

  • 6种基本类型:
    • 数值(number)
    • 字符串(string)
    • 布尔值(boolean)
    • undefined
    • null
    • Symbol
  • 和对象(object)

typeof运算符

typeof运算符可以返回一个值的数据类型,返回一个字符串。

数值

根据 ECMAScript 标准,JavaScript 中只有一种数值类型:基于 IEEE 754 标准的双精度 64 位二进制格式的值(-( $2^{63}$ -1) 到 $2^{63}$ -1)。它并没有为整数给出一种特定的类型。除了能够表示浮点数外,还有一些带符号的值:+Infinity,-Infinity 和 NaN (非数值,Not-a-Number)。

在JavaScript语言的底层,根本没有整数,所有数字都是小数(64位浮点数)。

1 === 1.0 // true

由于浮点数不是精确的值,所以涉及小数的比较和运算要特别小心。

0.1 + 0.2 === 0.3
// false
0.1 + 0.2
// 0.30000000000000004

数值的进制

JavaScript对整数提供四种进制的表示方法:十进制、十六进制、八进制、2进制。

十进制:没有前导0的数值
八进制:有前缀0o或0O的数值,或者有前导0、且只用到0-7的七个阿拉伯数字的数值。
十六进制:有前缀0x或0X的数值。
二进制:有前缀0b或0B的数值。

NaN

NaN是JavaScript的特殊值,表示“非数字”(Not a Number),主要出现在将字符串解析成数字出错的场合。

typeof NaN // 'number'

运算规则

NaN不等于任何值,包括它本身。

NaN === NaN  // false

字符串

字符串就是零个或多个排在一起的字符,放在单引号或双引号之中。

'abc'
"abc"

如果长字符串必须分成多行,可以在每一行的尾部使用反斜杠。

var longString = "Long \
long \
long \
string";

longString
// "Long long long string"

转义

反斜杠(\)在字符串内有特殊含义,用来表示一些特殊字符,所以又称为转义符。

\0 null(\u0000)
\b 后退键(\u0008)
\f 换页符(\u000C)
\n 换行符(\u000A)
\r 回车键(\u000D)
\t 制表符(\u0009)
\v 垂直制表符(\u000B)
\’ 单引号(\u0027)
\” 双引号(\u0022)
\ 反斜杠(\u005C)

反斜杠还有三种特殊用法。

(1)\HHH

反斜杠后面紧跟三个八进制数(000到377),代表一个字符。HHH对应该字符的Unicode码点,比如\251表示版权符号。显然,这种方法只能输出256种字符。

(2)\xHH

\x后面紧跟两个十六进制数(00到FF),代表一个字符。HH对应该字符的Unicode码点,比如\xA9表示版权符号。这种方法也只能输出256种字符。

(3)\uXXXX

\u后面紧跟四个十六进制数(0000到FFFF),代表一个字符。HHHH对应该字符的Unicode码点,比如\u00A9表示版权符号。

Base64转码

Base64是一种编码方法,可以将任意字符转成可打印字符。

JavaScript原生提供两个Base64相关方法。

btoa():字符串或二进制值转为Base64编码
atob():Base64编码转为原来的编码

var string = 'Hello World!';
btoa(string) // "SGVsbG8gV29ybGQh"
atob('SGVsbG8gV29ybGQh') // "Hello World!"

要将非ASCII码字符转为Base64编码,必须中间插入一个转码环节,再使用这两个方法。

function b64Encode(str) {
  return btoa(encodeURIComponent(str));
}

function b64Decode(str) {
  return decodeURIComponent(atob(str));
}

b64Encode('你好') // "JUU0JUJEJUEwJUU1JUE1JUJE"
b64Decode('JUU0JUJEJUEwJUU1JUE1JUJE') // "你好"

布尔值

布尔值只有两个值:true和false。

除了下面六个值被转为false,其他值都视为true。

undefined
null
false
0
NaN
“”或’’(空字符串)

null和undefined

null与undefined都可以表示“没有”,含义非常相似。将一个变量赋值为undefined或null,老实说,语法效果几乎没区别。

null的特殊之处在于,JavaScript把它包含在对象类型(object)之中。

typeof null // "object"

注意,JavaScript的标识名区分大小写,所以undefined和null不同于Undefined和Null(或者其他仅仅大小写不同的词形),后者只是普通的变量名。

null表示空值,即该处的值现在为空。调用函数时,某个参数未设置任何值,这时就可以传入null。比如,某个函数接受引擎抛出的错误作为参数,如果运行过程中未出错,那么这个参数就会传入null,表示未发生错误。

undefined表示“未定义”。

Symbol

Symbol 是JavaScript的原始数据类型,Symbol实例是唯一且不可改变的.

let s = Symbol();

typeof s
// "symbol"

Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述。注意,Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。

// 没有参数的情况
var s1 = Symbol();
var s2 = Symbol();

s1 === s2 // false

// 有参数的情况
var s1 = Symbol('foo');
var s2 = Symbol('foo');

s1 === s2 // false

对象

对象(object)是JavaScript的核心概念,也是最重要的数据类型。JavaScript的所有数据都可以被视为对象。
所谓对象,就是一种无序的数据集合,由若干个“键值对”(key-value)构成。

var o = {
  p: 'Hello World'
};

p是“键名”,字符串Hello World是“键值”,键名与键值之间用冒号分隔。

对象的所有键名都是字符串。对象的每一个“键名”又称为“属性”(property),它的“键值”可以是任何数据类型。

检查变量是否声明

if ('a' in window) {
  // 变量 a 声明过
} else {
  // 变量 a 未声明
}

查看所有属性

查看一个对象本身的所有属性,可以使用Object.keys方法。

var o = {
  key1: 1,
  key2: 2
};

Object.keys(o);
// ['key1', 'key2']

本篇博客是转载:

@media与@media screen的区别(转载)

在网站自适应设计中,@media@media screen是必须用到的css代码,可能大家对此并不陌生。但是大家又知不知道@media@media screen的区别在哪呢?在自适应设计中你是用@media还是用@media screen?你知不知道两者产生的结果有什么不同呢?这些问题我想很多人都不清楚吧。本文便通过实例给你说说@media@media screen的区别,主要是告诉你它们产生的效果区别在哪。

@media@media screen手机网页区别

好吧,理论方面暂时放在一边,先看看两者产生的手机网页视图有什么不同。

@media与@media  screen手机网页

@media@media screen手机网页

上图看出,没有任何区别。没错,确实没有任何区别,因为@media@media screen的css在手机设备里都是有效的。

@media@media screen打印网页区别

两者在手机设备上没有区别,在打印设备上是怎样的呢?看看如下截图。

@media打印网页

@media打印网页

@media  screen打印网页

@media screen打印网页

很明显,两者的打印效果是不同的。

至此,我们应该明白了@media@media screen的区别了吧,没错,@media screen的css在打印设备里是无效的,而@media在打印设备里是有效的,这就是它们的区别了。

知道了它们的区别,我们就应该懂得如何使用它们了。如果css需要用在打印设备里,那么就用@media ,否则,就用@media screen。

不过这只是笼统的做法,其实如果把“screen”换为“print”,写为**@media print**,那么该css就可用到打印设备上了,但要注意,@media print声明的css只能在打印设备上有效哦。

通过此实例的见解,我们可以举一反三,对@media的用法就能更易接受了。下面的知识扩展可以让你更深入地了解@media的各种用法。

知识扩展

@media only screen and

only(限定某种设备)
screen 是媒体类型里的一种
and 被称为关键字,其他关键字还包括 not(排除某种设备)

*/* 常用类型 /
类型 解释
all 所有设备
braille 盲文
embossed 盲文打印
handheld 手持设备
print 文档打印或打印预览模式
projection 项目演示,比如幻灯
screen 彩色电脑屏幕
speech 演讲
tty 固定字母间距的网格的媒体,比如电传打字机
tv 电视

screen一般用的比较多,下面是我自己的尝试,列出常用的设备的尺寸,然后给页面分了几个尺寸的版本。

**/* 常用设备 */**设备 屏幕尺寸
显示器 1280 x 800
ipad 1024 x 768
Android 800 x 480
iPhone 640 x 960

两种方式

a:

意思是当屏幕的宽度大于600小于800时,应用styleB.css

b:

  • @media screen and (max-width: 600px) { /当屏幕尺寸小于600px时,应用下面的CSS样式/
  • .class {
  • ​ background: #ccc;
  • }
  • }

device-aspect-ratio

device-aspect-ratio可以用来适配特定屏幕长宽比的设备,这也是一个很有用的属性,比如,我们的页面想要对长宽比为4:3的普通屏幕定义一种样式,然后对于16:9和16:10的宽屏,定义另一种样式,比如自适应宽度和固定宽度:

  • @media only screen and (device-aspect-ratio:4/3)

转载地址: http://www.webkaka.com/tutorial/zhanzhang/2015/090643/

jQuery的初步认识(做tab组件)

首先用原生JS做tab组件

代码地址

主要知识点如下

let tabitems = document.querySelectorAll('.tabs>li') //获取css中.tabs>li
tabitems[0].addEventListener('click', function(e) {  //对选项卡1进行click监视
  // e.currentTarget === tabitems[0]  //获取当前监听的元素,也就是tabitems[0]
  /*e.currentTarget.classList.add('active') //在tabitems里添加一个className='active' 这个方法不会覆盖掉原有html里的className而是在添加一个className ='active' (推荐这个方法)*/
  /*e.currentTarget.className = "active" //这个方法等同于上一个,区别在于会覆盖掉html里已经添加的className*/
  // active 自己
  let li = e.currentTarget                            //获取用户点击选项卡1
  li.classList.add('active')                          
  // 去掉弟弟的 active
  let next = li.nextSibling
  while (next.nodeType === 3) {
    next = next.nextSibling
  }
  next.classList.remove('active')
  // 将内容显示选项卡1
  let panesLi = li.parentNode.parentNode.children[1].children[0] //获取到panes的第一个子元素
  panesLi.classList.add('active') //给这个子元素添加className='active'
  li.parentNode.parentNode.children[1].children[1].classList.remove('active')//删除panes的第二个子元素中的className = 'active'
})
// 
tabitems[1].addEventListener('click', function(e) { //对选项卡2进行click监视
  // active 自己
  let li = e.currentTarget                          //获取用户点击选项卡2
  li.classList.add('active')                        //给选项卡2添加className='active'
  // 去掉哥哥的 active
  let prev = li.previousSibling                     //获取选项卡2的哥哥元素
  while (prev.nodeType === 3) {                     //判断是不是文字元素
    prev = prev.previousSibling
  }
  prev.classList.remove('active')
  li.parentNode.parentNode.children[1].children[0].classList.remove('active')
  li.parentNode.parentNode.children[1].children[1].classList.add('active')
})

上面是运用的DOM方法

jQuery是什么

jQuery是一个快速、简洁的JavaScript框架,jQuery的核心特性可以总结为:具有独特的链式语法和短小清晰的多功能接口;具有高效灵活的css选择器,并且可对CSS选择器进行扩展;拥有便捷的插件扩展机制和丰富的插件。jQuery兼容各种主流浏览器,如IE 6.0+、FF 1.5+、Safari 2.0+、Opera 9.0+等。

jQuery为什么会流行?因为DOM反人类

如何使用jQuery

  1. 在html中添加<script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>既可以调用。

  2. 在调用jQuery后我们console.log(jQuery)控制台会返回function (a,b){return new n.fn.init(a,b)}说明jQuery是一个对象函数。

  3. 在调用jQuery后还提供了一个全局变量*$*, window.$ = window.jQuery

  4. jQuery是如何简单高效的对元素进行事件委托

    jQuery('.tabs').on('click','li',function(e){
      let li =e.currentTarget
    })
    

    下边是DOM对.tabs进行监听

    let tabitems=document.querySelectorAll('.tabs>li')
    tabitems[number].addEventListener('click',function(e){
      let li =e.currentTarget
    })
    

    对比jQuery与DOM,确实DOM很繁琐

    jQuery的源代码就是用DOM编写的

    百度静态jQuery地址

使用jQuery做tab组件

代码地址

jQuery主要代码如下:

jQuery('.tabs').on('click','li',function(e){
  let li =e.currentTarget
  let Index=jQuery(li).index()
  jQuery(li).addClass('active')
  jQuery(li).siblings('.active').removeClass('active')
  let liIndex=jQuery(li).parent().next().children().eq(Index)
  liIndex.addClass('active')
  liIndex.siblings('.active').removeClass('active')
})

target和currentTarget的区别
比如说现在有A和B, A.addChild(B) A监听鼠标点击事件 那么当点击B时,target是B,currentTarget是A 也就是说,currentTarget始终是监听事件者,而target是事件的真正发出者

不禁感叹jQuery是多么的简洁高效,它的出现拯救了前端程序员

练习DEMO

tab组件
仿制苹果轮播
js源生代码tab组件

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.