ol自定义序号样式的方法

ol为有序列表元素,它的默认样式在前面会自动添加序号如1.、2.、3.等,本文介绍如何通过css及html5新增属性改变需要的样式以及编号规则等。

一、编号倒序(HTML5新增)
reversed属性可以指定编号为倒序:

  1. 第一项
  2. 第二项
  3. 第三项
  4. 第四项

二、指定编号起始值(HTML5新增)
start属性可以指定编号的初始值:

  1. 第一项
  2. 第二项
  3. 第三项
  4. 第四项

三、指定编号位置(内、外)
css属性list-style-position:inside;可以指定编号的位置,下面的列表的编号为inside(默认为outside),可以与上面两个例子对比一下,对齐方式有所不同。

  1. 第一项
  2. 第二项
  3. 第三项
  4. 第四项

四、自定义编号的值

<style>
ol.example{counter-reset: num;}
ol.example li{counter-increment: num; list-style:none;}
ol.example li::before{content: '第' counter(num) '条:';}
</style>

五、自定义编号增量

<style>
ol.example{counter-reset: num;}
ol.example li{counter-increment: num 2; list-style:none;}
ol.example li::before{content: '第' counter(num) '条:';}
</style>

六、自定义编号初始值
下面的例子中example的第一个li为12,后面依次是14、16、18,counter-reset和counter-increment两个属性的第二个值也可以为负值

counter-reset属性第二个值的默认值为0,表示初始值为0;counter-increment属性第二个值的默认值为1,表示增量为1。

<style>
ol.example{counter-reset: num 10;}
ol.example li{counter-increment: num 2; list-style:none;}
ol.example li::before{content: '第' counter(num) '条:';}
</style>

Input Text键盘类事件触发结果总结

Input的键盘类事件(onkeydown、onkeyup等)触发的规则在不同浏览器下并不一致,本文总结了在IE9~IE11以及Chrome和Firefox下这些事件触发的规则。

本文Demo:https://www.lyz810.com/demo/inputKeyEvent/
下载对比表格:https://www.lyz810.com/demo/inputKeyEvent/inputEventCompare.xlsx

说明:本文中只考虑IE9~IE11以及Chrome和Firefox浏览器,文中的其他浏览器均指代上述范围内的浏览器。

一、oninput和onpropertychange

oninput是HTML5中针对input、textarea及select新增的事件,在IE9以上以及其他浏览器中可以使用。
老板本的IE(IE10及更早版本)中,支持使用onpropertychange事件来实时监听用户输入。
注意:onpropertychange事件会在input的任意一个属性变化时回调,所以使用时应判断哪个属性发生了变化。

二、IE中oninput的坑
在IE10以上的版本中(支持placeholder的IE),如果设置了placeholder,那么在input获取焦点时,如果原始值为空,则会触发oninput事件,因为IE的placeholder会在获取焦点时清空,而Chrome和Firefox不会(在输入第一个字符后,placeholder内容才会消失)。
同理,删除最后一个字符也会触发oninput(但这个是正常情况,毕竟input的值已经发生了改变)。

三、键盘事件触发顺序表

onkeydown onkeypress oninput onkeyup onchange onblur onpropertychange
IE9 1 2 3 5 6 7 4
IE10 1 2 4 5 6 7 3
IE11 1 2 3 4 5 6
Chrome 1 2 3 4 5 6
Firefox 1 2 3 4 5 6

总体看来,基本上都是down→press→input→up,但IE9和IE10的onpropertychange触发顺序是相反的,IE9会先触发oninput然后才是onpropertychange,IE10则是先触发onpropertychange,然后再触发oninput。

四、特殊按键触发表

onkeydown onkeypress onkeyup oninput onpropertychange
IE9 ESC、SHIFT、CTRL、ALT、DELETE、BACKSPACE、ENTER ESC、ENTER ESC、SHIFT、CTRL、ALT、DELETE、BACKSPACE、ENTER
IE10 ESC、SHIFT、CTRL、ALT、DELETE、BACKSPACE、ENTER ESC、ENTER ESC、SHIFT、CTRL、ALT、DELETE、BACKSPACE、ENTER DELETE、BACKSPACE DELETE、BACKSPACE
IE11 ESC、SHIFT、CTRL、ALT、DELETE、BACKSPACE、ENTER ESC、ENTER ESC、SHIFT、CTRL、ALT、DELETE、BACKSPACE、ENTER DELETE、BACKSPACE
Chrome ESC、SHIFT、CTRL、ALT、DELETE、BACKSPACE、ENTER ENTER ESC、SHIFT、CTRL、ALT、DELETE、BACKSPACE、ENTER DELETE、BACKSPACE
Firefox ESC、SHIFT、CTRL、ALT、DELETE、BACKSPACE、ENTER ESC、ENTER、DELETE、BACKSPACE ESC、SHIFT、CTRL、ALT、DELETE、BACKSPACE、ENTER DELETE、BACKSPACE

测试了ESC、SHIFT、CTRL、ALT、DELETE、BACKSPACE、ENTER按键,它们都可以触发onkeydown和onkeyup事件。
除IE9外,其他浏览器在两种删除按键下都可以触发oninput事件,IE10还可以触发onpropertychange事件(IE9都不能触发,要想监控到删除字符需要使用keydown)。
而onpress事件触发规则更是五花八门,IE下只有ESC和ENTER可以触发,Chrome只有ENTER能够触发,Firefox在ESC、ENTER、DELETE、BACKSPACE下都可以触发onpress事件。

五、剪切粘贴触发规则表

oninput onpropertychange
IE9 粘贴 粘贴
IE10
IE11
Chrome
Firefox

这里IE9仍保持着它的怪异风格,只有粘贴时会触发oninput和onpropertychange事件,剪切时并不会。
IE10和IE11以及其他浏览器剪切粘贴都可以触发oninput事件,而IE10还会触发onpropertychange事件。

六、总结
IE9认为输入框中的内容减少不触发oninput和onpropertychange事件,如删除和剪切,只有增加内容才会触发。
其他浏览器会在文本框内容改变时触发oninput事件。
IE的placeholder会导致初始文本框无内容时,获取焦点即触发oninput事件。
oninput和onpropertychange(IE专有)事件会同时触发,只是不同浏览器中触发顺序不同。
浏览器都是按照down→press→input→up的顺序触发,某些按键不触发其中的几个事件。
在press事件之前,按下的字符不会显示在文本框内,所以要禁止输入一些字符可以在onkeydown或onkeypress事件中阻止默认行为(建议在keydown中,因为keypress的触发规则不同,需要做一些兼容性处理)。
印屏幕键(print screen)只触发keyup不触发keydown。
Fn键不触发任何事件
onchange事件都是在onblur前触发(只有最终内容与focus是不同才会触发)。

页面可见状态API使用方法

Page Visibility API用于获取当前页面的可见性状态,在音频、视频播放、游戏等场景中十分有用。

本文Demo请见:https://www.lyz810.com/demo/pageVisibility/
一、应用场景

页面可见性状态一般可以用于以下场景:

  1. 音频或视频(尤其是视频)播放时,当页面不可见自动暂停播放,页面恢复后再继续播放
  2. 一些定时执行的操作(如微博获取最新的微博条数,该动作每分钟获取一次),没有必要在用户不可见的时候去获取数据
  3. 网页游戏在用户切换页面后,游戏自动暂停,用户体验更好
  4. 统计用户停留时长,这种方式比目前常用的两次页面打开时间之差得到用户停留时长更准确

二、document.visibilityState属性值

document.visibilityState有四个值(后两个是可选属性,浏览器可以不实现):

  1. hidden
  2. visible
  3. prerender
  4. unloaded

以下情况,document.visibilityState的值为hidden:

  • 浏览器最小化
  • 浏览器没有最小化,但是页面是非活动的标签页(即切换到其他标签),浏览器被其他窗口覆盖值不会受影响
  • 操作系统锁屏

在页面可见时,值为visible

在页面的父级页面不可见时,值可以为prerender(该属性为可选的)

在文档卸载时,值应为unloaded(该属性为可选的)

三、document.hidden属性

document.hidden为布尔型,当页面不可见(hidden)时为true,可见(visible)时为false

四、visibilitychange事件

document中的visibilitychange用于监听可见状态改变的事件,用法如下:

document.addEventListener('visibilitychange', function(){
  console.log(document.visibilityState);
}, false);

五、兼容性

IE:10+
FireFox:18+(10~17需要加moz前缀)
Chrome:33+(14~32需要加webkit前缀)
Safari:6.1+
Opera:12.1以及20+(15~19需要加webkit前缀,12.1不需要加前缀)

JavaScript Blob对象详解

Blob对象为大二进制对象,一个Blob对象就是一个包含有只读原始数据的类文件对象。

一、构造函数

Blob构造函数接受两个可选参数,第一个参数为一个数组,数组的元素可以是任意多个的ArrayBuffer,ArrayBufferView (typed array), Blob,或者 DOMString对象。
第二个参数为一个对象,设置Blob对象的属性

二、方法

slice:返回一个新的Blob对象,它截取了原对象中的一部分内容。
slice方法支持三个可选参数,分别是start、end、contentType
第一个参数start为起始索引,默认为0
第二个参数end为结束索引,默认为最后一个索引
第三个参数contentType为新生成的Blob对象的MIME类型,默认为空字符串
如果start的值大于原始Blob的值,则新生成的Blob对象大小为0,内容是空的。

三、MIME类型

构造函数的第二个参数一般用于设置MIME类型,例如:

  new Blob(['test'], {type:'text/plain'});

上面的例子中Blob对象的类型为text/plain,内容为test
设置MIME类型的主要目的是让浏览器知道该如何显示该二进制资源(例如,是图片则显示图片,是网页则解析HTML,是二进制文件则弹出下载框等)

四、示例
下面的例子可以复制到浏览器的控制台中,并将返回的blob:开头的url复制粘贴到地址栏中,就可以看到效果。
显示文本:

  var blob = new Blob(['纯文本'],{type: 'text/plain'});
  URL.createObjectURL(blob);

显示网页:

 var blob = new Blob(['<html><head><title>我是标题</title></head>', '<body><h1>我是内容</h1></body></html>'],{type: 'text/html'});
 URL.createObjectURL(blob);

显示一个json:

  var blob = new Blob([JSON.stringify({"key":"value"})], {type:'text/json'});
  URL.createObjectURL(blob);

作为二进制文件下载:

  var blob = new Blob(['文件内容'], {type:'application/octet-binary'});
  URL.createObjectURL(blob);

五、兼容性

IE:10+
Firefox:13+
Chrome:20+
Safari:6+
Opera:12.1+

Beacon API介绍

Beacon API可以向指定服务器发送少量的数据以实现统计等目的。

一、应用场景

主要用于记录并发送用户行为、发送错误日志等。
之前记录用户行为通常是通过发送一个GET请求(一般是一个带所有统计信息参数的图片请求),使统计服务器获取到用户行为数据并记录。
这种方式有以下几个弊端:
1.当页面关闭时,发送的统计请求可能不会成功,导致统计数据丢失
2.通常为了解决关闭页面的统计数据丢失问题,会发起一个同步的请求,这样会阻塞页面的卸载
3.GET请求通常有长度限制,可携带的数据量有限,并且一般只能携带文本信息
而使用Beacon API后,上述问题都能够解决:
Beacon是以非阻塞POST的方式发送数据,支持多种数据类型:ArrayBufferView, Blob, DOMString, 或者 FormData 类型的数据
关闭页面不会中断Beacon的发送,也不会阻塞页面的卸载,用户体验更好

二、API用法

navigator.sendBeacon(url, data);
url 参数表明 data 将要被发送到的网络地址。(不存在跨域问题,但https的页面中不能向http协议的统计服务器发送数据,会报安全错误)
data 参数是将要发送的 ArrayBufferView, Blob, DOMString, 或者 FormData 类型的数据。
sendBeacon() 方法返回 true 如果用户代理能够成功地将要发送的数据排入队列,否则返回 false。
示例:

document.getElementById('container').addEventListener('click', function(e){
   if(e.target.tagName === 'A'){
      var formData = new FormData();
      formData.append('type', 'click');
      formData.append('elementType', 'link');
      formData.append('href', e.target.href);
      formData.append('t', Date.now());
      navigator.sendBeacon('http://tongji.example.com', formData);
   }
}, false);

window.addEventListener('unload', function(){
    var data = JSON.stringify({
        type: 'unload',
        t: Date.now()
      });
    navigator.sendBeacon('http://tongji.example.com', data);
}, false);

上面示例中,为container下的所有A标签的点击事件进行了统计,当点击链接时,会发送统计信息到统计服务器tongji.example.com上,并以form表单的形式发送。
在页面卸载时,也会将相关信息以json字符串的方式发送给统计服务器
注意此例为了演示不同的发送方式采用了两种不同的数据类型,实际使用时建议同一使用一种数据类型便于统计处理。

三、兼容性

IE:不支持
Edge:14+
Firefox:31+
Chrome:39+
Opera:26+

四、注意事项

该API没有响应回调方法,所以建议服务器返回204状态码
该API同样可以应用于一些只需要发送数据而不需要关心返回结果的场景(如CSRF攻击中使用此API构造POST请求,要比模拟表单提交成本低很多)

getBattery API用法详解

本文介绍getBattery API的使用方法及注意事项。getBattery API是获取电源管理信息的API。

本文内容可以在https://www.lyz810.com/demo/battery/中查看相关Demo。
一、概述
navigator.getBattery是一个用于获取电源管理信息的API。它可以获取电源的连接状态、电池电量水平、剩余放电时间、剩余充电时间等于电源管理相关的参数。

二、兼容性
navigator.getBattery基本只能在Chrome及Firefox浏览器上使用,其他浏览器基本上都没戏。
Firefox:43+
Chrome:38+
Opera:25+

三、基本用法

  navigator.getBattery().then(function(batteryInfo){
    console.log(batteryInfo);
  });

navigator.getBattery返回一个Promise对象,通过then的第一个function,可以获取到电池状态信息。
其中batteryInfo对象如下:
batteryInfo.charging:电源状态,true表示电源接通(但是否充电不一定),false表示电源没有接通。
batteryInfo.chargingTime:充电时长,目前测试没有发现此数值除Infinity外还有其他值,可能与系统等因素有关。
batteryInfo.dischargingTime:剩余时长,在不接电源时此属性会有一个非Infinity的值,表示电池剩余时间的秒数。
batteryInfo.level:电池电量水平,范围0~1表示0%~100%。
batteryInfo.onchargingchange:当切换电源状态时触发,如插入电源和拔出电源。
batteryInfo.onchargingtimechange:目前未发现会触发事件,因为chargingTime总是Infinity。
batteryInfo.ondischargingtimechange:电池剩余时间改变时触发该事件。
batteryInfo.onlevelchange:在电池电量水平改变时触发。

四、注意事项
Firefox中电量水平的精度为0.1,会对电量进行四舍五入,如电量为95%时,Firefox电量显示为100%(1),当电量为94%时,Firefox电量显示为90%(0.9)
Chrome中电量水平的精度为0.01,能够精确的获取1%的电量差距
电池剩余时间估算可能与系统的电池剩余时间不同

Javascript原生base64编码解码函数btoa(atob)用法详解

window.btoa和window.atob分别编码与解码base64,它们在现代浏览器中受到广泛的支持。

一、兼容性

btoa和atob在除IE之外的浏览器上都能够得到良好的兼容:
IE:10+
Chrome:4+
Firefox:2+
Safari:3.1+
Opera:11.5+

二、编码base64函数btoa
提示:函数名中的b表示binary即原始的待编码数据,a表示ASCII,即编码后的结果(base64编码后,就只是纯的ASCII字符),btoa就是将binary的数据转为ASCII字符(串)。

用法:
console.log(btoa('Hello world!'));//SGVsbG8gd29ybGQh

三、解码base64函数atob
可以将base64字符串还原成二进制格式(通常是原始的字符串,JavaScript中字符串就是一种序列化的二进制数据)

用法:
console.log(atob('SGVsbG8gd29ybGQh'));//Hello world!

四、原始数据含非ASCII字符(例如中文)时的处理
执行下面的代码:
btoa('我是中文');
会有如下的报错信息:

Uncaught DOMException: Failed to execute ‘btoa’ on ‘Window’: The string to be encoded contains characters outside of the Latin1 range.(…)

说明只能对ASCII字符进行编码,对于中文来说,可以通过下面的方式进行编码:
console.log(btoa(unescape(encodeURIComponent('我是中文'))));//5oiR5piv5Lit5paH

解码方法:
console.log(decodeURIComponent(escape(atob('5oiR5piv5Lit5paH'))));//我是中文

nginx中文文档-ngx_stream_map_module

此页面版本:2016-07-05,577bcc35-cc0
ngx_stream_map_module 模块(1.11.2+)创建变量,其值取决于其他变量的值。

示例配置

map $remote_addr $limit {
    127.0.0.1    "";
    default      $binary_remote_addr;
}

limit_conn_zone $limit zone=addr:10m;
limit_conn addr 1;

map

语法:map string $variable { … }
默认:—
上下文:stream

创建新变量,其值依赖于指定在第一个参数中的一个或多个源变量。
由于变量只有在使用的时候会被计算,可以声明一个很大的变量映射表,而不会带来连接的额外开销。
在map块中的参数指定了一个源与结果值之间的映射关系。
源值定义为字符串或正则表达式。
字符串匹配忽略大小写。
正则表达式可以以“~”标识起始,表示大小写敏感的匹配,也可以以“~*”标识起始,表示大小写不敏感的匹配。一个正则表达式可以包含名称和位置捕获,稍后可以用于其他指令。
如果源值匹配任一个下面描述的特殊参数名,需要用“\”标识作为前缀。
结果值可以包含文本、变量以及它们的混合。
指令可以支持三种特殊参数:
default value
如果源值不匹配任何指定的变量,设置结果值。当没有指定default时,默认结果值是一个空字符串。

hostnames
表明源值可以是带前缀或后缀的主机名:

*.example.com 1;
example.*     1;

下面两个记录:

example.com   1;
*.example.com 1;

可以合并为:

.example.com  1;

该参数需要在列表值之前指定。

include file
包含一个含值的文件,可以有多个包含。

如果源值匹配超过一个定义的变量,例如同时匹配一个标记和正则表达式,先匹配的变量会被选中,按照下面的优先级:

  1. 没有标记的字符串值
  2. 最长的前缀标记字符串,例如“*.example.com”
  3. 最长的后缀标记字符串,例如“mail.*”
  4. 第一个匹配的正则表达式(按出现在配置文件中的顺序)
  5. 默认值

map_hash_bucket_size

语法:map_hash_bucket_size size
默认:map_hash_bucket_size 32|64|128
上下文:stream

为映射变量哈希表设置一个桶的大小。默认大小取决于处理器的缓存行大小。

map_hash_max_size

语法:map_hash_max_size size
默认:map_hash_max_size 2048
上下文:stream

设置变量映射哈希表的最大大小。

nginx中文文档-ngx_stream_return_module

此页面版本:2016-07-05,577bcc35-851
ngx_stream_return_module模块(1.11.2+)允许向客户端发送一个指定的值然后关闭连接。

示例配置

server {
    listen 12345;
    return $time_iso8601;
}

return

语法:return value
默认:—
上下文:server

指定一个值发送到客户端,这个值可以包含文本、变量以及它们的混合。

HTML5实现多文件上传(支持文件拖拽及上传进度与速度)

本文介绍通过HTML5新增的多个API实现多文件(拖拽)上传,支持上传进度及上传速度显示。

本文示例可以在https://www.lyz810.com/demo/fileUpload/上查看。

一、input的multiple属性
input的file类型中新增了multiple属性,用于选择多个文件:
<input type="file" multiple="multiple" />

二、input的accept属性
accept属性用于限制选择文件的类型,例如accept=”image/*”则只能选择图片,其值为文件的MIME类型,可以由逗号分割多个值,如accept=”image/*,text/plain”。MIME类型可以参考http://www.w3school.com.cn/media/media_mimeref.asp

注意:此处只是限制打开文件选择框时默认展现的文件类型,用户可以通过更改选择文件类型,选择其他类型的文件,所以应该对文件类型进行校验。

三、progress元素
progress元素为HTML5新增元素,用于显示进度条,默认样式如下: 你的浏览器不支持此标签

元素的max属性为进度条满时的值
给元素设置value属性,即可控制进度条的进度,如设置进度为50%(value=50,max=100): 你的浏览器不支持此标签

四、拖拽事件
本例中使用的拖拽事件有以下几个:
1.ondragover:当文件出于元素上方时触发
2.ondragout:当文件脱离元素时触发
3.ondrop:当文件在元素上,并且鼠标释放时触发

五、通过Ajax上传文件
本例中,多个文件是通过多个请求上传的。这样上传的好处是,对于每一个文件来说,都可以根据上传结果来判断是否需要重传。
也可以通过一个请求同时上传多个文件,但如果上传过程中出错,那么重传的成本要更高,具体使用哪种方式可以根据自己的实际需要进行选择。
本例中,若服务器返回5xx状态码则认为上传失败,会重新上传失败的文件(如果失败3次则不再尝试重传)。
代码片段如下(截取核心代码,完整代码请进入Demo页面查看):

function uploadFile(index){
  var formData = new FormData();
    formData.append('file', files[index]);
    var xhr = new XMLHttpRequest();
    xhr.open('POST', './backend.php', true);
    xhr.onreadystatechange = function(){
      if(xhr.readyState === xhr.DONE){
        if(xhr.status >= 200 && xhr.status < 300){ 
          failCount = 0;
          if(files[++index]){ 
            uploadFile(index);
          } 
        } else if(xhr.status >= 500 && xhr.status < 600){ 
           failCount++;
           if(failCount > 3){
             failCount = 0;
             if(files[++index]){
               uploadFile(index);
             }
           }else{
            setTimeout(function(){
              uploadFile(index);
            }, 1000);
          }
        }
      }
    };
    xhr.send(formData);
}

六、上传进度与上传速率查看
XMLHttpRequest对象的upload.onprogress事件可以提供上传进度相关的数据
它的参数中包含了已上传字节数loaded、总字节数total、时间戳timeStamp
通过上传字节数/总字节数可以得出上传进度的百分比
通过两次onprogress事件timeStamp之差可以得出两次的时间差(毫秒),通过两次的loaded值可以得出该时间段内上传的字节数
多个文件上传时,可以算出一个文件占整体的百分比,当前上传的文件索引可以得出已经上传的文件个数,用已上传的文件个数乘上每个文件的占比,就可以得到当前文件上传起始时的整体百分比,加上当前文件上传的百分比*每个文件占用的百分比即可得出整体进度的百分比。
这样就可以大致的算出上传的速率:

var timeStamp = 0,
  loaded = 0,
  start = Date.now();
xhr.upload.onprogress = function(e){
  var curProgress = (e.loaded * 100 / e.total).toFixed(2),
    //index:当前文件索引,files:所有要上传的文件
    totalProgress = index / files.length + curProgress / files.length * .01,
    timeInterval = 0,
    rate = 0;
  if(timeStamp > 0){
    timeInterval = e.timeStamp - timeStamp;
  }else{
    timeInterval = Date.now() - start;
  }
  rate = (e.loaded - loaded) / 1024 / timeInterval * 1000;
  timeStamp = e.timeStamp;
  loaded = e.loaded;
};