前端开发 大前端 W3Cbest

一个专注 WEB 开发的技术博客

0%

径向渐变由它的中心定义。 为了创建一个径向渐变,你也必须至少定义两种颜色结点。颜色结点即你想要呈现平稳过渡的颜色。同时,你也可以指定渐变的中心、形状(圆形或椭圆形)、大小。默认情况下,渐变的中心是 center(表示在中心点),渐变的形状是 ellipse(表示椭圆形),渐变的大小是 farthest-corner(表示到最远的角落)。 语法

background: radial-gradient(center, shape size, start-color, ..., last-color);

径向渐变 - 颜色结点均匀分布

background: radial-gradient(red, green, blue); /* 标准的语法 */

径向渐变 - 颜色结点不均匀分布

background: radial-gradient(red 5%, green 15%, blue 60%); /* 标准的语法 */

设置形状

shape 参数定义了形状。它可以是值 circle 或 ellipse。其中,circle 表示圆形,ellipse 表示椭圆形。默认值是 ellipse。

background: radial-gradient(circle, red, yellow, green); /* 标准的语法 */

不同尺寸大小关键字的使用

size 参数定义了渐变的大小。它可以是以下四个值:

  • closest-side
  • farthest-side
  • closest-corner
  • farthest-corner

带有不同尺寸大小关键字的径向渐变

background: radial-gradient(60% 55%, closest-side,blue,green,yellow,black);

background: radial-gradient(60% 55%, farthest-side,blue,green,yellow,black);

重复的径向渐变

repeating-radial-gradient() 函数用于重复径向渐变

background: repeating-radial-gradient(red, yellow 10%, green 15%);

CSS3 渐变(gradients)可以让你在两个或多个指定的颜色之间显示平稳的过渡。 以前,你必须使用图像来实现这些效果。但是,通过使用 CSS3 渐变(gradients),你可以减少下载的时间和宽带的使用。此外,渐变效果的元素在放大时看起来效果更好,因为渐变(gradient)是由浏览器生成的。 CSS3 定义了两种类型的渐变(gradients):

  • 线性渐变(Linear Gradients)- 向下/向上/向左/向右/对角方向
  • 径向渐变(Radial Gradients)- 由它们的中心定义

CSS3 线性渐变

为了创建一个线性渐变,你必须至少定义两种颜色结点。颜色结点即你想要呈现平稳过渡的颜色。同时,你也可以设置一个起点和一个方向(或一个角度)。 语法

background: linear-gradient(direction, color-stop1, color-stop2, ...);

线性渐变 - 从上到下(默认情况下)

background: linear-gradient(red, blue); /* 标准的语法 */

线性渐变 - 从左到右

background: linear-gradient(to right, red , blue); /* 标准的语法 */

线性渐变 - 对角

background: linear-gradient(to bottom right, red , blue); /* 标准的语法 */

使用角度

如果你想要在渐变的方向上做更多的控制,你可以定义一个角度,而不用预定义方向(to bottom、to top、to right、to left、to bottom right,等等)。 语法

background: linear-gradient(angle, color-stop1, color-stop2);

角度是指水平线和渐变线之间的角度,逆时针方向计算。换句话说,0deg 将创建一个从下到上的渐变,90deg 将创建一个从左到右的渐变。 http://www.w3cbest.com/wp-content/uploads/2019/07/7B0CC41A-86DC-4E1B-8A69-A410E6764B91.jpg 但是,请注意很多浏览器(Chrome,Safari,fiefox等)的使用了旧的标准,即 0deg 将创建一个从左到右的渐变,90deg 将创建一个从下到上的渐变。换算公式 90 - x = y 其中 x 为标准角度,y为非标准角度。 带有指定的角度的线性渐变

background: linear-gradient(180deg, red, blue); /* 标准的语法 */

使用多个颜色结点 带有多个颜色结点的从上到下的线性渐变

background: linear-gradient(red, green, blue); /* 标准的语法 */

带有多个颜色结点的从左到右的线性渐变

background: linear-gradient(to right, red,orange,yellow,green,blue,indigo,violet);

使用透明度(transparent)

CSS3 渐变也支持透明度(transparent),可用于创建减弱变淡的效果。 为了添加透明度,我们使用 rgba() 函数来定义颜色结点。rgba() 函数中的最后一个参数可以是从 0 到 1 的值,它定义了颜色的透明度:0 表示完全透明,1 表示完全不透明。

background: linear-gradient(to right, rgba(255,0,0,0), rgba(255,0,0,1)); /* 标准的语法 */

重复的线性渐变

repeating-linear-gradient() 函数用于重复线性渐变:

background: repeating-linear-gradient(red, yellow 10%, green 20%);

ECharts 雷达图可以使用 series[i]-radar设置 ,雷达图主要用于表现多变量的数据,例如玩”和平精英”战况的各个属性分析,雷达图依赖radar组件。 关于雷达图其他的一些属性自行去查看官网,我们在这里讲的是在雷达图上设置极坐标。 首先要了解极坐标系,因为实现极坐标需要一个系列三个属性完成,分别是polar:{}极坐标系、radiusAxis:{}极坐标系的径向轴、angleAxis:{}极坐标系的角度轴完成,查看示例 如何把极坐标系和雷达图合并呢,下面我们就来说说如何设置,还是需要用到radar组件,radar组件下面有一个indicator属性, 它是雷达图的指示器,用来指定雷达图中的多个变量(维度)。 然后我们它的最大值radar.indicator[i].max和最小值radar.indicator[i].min设为统一的值,要注意它的这里的最大值要大于或等于你的数据的最大值,最小值要小于或等于你的数据的最小值。 下面是用雷达图表现的示例。

See the Pen echarts 雷达图设置极坐标 by w3cbest.com (@w3cbest) on CodePen.

var max = 4, min = -4;
option = {
    ...
    radar: {
        ...
        indicator: [{
                name: '销售',
                max: max,
                min: min
            },
            {
                name: '管理',
                max: max,
                min: min
            },
            {
                name: '技术',
                max: max,
                min: min
            }
        ],
        splitArea: {
            show: false
        },
        splitLine: {
            show: false
        }
    },
    polar: {},
    angleAxis: {
        min: 0,
        max: 360,
        interval: 5,
        clockwise: false,
        axisTick: {
            show: false
        },
        axisLabel: {
            show: false
        },
        axisLine: {
            show: false
        },
        splitLine: {
            show: false
        }
    },
    radiusAxis: {
        min: min,
        max: max,
        interval: 2,
        splitArea: {
            show: true
        }
    },
    series: [{
        ...
        data: [{
            value: [4, -4, 1]
        }]
    }]
};

ECharts 雷达图可以使用series[i]-radar设置 ,雷达图主要用于表现多变量的数据,例如玩”和平精英”战况的各个属性分析,雷达图依赖radar组件。 关于雷达图其他的一些属性自行去查看官网,我们在这里讲的是在遇到负值的时候如何设置雷达图的中心点,通过官网的例子来设置的话,遇到负值无法找到中心位置,使得雷达图的内部图形发生变异。 下面我们就来说说如何设置,想要设置负值还需要从radar组件出发,radar组件下面有一个indicator属性, 它是雷达图的指示器,用来指定雷达图中的多个变量(维度)。 然后我们它的最大值radar.indicator[i].max和最小值radar.indicator[i].min设为统一的值,要注意它的这里的最大值要大于或等于你的数据的最大值,最小值要小于或等于你的数据的最小值 下面是用雷达图表现的示例。

See the Pen echarts 雷达图遇到负值中心点偏移的设置 by w3cbest.com (@w3cbest) on CodePen.

var max = 100, min = -50;
option = {

radar: {

indicator: [{
name: ‘销售’,
max: max,
min: min
},
{
name: ‘管理’,
max: max,
min: min
},
{
name: ‘技术’,
max: max,
min: min
}
],
},
series: [{

data: [{
value: [100, 80, -40]
}]
}]
};

为什么要做 HTML5 语义化

HTML5的主要进步之一是引入了一组标准化的语义元素。 ”语义元素“是用于以更有意义的方式标记文档结构的元素,这种方式可以清楚地表明它们的用途和目的是什么。而且重要的是,由于它们是标准化的,定义文档的这些元素可以被每个人使用并理解,包括机器人。 在web无障碍开发领域,给视障用户使用的屏幕阅读器是视障群体访问网络的必备工具,合理的 HTML5 语义化元素,会让屏幕阅读器正确的理解网页的内容,从而以最为合适的方式朗读。 站在开发者的角度,写出符合 HTML5 语义化的结构,也是显示我们职业素养的一种方式,最为重要的是这样的代码自带翻译,比如下面的div结构,为了让开发者明白div的含义,我们必须在class命名上下功夫。

Super duper best blog ever

...

Why you should buy more cheeses than you currently do

...

而如果采用语义化元素,结构一目了然,不管是日后维护还是交接给他人,都是件轻松的事情。

Super duper best blog ever

...

Why you should buy more cheeses than you currently do

...
Contact us!
this.is.us@example.com

先来回顾一下 HTML5 有哪些元素(根据 MDN 资料整理)

HTML5 元素大全

文档元素

元素

html

HTML 文档中最外层的元素,也可称为根元素。

文档元数据

元素

元素

head

表示文档的头部

title

用来定义文档的标题

base

为页面上的所有的相对链接规定默认 URL 或默认目标

link

定义文档与外部资源的关系

meta

提供了 HTML 文档的元数据

style

用于表示文档所使用的样式

区块

元素

元素

body

表示文档的内容

article

表示文档、页面、应用或网站中的独立结构

section

表示文档中的一个区域(或节)

nav

描绘一个含有多个超链接的导航栏区域

aside

表示一个和其余页面内容几乎无关的部分

h1-h6

标题(Heading)元素呈现了六个不同的级别的标题,<h1> 级别最高,而 <h6> 级别最低

footer

表示最近一个章节内容或者根节点(sectioning root )元素的页脚

header

用于展示介绍性内容

内容分组

元素

元素

p

表示文本的一个段落

address

表示其中的内容提供了某个人或某个组织(等等)的联系信息

hr

表示段落级元素之间的主题转换

pre

表示预定义格式文本

blockquote

表示其中的文字是引用内容

ol

表示多个元素的有序列表

ul

表示多个元素的无序列表

li

表示列表里的条目

dl

表示一个包含术语定义以及描述的列表

dt

用于在一个定义列表中声明一个术语

dd

用来指明一个描述列表元素中一个术语的描述

figure

代表一段独立的内容

figcaption

与其相关联的图片的说明/标题

main

呈现了文档的 <body> 或应用的主体部分

div

通用型的流内容容器,它应该在没有任何其它语义元素可用时才使用

文本级语义

元素

元素

a

定义超链接,用于从一个页面链接到另一个页面

em

标记出需要用户着重阅读的内容

strong

表示文本十分重要

small

表示边注释和附属细则,包括版权和法律文本

s

表示不再相关,或者不再准确的事情

cite

表示一个作品的引用

q

表示一个封闭的并且是短的行内引用的文本

dfn

表示术语的一个定义

abbr

用于展示缩写

ruby

用来展示东亚文字注音或字符注释

rb

用于分隔<ruby>注释的基本文本组件

rt

包含字符的发音

rtc

包含 <ruby> 元素中文字的语义注解

rp

用于为那些不能使用 <ruby> 元素展示 ruby 注解的浏览器

data

将一个指定内容和机器可读的翻译联系在一起

time

用来表示24小时制时间或者公历日期

code

呈现一段计算机代码

var

表示变量的名称,或者由用户提供的值

samp

用于标识计算机程序输出

kbd

表示用户输入

sub

定义了一个下标文本区域

sup

定义了一个上标文本区域

i

用于表现因某些原因需要区分普通文本的一系列文本

b

用于吸引读者的注意到该元素的内容上

u

表示具有未标注的文本跨度,显示渲染,非文本注释

mark

用来表示上下文的关联性的而突出显示的文字

bdi

隔离可能以不同方向进行格式化的外部文本

bdo

用于覆盖当前文本的朝向

span

短语内容的通用行内容器,并没有任何特殊语义

br

在文本中生成一个换行(回车)符号

wbr

一个文本中的位置,其中浏览器可以选择来换行

修改记录

元素

元素

ins

定义已经被插入文档中的文本

del

表示一些被从文档中删除的文字内容

嵌入内容

元素

元素

picture

通过包含零或多个 <source> 元素和一个 <img> 元素来为不同的显示/设备场景提供图像版本

source

为 <picture><audio> 或者 <video> 元素指定多个媒体资源

img

代表文档中的一个图像

iframe

表示嵌套的浏览上下文,有效地将另一个HTML页面嵌入到当前页面中

embed

将外部内容嵌入文档中的指定位置。此内容由外部应用程序或其他交互式内容源(如浏览器插件)提供

object

表示引入一个外部资源,这个资源可能是一张图片,一个嵌入的浏览上下文,亦或是一个插件所使用的资源

param

<object>元素定义参数

video

用于支持文档内的视频播放

audio

用于在文档中表示音频内容

track

指定计时字幕(或者基于时间的数据)

map

与 <area>属性一起使用来定义一个图像映射(一个可点击的链接区域)

area

在图片上定义一个热点区域,可以关联一个超链接

表格数据

元素

元素

table

表示表格数据

caption

展示一个表格的标题

colgroup

用来定义表中的一组列表

col

定义表格中的列,并用于定义所有公共单元格上的公共语义

tbody

表示它们包含表的主体

thead

定义了一组定义表格的列头的行

tfoot

定义了一组表格中各列的汇总行

tr

定义表格中的行

td

定义包含数据的表格的单元格

th

定义表格内的表头单元格

表单

元素

元素

form

示了文档中的一个区域,这个区域包含有交互控制元件

label

表示用户界面中某个元素的说明

input

用于为基于Web的表单创建交互式控件,以便接受来自用户的数据

button

表示一个可点击的按钮

select

表示一个控件,提供一个选项菜单

datalist

包含了一组<option>元素,这些元素表示其它表单控件可选值

optgroup

创建包含在一个 <select>元素中的一组选项

option

用于定义在<select><optgroup> 或<datalist> 元素中包含的项

textarea

表示一个多行纯文本编辑控件

output

表示计算或用户操作的结果

progress

用来显示一项任务的完成进度

meter

用来显示已知范围的标量值或者分数值

fieldset

用来对表单中的控制元素进行分组

legend

用于表示它的父元素<fieldset>的内容的标题

交互元素

元素

元素

details

可创建一个挂件,仅在被切换成展开状态时,它才会显示内含的信息

summary

用作 一个<details>元素的一个内容的摘要,标题或图例

dialog

表示一个对话框或其他交互式组件

脚本元素

元素

元素

script

用于嵌入或引用可执行脚本

noscript

定义脚本未被执行时的替代内容

template

用于保存客户端内容机制,该内容在加载页面时不会呈现

canvas

用来通过脚本(通常是JavaScript)绘制图形

slot

是 Web Components 技术套件的一部分,是Web组件内的一个占位符

基本布局

示例页面

我的网站

文章标题11111

巴拉巴拉巴拉巴拉巴拉巴拉巴拉

文章标题2222

巴拉巴拉巴拉巴拉巴拉巴拉巴拉

文章标题3333

巴拉巴拉巴拉巴拉巴拉巴拉巴拉

查看示例

header 、footer 不止表示页头页尾

通常的用法都是把一个页面的页头用header,页尾用footer来表示,但这并不是它们的唯一用法,根据元素的定义,它们表示的是章节或区块的头和尾,严格来说一个<article> 元素的头部需要用 header 来表示,如:

我们是相亲相爱的一家人

.....

但以上这种 header 中只有一个 h2 的场景中 header 是可以忽略的。 MacOs VoiceOver 读屏软件对 div 中的 header 会说“横幅”,而对 article 、section 中的 header 会忽略,直接读内部的内容。查看示例

section 、 figure 的用法区别

section 和 figure 有相似的地方,都可以表示一个区域,结构上也相似,都可以有标题和内容,但两个元素的用法是完全不一样的。

  • section 的内容跟上下文结构存在关联关系,figure 的内容是独立存在的一部分,把 figure 移除不影响主体内容的表达;
  • section 中存在标题只能在开始的位置,figure 中的标题可以在开头也可以在结尾;

MacOs VoiceOver 读屏软件会把 section 的标题说成“标题”,但对 figure 的标题会说成“文本”。查看示例

article 、 section 、 div 的用法区别

  • 如果元素内容可以分为几个部分的话,应该使用 <article> 而不是 <section>
  • 如果内容中的几个部分是互相独立的,应该使用 <article> 嵌套,几个部分的内容之间是关联的应该使用 <section>
  • 不要把 <section> 元素作为一个普通的容器来使用,这是本应该是<div>的用法(特别是当片段仅仅是为了美化样式的时候)。

MacOs VoiceOver 读屏软件对3个元素的朗读方式没有区别,都是直接读取内部的内容。查看示例

不是所有的导航链接都需要 nav

只用来将一些热门的链接放入 <nav> 导航栏,建议这些链接应该是跟当前页面或站点有较强的关联性。例如 <footer> 元素就常用来在页面底部包含一个不常用到,没必要加入 <nav> 的链接列表。 MacOs VoiceOver 读屏软件遇到 nav 时会先说“导航“,下一步读取内部的内容,最后会说”导航的结尾“。查看示例

address 的内容不只是地址

HTML <address> 元素 表示其中的内容提供了某个人或某个组织的联系信息,包括真实地址、URL、电子邮箱、电话号码、社交媒体账号、地理坐标等等,通过它会被放到 footer 里,但这并不是唯一的用法,在页头 header 中,article 或其它区块中需要显示联系信息的地方都可以使用 address。 MacOs VoiceOver 读屏软件遇到 address 只是当作普通文本朗读。查看示例

em 、 strong 、 b 、 i 、 mark 的用法区别

这几个元素表示的都是对文本内容的强调,但强调的含义是不一样的

  • em 强调的是语句的重心,如“我<em>今天</em>吃薯条了”、“我今天吃<em>薯条</em>了”;
  • strong 强调的是一句话中重要的一定要看的部分,如”每天早上9点打卡,<strong迟到者罚款1000元</strong>“;
  • b 强调的是视觉上需要吸引用户来看的内容,类似广告语,对用户来说并不一定很重要;
  • i 强调的是一段文本中,某些部分需要跟周边的文本做一些视觉上区别,但不一定是吸引人的;
  • mark 强调的是上下文的关联性,如搜索关键字;

MacOs VoiceOver 读屏软件遇到这5个元素时只是当作普通文本朗读。查看示例

small 、 s 、 u 还能用吗

这三个元素在 HTML5 之前,都是为了表示明显的排版视觉效果,HTML5 中保留下来并添加了新的语义

  • small 用来描述对内容的注释,如版权和法律文本等;
  • s 用来表示不再相关或不再准确的事,如之前对某句话的解释,很多年之后已经不适用,但要保留在内容中的时候;
  • u 用来标记中文文本中的专有名称,或将文本标记为拼写错误;

建议在除了上面提到的几个特殊用途之外,不再使用这三个元素; MacOs VoiceOver 读屏软件遇到这3个元素时只是当作普通文本朗读。查看示例

blockquote 、 q 、 cite 都表示引用,如何区分

  • blockquote 引用的是长文本;
  • q 引用的是短文本;
  • cite 引用的是一个作品的名称或链接;

MacOs VoiceOver 读屏软件遇到这3个元素时只是当作普通文本朗读。查看示例

什么时候使用 div

根据元素的定义,只有在所有 html5 标签都不适用于你想表达的语义时,这时候才使用终极大法 div,从这个角度来说,目前对 div 元素的滥用其实是程序员偷懒的一种表现,反正有 div 兜底,也就懒得去思考那些语义元素的区别。 在追求开发效率和做不完的需求面前,使用 div 兜底也是很多人无奈的选择,我觉得这个是可以理解,某种程度上也可以接受的,但进一步去想一想,你的产品面向的用户也有可能会是存在各种行为障碍的视障、残障人群,他们无法像正常人那样使用你开发的功能,而需要借助类似于屏幕阅读器这样的辅助工具,这时候才是真正考验你的产品是否合格的时候。 我们做语义化开发,很大程度上就是在帮助更多人的人正常的使用我们的产品,同时也会让你离专业的程序员更进一步。

参考资料

来源:http://gafish.github.io

什么是deferred对象?

开发网站的过程中,我们经常遇到某些耗时很长的javascript操作。其中,既有异步的操作(比如ajax读取服务器数据),也有同步的操作(比如遍历一个大型数组),它们都不是立即能得到结果的。 通常的做法是,为它们指定回调函数(callback)。即事先规定,一旦它们运行结束,应该调用哪些函数。 但是,在回调函数方面,jQuery的功能非常弱。为了改变这一点,jQuery开发团队就设计了deferred对象。 简单说,deferred对象就是jQuery的回调函数解决方案。在英语中,defer的意思是”延迟”,所以deferred对象的含义就是”延迟”到未来某个点再执行。 它解决了如何处理耗时操作的问题,对那些操作提供了更好的控制,以及统一的编程接口。它的主要功能,可以归结为四点。下面我们通过示例代码,一步步来学习。

ajax操作的链式写法

首先,回顾一下jQuery的ajax操作的传统写法:

$.ajax({
    url: "test.html",
    success: function() {
        console.log("success");
    },
    error: function() {
        console.log("error");
    }
});

在上面的代码中,$.ajax()接受一个对象参数,这个对象包含两个方法:success方法指定操作成功后的回调函数,error方法指定操作失败后的回调函数。 $.ajax()操作完成后,如果使用的是低于1.5.0版本的jQuery,返回的是XHR对象,你没法进行链式操作;如果高于1.5.0版本,返回的是deferred对象,可以进行链式操作。 现在,新的写法是这样的:

$.ajax({
    url: '/path/to/file',
    type: 'default GET (Other values: POST)',
    dataType: 'default: Intelligent Guess (Other values: xml, json, script, or html)',
    data: {param1: 'value1'},
})
.done(function() {
    console.log("success");
})
.fail(function() {
    console.log("error");
})
.always(function() {
    console.log("complete");
});

可以看到,done()相当于success方法,fail()相当于error方法。采用链式写法以后,代码的可读性大大提高。

指定同一操作的多个回调函数

deferred对象的一大好处,就是它允许你自由添加多个回调函数。 还是以上面的代码为例,如果ajax操作成功后,除了原来的回调函数,我还想再运行一个回调函数,怎么办? 很简单,直接把它加在后面就行了。

$.ajax("test.html")
.done(function(){ console.log("哈哈,成功了!");} )
.fail(function(){ console.log("出错啦!"); } )
.done(function(){ console.log("第二个回调函数!");} );

回调函数可以添加任意多个,它们按照添加顺序执行。

为多个操作指定回调函数

deferred对象的另一大好处,就是它允许你为多个事件指定一个回调函数,这是传统写法做不到的。 请看下面的代码,它用到了一个新的方法$.when()

$.when($.ajax("test1.html"), $.ajax("test2.html"))
.done(function(){ console.log("哈哈,成功了!"); })
.fail(function(){ console.log("出错啦!"); });

这段代码的意思是,先执行两个操作$.ajax(“test1.html”)和$.ajax(“test2.html”),如果都成功了,就运行done()指定的回调函数;如果有一个失败或都失败了,就执行fail()指定的回调函数。

普通操作的回调函数接口(上)

deferred对象的最大优点,就是它把这一套回调函数接口,从ajax操作扩展到了所有操作。也就是说,任何一个操作—-不管是ajax操作还是本地操作,也不管是异步操作还是同步操作—-都可以使用deferred对象的各种方法,指定回调函数。 我们来看一个具体的例子。假定有一个很耗时的操作wait:

var wait = function() {
    var tasks = function() {
        console.log("执行完毕!");
    };
    setTimeout(tasks, 5000);
};

我们为它指定回调函数,应该怎么做呢? 很自然的,你会想到,可以使用$.when():

$.when(wait())
.done(function(){ console.log("哈哈,成功了!"); })
.fail(function(){ console.log("出错啦!"); });

但是,这样写的话,done()方法会立即执行,起不到回调函数的作用。原因在于$.when()的参数只能是deferred对象,所以必须对wait()进行改写:

var dtd = $.Deferred(); // 新建一个deferred对象
var wait = function(dtd) {
    var tasks = function() {
        console.log("执行完毕!");
        dtd.resolve(); // 改变deferred对象的执行状态
    };
    setTimeout(tasks, 5000);
    return dtd;
};

现在,wait()函数返回的是deferred对象,这就可以加上链式操作了。

$.when(wait(dtd))
.done(function(){ console.log("哈哈,成功了!"); })
.fail(function(){ console.log("出错啦!"); });

wait()函数运行完,就会自动运行done()方法指定的回调函数。

deferred.resolve()方法和deferred.reject()方法

如果仔细看,你会发现在上面的wait()函数中,还有一个地方我没讲解。那就是dtd.resolve()的作用是什么? 要说清楚这个问题,就要引入一个新概念”执行状态”。jQuery规定,deferred对象有三种执行状态—-未完成,已完成和已失败。如果执行状态是”已完成”(resolved),deferred对象立刻调用done()方法指定的回调函数;如果执行状态是”已失败”,调用fail()方法指定的回调函数;如果执行状态是”未完成”,则继续等待,或者调用progress()方法指定的回调函数(jQuery1.7版本添加)。 前面部分的ajax操作时,deferred对象会根据返回结果,自动改变自身的执行状态;但是,在wait()函数中,这个执行状态必须由程序员手动指定。dtd.resolve()的意思是,将dtd对象的执行状态从”未完成”改为”已完成”,从而触发done()方法。 类似的,还存在一个deferred.reject()方法,作用是将dtd对象的执行状态从”未完成”改为”已失败”,从而触发fail()方法。

var dtd = $.Deferred(); // 新建一个Deferred对象
var wait = function(dtd) {
    var tasks = function() {
        console.log("执行完毕!");
        dtd.reject(); // 改变Deferred对象的执行状态
    };
    setTimeout(tasks, 5000);
    return dtd;
};

$.when(wait(dtd))
 .done(function() { console.log("哈哈,成功了!"); })
 .fail(function() { console.log("出错啦!"); });

deferred.promise()方法

上面这种写法,还是有问题。那就是dtd是一个全局对象,所以它的执行状态可以从外部改变。 请看下面的代码:

var dtd = $.Deferred(); // 新建一个Deferred对象
var wait = function(dtd) {
    var tasks = function() {
        console.log("执行完毕!");
        dtd.resolve(); // 改变Deferred对象的执行状态
    };
    setTimeout(tasks, 5000);
    return dtd;
};

$.when(wait(dtd))
.done(function() { console.log("哈哈,成功了!"); })
.fail(function() { console.log("出错啦!"); });
dtd.resolve();

我在代码的尾部加了一行dtd.resolve(),这就改变了dtd对象的执行状态,因此导致done()方法立刻执行,跳出”哈哈,成功了!”的提示框,等5秒之后再跳出”执行完毕!”的提示框。 为了避免这种情况,jQuery提供了deferred.promise()方法。它的作用是,在原来的deferred对象上返回另一个deferred对象,后者只开放与改变执行状态无关的方法(比如done()方法和fail()方法),屏蔽与改变执行状态有关的方法(比如resolve()方法和reject()方法),从而使得执行状态不能被改变。 请看下面的代码:

var dtd = $.Deferred(); // 新建一个Deferred对象
var wait = function(dtd) {
    var tasks = function() {
        console.log("执行完毕!");
        dtd.resolve(); // 改变Deferred对象的执行状态
    };
    setTimeout(tasks, 5000);
    return dtd.promise(); // 返回promise对象
};

var d = wait(dtd); // 新建一个d对象,改为对这个对象进行操作
$.when(d)
    .done(function() { console.log("哈哈,成功了!"); })
    .fail(function() { console.log("出错啦!"); });
d.resolve(); // 此时,这个语句是无效的

在上面的这段代码中,wait()函数返回的是promise对象。然后,我们把回调函数绑定在这个对象上面,而不是原来的deferred对象上面。这样的好处是,无法改变这个对象的执行状态,要想改变执行状态,只能操作原来的deferred对象。 不过,更好的写法是allenm所指出的,将dtd对象变成wait()函数的内部对象。

var dtd = $.Deferred(); //在函数内部,新建一个Deferred对象
    var tasks = function() {
        console.log("执行完毕!");
        dtd.resolve(); // 改变Deferred对象的执行状态
    };
    setTimeout(tasks, 5000);
    return dtd.promise(); // 返回promise对象
};

$.when(wait())
    .done(function() { console.log("哈哈,成功了!"); })
    .fail(function() { console.log("出错啦!"); });

普通操作的回调函数接口(中)

另一种防止执行状态被外部改变的方法,是使用deferred对象的建构函数$.Deferred()。 这时,wait函数还是保持不变,我们直接把它传入$.Deferred():

$.Deferred(wait)
  .done(function(){ console.log("哈哈,成功了!"); })
  .fail(function(){ console.log("出错啦!"); });

jQuery规定,$.Deferred()可以接受一个函数名(注意,是函数名)作为参数,$.Deferred()所生成的deferred对象将作为这个函数的默认参数。

普通操作的回调函数接口(下)

除了上面两种方法以外,我们还可以直接在wait对象上部署deferred接口。

var dtd = $.Deferred(); // 生成Deferred对象
var wait = function(dtd) {
    var tasks = function() {
        console.log("执行完毕!");
        dtd.resolve(); // 改变Deferred对象的执行状态
    };
    setTimeout(tasks, 5000);
};

dtd.promise(wait);
wait.done(function() { console.log("哈哈,成功了!"); })
    .fail(function() { console.log("出错啦!"); });
wait(dtd);

要定义网格,请在父元素上使用display: grid或display:inline-grid。然后可以使用grid-template-columns和grid-template-rows属性创建网格。 我使用grid-gap属性在列和行之间创建一个10px的空白。此属性是grid-column-gap和grid-row-gap的简写,因此可以单独设置这些值。 父元素的所有直接子元素现在都成为网格项,自动放置算法将它们放置在每个网格单元中。根据需要会自动创建所需要的行

A
B
C
D
E
F

.wrapper {
display: grid;
grid-template-columns: 100px 100px 100px;
grid-gap: 10px;
background-color: #fff;
color: #444;
}
.box {
background-color: #444;
color: #fff;
border-radius: 5px;
padding: 20px;
font-size: 150%;
}

查看示例

很多人想让我给他们推荐有关CSS部分的教程,或者问我如何学习CSS。 我也看到很多人对CSS的部分内容感到困惑,一部分原因是由于对语言的过时认识。 鉴于CSS在过去几年中发生了相当大的变化,这是一个更新知识的好时机。 即使CSS只是你所做工作的一小部分(因为你使用其他技术栈),CSS是你最终希望在屏幕上显示的结果,所以值得合理去学习。 因此,本文旨在概述CSS的关键基础和资源,以便进一步阅读现代CSS开发的关键领域。 其中许多资源在Smashing杂志上已经有了,但我也选择了其他一些资源,也有人关注CSS的关键领域。 这不是一本完整的初学者指南或旨在涵盖所有知识点。 我的目标是覆盖现代CSS的广度,同时重点关注几个关键领域,将帮助你理解CSS的其他部分。

语言基础知识

对于CSS的大部分内容,你不需要特意去学习属性和值,你可以在需要时查找它们。 然而,CSS中一些基础知识没有掌握好,你将很难去理解它。 这些基础知识值得你花时间去理解,从长远看,它会帮你节省很多时间和少走弯路。

选择器,不仅仅有类

选择器的表现如标题所说的,它选择文档的某些部分,以便你可以将CSS规则应用于它。 大多数人都熟悉使用 class,或在 直接使用HTML元素设置样式,比如 body,但是还有很多更高级的选择器可以根据文档中的位置选择元素,直接选择位于元素之后的元素,或选择表格中的奇数行。 这些选择器是CSS3规范的一部分(你可能听说过它们被称为第3级选择器)具有出色的浏览器支持。 有关可以使用的各种选择器的详细信息,请参阅 MDN 参考。 有些选择器的行为就好像你已经将类应用于文档中的某些内容。 例如p:first-child就像你在第一个p元素中添加了一个类一样,这些被称为 伪类选择器。 伪元素选择器就像动态插入一个元素一样,例如::first-line的表现与用span 包裹第一行文本类似。 但是,如果该行的长度发生变化,它将重新应用,如果插入该元素则不会出现这种情况。 这些选择器可能会相当复杂。 在下面的CodePen中是一个用伪类链接的伪元素的例子。 我们使用:first-child伪类定位第一个p元素,然后::first-line选择器选择该元素的第一行,就好像在第一行周围添加了一个<span>以使其变为粗体和改变颜色。

See the Pen first-line of first-child by rachelandrew (@rachelandrew) on CodePen.

继承和层叠

层叠是指当元素应用了多个样式规则,哪个规则优先应用。 如果你曾经遇到过无法理解为什么某些CSS似乎没有应用的情况,那可能是层叠没有运用好。 层叠与继承紧密相关,继承定义了子元素可以继承父元素的样式属性。 它还与特异性有关,不同的选择器具有不同的特异性,当有几个选择器可以应用于一个元素时,继承可以决定应用哪个规则。

注意:为了理解所有这些内容,我建议阅读MDN CSS简介中的 层叠和继承

如果你正在尝试将一些CSS应用于一个元素,那么你的浏览器开发者工具是开始最好的地方。看看下面的例子,我用元素选择器 h1 将 h1 标题设置为橙色。同时,我也使用类选择器设置h1 设置为紫色。 由于类更具体,因此h1是紫色的。 在开发者工具中,您可以看到元素选择器被划掉,因为它没有被应用。 一旦你看到浏览器正在获取你的CSS(但其他东西已经推翻了它),那么你可以开始找出原因。

See the Pen specificity by rachelandrew (@rachelandrew) on CodePen.

盒模型

CSS里一切都是盒子。 屏幕上显示的所有内容都有一个框,盒模型描述了如何计算该框的大小 - 将外边距,内边距和边框考虑进去。 标准的CSS框模型接受给定元素的宽度,然后将内边框和边框添加到该宽度上——这意味着元素占用的空间大于给定的宽度。 最近,我们已经能够选择使用IE盒模型,使得元素上的给定宽度作为屏幕上可见元素的宽度。 任何内边距或边框都会从边缘插入框的内容。 这对许多布局更有意义。 在下面的演示中,我有两个盒子。 两者的宽度均为200像素,边框为5像素,内边距为20像素。 第一个框使用标准框模型,因此占用总宽度为250像素,第二个框使用IE盒模型,因此实际上是200像素宽。

See the Pen box models by rachelandrew (@rachelandrew) on CodePen.

浏览器开发者工具可以再次帮助你了解正在使用的盒子模型。 在下图中,我使用火狐浏览器的开发者工具使用默认的内容框框模型检查框。 工具告诉我这是正在使用的盒模型,我可以看到大小以及如何将边框和内边框添加到指定的宽度。

注意:在IE6之前,Internet Explorer使用IE盒模型,内边框和边框插入给定宽度的内容里。 所以有一段时间浏览器使用不同的盒模型! 如果今天的互操作性问题感到沮丧,现在已经有所改善,那么我们就不会处理浏览器以不同的方式计算元素的宽度。

CSS Tricks 里,有关于盒模型和盒子尺寸的很好的解释,并解释了在你的网站中全局使用IE盒模型的最佳方法。

标准流

如果你的文档内容用一些HTML标记,你的文档将具有可读性。标题和段落会另起新的一行,单词组成句子时,它们之间有一个空格。标记是用来格式化的,像 em 不会破坏句子的流。 句子会表现标准流,或块流布局。句子的每个部分都被描述为“在流中”,它知道句子其余内容,所以不会重叠。 如果你去了解,而不是去反对这种行为,你会变得更加轻松。这是为什么从正确标记的HTML文档开始很有意义的原因之一,由于浏览器遵守正常流和内置样式表,你的内容从可读的地方开始。

格式化上下文

一旦文档的内容处于正常流程中,您可能希望更改其中一些内容的外观。 你可以通过更改元素的格式上下文来完成此操作。 举个一个非常简单的示例,如果你希望所有段落连在一起而不是从新行开始,你可以设置 p 元素的样式属性display:inline ,将 p 元素由块级元素变成内联元素。 本质上,格式化上下文定义了外部和内部类型。外部控制元素与页面上其他元素的行为,内部控制子元素的外观。例如,当你设置 display:flex ,你在设置外部为块格式化上下文,设置子元素为 flex 格式化上下文。 注意:最新版本的Display规范改变了 display 的值,显式声明外部和内部的类型。因此,将你可能会声明 display:block flex; (外部为 block,内部为 flex) 在 MDN 上阅读更多关于 display的信息

进入或离开流

CSS中的元素被描述为“在流中”或“脱离流”。流中的元素被赋予空间,并且空间被流中的其他元素所影响。 如果通过浮动或定位元素使元素脱离流,则该元素的空间将不再受到其他流元素的影响。 对于绝对定位的元素,是最明显的。 如果你设置一个元素 position: absolute ,该元素会从流中脱离,你需要确保这个元素不会与流中的元素重叠,且不影响你布局的其他部分的可读性。

See the Pen out of flow: abspos by rachelandrew (@rachelandrew) on CodePen.

然而,浮动元素也会从流中脱离,但后面的元素的文本将环绕该浮动元素。你可以设置后面元素的背景颜色,你会看到他们会上升并占用了原来浮动元素原来的空间。

See the Pen Out of flow: floated by rachelandrew (@rachelandrew) on CodePen.

你可以在MDN上阅读更多有关 流中和脱离流 的信息。 需要记住的重要一点是,如果从流中取出一个元素,则需要自己管理重叠,因为块流布局的常规规则不再适用。

布局

十五年来,我们一直使用CSS布局,但没有设计一套布局系统。这已经改变了。 我们现在拥有功能完备的布局系统,其中包括 Grid 和 Flexbox ,还有多列布局和旧布局方法也应用于实际目的。如果你想对CSS布局还不熟悉,你可以阅读 MDN 上的布局教程,或阅读我发表在Smashing杂志上的文章 《开始学习CSS布局》。 不要认为grid和flexbox等方法在某种程度上是竞争的。为了更好地使用布局,你有时会发现组件最好作为 flex ,有时作为 Grid。有时,你想要多列流动布局。所有这些都是不错的选择。如果你觉得自己在与某些事物的行为方式作斗争,这通常是一个非常好的迹象,表明它可能值得退一步,尝试一种不同的方法。我们已经习惯了在CSS上做一些我们想做的事情,以至于我们可能会忘记我们还有很多其他的选择可以尝试。 布局是我的主要专业领域,我在Smashing Magazine和其他地方写了很多文章,试图开拓新的布局美化。 除了上面提到的布局文章,我在Flexbox上有一整套系列 - 《从创建Flex 容器时,发生了什么》。 在 Grid示例 上,我列出很多CSS Grid 的例子 — 以及一个视频教程。 此外 - 特别是对于设计师 - 查看 Jen Simmons 和她的《Layout Land》视频系列。

对齐

通常,我会将对齐和布局分开,虽然大多数人把对齐当作 Flexbox的一部分。对齐这些属性适用于所有布局方法上,应该在上下文去理解对齐,而不是考虑 “Flexbox对齐”或“CSSGrid 对齐”。对齐属性在大体上表现一样,但不同布局方式里会有一些差异。 在MDN上,您可以深入了解 盒对齐 及其在Grid,Flexbox,多列和块布局中的实现方式。 在Smashing Magazine上,我有一篇文章专门介绍Flexbox中的对齐方式:你需要知道的有关Flexbox中对齐的所有内容

尺寸

我在2018年花了很多时间讨论了内部和外部尺寸规范,特别是它与Grid和Flexbox的关系。在web上,我们习惯于设置尺寸为长度或百分比,这就是我们如何使用浮动来制作网格类型布局的方法。然而,现代的布局方法可以为我们做很多空间分配——如果我们允许的话。值得花时间去了解Flexbox如何分配空间(或Grid fr 单元如何工作)。 在Smashing Magazine上,我写了一些关于 布局中的尺寸 的文章,也写了一些关于Flexbox的文章,比如 Flex 盒子有多大?

响应式设计

通常,新的Grid和Flexbox布局方法意味着我们可以使用比旧方法更少的媒体查询,因为它们非常灵活,可以响应视口或组件大小的变化,而无需我们更改元素的宽度。 但是,有些地方需要添加一些断点来进一步增强设计。 以下是响应式设计的一些简单指南,一般情况下,对于媒体查询,请查看我的文章《在2018年使用媒体查询进行响应式设计》。我将查看媒体查询的用途,并介绍规范4的媒体查询的新功能。

字体和排版

与布局一样,网络上的字体使用在去年发生了巨大的变化。现在,可变字体,使单个字体文件具有无限的变化。 要了解它们是什么以及它们如何工作,请观看Mandy Michael的精彩简短演讲:可变字体和网页设计的未来。 另外,我会推荐Jason Pamental动态排版与现代CSS和可变字体。 为了探索可变字体和它们的功能,微软提供了一个有趣的演示,以及一些尝试可变字体的游乐场 - Axis Praxis是最知名的(我也喜欢字体游乐场)。 MDN上的指南将证明一开始使用可变字体是非常有用的。要了解如何为不支持可变字体的浏览器实现回退解决方案,请阅读Oliver Schondorfer的《使用回退Web字体实现可变字体Firefox DevTools字体编辑器还支持使用可变字体。

变形和动画

CSS转换和动画绝对是我需要知道的基础。 我不经常需要使用它们,在使用时会忘记语法。 值得庆幸的是,MDN上的参考资料帮助了我,我建议从使用CSS变换和使用CSS动画的指南开始。 Zell Liew也有一篇很好的文章,为CSS过渡提供了很好的解释。 要发现一些可能的事情,请查看Animista网站。 关于动画可能令人困惑的事情之一是采取哪种方法。 除了CSS支持的内容之外,你可能还需要涉及JavaScript,SVG或Web Animation API,而这些事情往往都会被混为一谈。 在她的演讲中,选择你的动画冒险记录在事件中,Val Head解释了这些选项。

使用速查表作为回忆,而不是学习工具

当我提到Grid或Flexbox资源时,我经常看到回复说,如果没有特定的速查表,他们就不能使用Flexbox。我觉得把速查表作为记忆助手查找语法没有问题,我自己也出版过一些速查表。完全依赖速查表的问题是当你复制语法时,你可能会忽略为什么要这样写。然后,当你遇到属性的行为似乎不同的情况时,这种明显的不一致性似乎令人困惑,或者是语言的错误 。 如果你发现CSS在做一些非常奇怪的事情的情况下,问问为什么。创建一个简单的测试用例来强调这个问题,问问对规范更熟悉的人。我被问到的许多CSS问题都是因为人们认为属性的表现与它在现实中的表现不同。这就是为什么我经常讨论关于对齐和尺寸,因为这些地方经常会混淆。 是的,CSS中有一些奇怪的东西。它是一门经过多年进化的语言,有些东西我们无法改变,除非我们发明了时间机器。然而,一旦你掌握了一些基础知识,并且理解了为什么会这样,你就可以更轻松地处理棘手的问题。 来源:https://www.smashingmagazine.com

简介:在本教程中,您将了解JavaScript数组类型及其一些独特的特性。

JavaScript数组简介

在 JavaScript 中,数组是有序的数据列表。与其他编程语言(如Java,C / C ++等)的数组相比,数组具有以下特殊特性。

  • 首先,数组可以在每个槽中保存不同类型的数据,即数组可以保存具有数字,字符串,对象等的混合的元素。
  • 其次,数组的长度是动态调整大小和自动增长的。

创建JavaScript数组

JavaScript 为您提供了两种创建数组的方法。 第一个是使用Array构造函数如下:

var scores = new Array();

该scores 数组为空即,它不包含任何元素。 如果您知道数组将包含的元素数,则可以创建一个具有初始大小的数组,如以下示例所示:

var scores = Array(10);

要使用初始元素创建数组,可以在Array()构造函数中将元素作为逗号分隔列表传递。以下示例创建scores包含五个数字的数组:

var scores = new Array(9,10,8,7,6);

重要的是要注意,如果使用数组构造函数创建数组并传入数字,则需要创建一个具有初始大小的数组。但是,如果传入另一个类型的一个参数,则创建一个包含该元素的数组。 请参阅以下示例:

var athletes = new Array(3); // creates an array with initial size 3
var scores = new Array(1, 2, 3); // create an array with three numbers 1,2 3
var signs = new Array(‘Red’); // creates an array with one element ‘Red’

JavaScript 允许您new在使用数组构造函数时省略运算符。例如,以下语句创建artists数组。

var artists = Array();

第二种方式,也许是更优选的方式,创建一个数组是使用数组文字表示法,如下所示:

var array_name = [element1, element2, element3];

数组文字形式使用方括号[]来包装逗号分隔的元素列表。例如,以下语句创建三种颜色的colors数组:

var colors = [‘red’, ‘green’, ‘blue’];

可以使用空方括号创建一个空数组。

var emptyArray = [];

以下示例创建一个包含两个未定义元素的数组。

var nonEmptyArry = [,,];
console.log(nonEmptyArray); // [undefined, undefined]

访问 JavaScript 数组元素

您可以使用方括号[]访问数组的元素。数组的第一个元素以0开头,第二个元素以1开头,依此类推。 例如,以下示例创建一个包含三个字符串的数组。

var mountains = [‘Everest’, ‘Fuji’, ‘Nanga Parbat’];

以下语句显示如何访问mountains数组的元素。

console.log(mountains[0]); // ‘Everest’
console.log(mountains[1]); // ‘Fuji’
console.log(mountains[2]); // ‘Nanga Parbat’

要更改数组中元素的值,可以使用为该元素赋值,如下所示:

mountains[2] = ‘K2’;

数组的大小

数组使用length属性来存储它保存的当前元素数。您可以访问获取length属性的值,如以下示例所示。

console.log(mountains.length); // 3

该length属性是可写的,因此您可以 通过更改属性的值来插入或删除元素length。 如果将length属性设置为大于数组中元素数的值,则新元素将添加初始值undefined。 例如,我们将mountains数组的length属性更改为4,那么数组末尾将会附加一个初始值为undefined的元素。

// append 1 element
mountains.length = 4;
console.log(mountains[3]); // undefined

类似地,下面的示例将mountains数组的length属性设置为2,那么将会删除数组的最后两个元素。当我们访问数组的第三个元素时,它返回undefined。

// remove the last 2 elements
mountains.length = 2;
console.log(mountains[2]); // undefined

请注意,JavaScript 数组可容纳的最大元素数为4,294,967,295,足以满足典型应用程序的要求。

数组的基本操作

所有数组都是该Array类型的实例。因此,数组变量的typeof返回object如下例所示:

var seas = [‘Black Sea’, ‘Caribbean Sea’, ‘North Sea’, ‘Baltic Sea’];
console.log(typeof seas); // object

要检查变量是否为数组,可以使用Array.isArray()方法进行检测:

console.log(Array.isArray(seas)); // true

当您调用数组的toString()和valueOf()方法时,您将获得一个以逗号分隔的元素列表表示的字符串,如以下示例所示:

console.log(seas.toString()); // Black Sea,Caribbean Sea,North Sea,Baltic Sea
console.log(seas.valueOf());

如果要以不同的方式使用数组的字符串表示形式,可以使用join()方法。 以下示例使用join()方法返回mountains数组的字符串表示形式。但是元素由()字符分隔。

console.log(seas.join(‘’));
// Black SeaCaribbean SeaNorth SeaBaltic Sea

如果数组的元素是null或undefined时,toString(), valueOf()以及join()将其视为得到的字符串中一个空字符串。这是一个例子。

var mixedValues = [1, 2, null, ‘A’, undefined, 3];
console.log(mixedValues.toString()); // 1,2,,A,,3

现在您应该对JavaScript数组及其一些独特的特性有基本的了解。

正则表达式到底是什么东西?

在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。 很可能你使用过Windows/Dos下用于文件查找的通配符(wildcard),也就是*和?。如果你想查找某个目录下的所有的Word文档的话,你会搜索*.doc。在这里,*会被解释成任意的字符串。和通配符类似,正则表达式也是用来进行文本匹配的工具,只不过比起通配符,它能更精确地描述你的需求——当然,代价就是更复杂——比如你可以编写一个正则表达式,用来查找所有以0开头,后面跟着2-3个数字,然后是一个连字号“-”,最后是7或8位数字的字符串(像010-12345678或0376-7654321)。

字符是计算机软件处理文字时最基本的单位,可能是字母,数字,标点符号,空格,换行符,汉字等等。字符串是0个或更多个字符的序列。文本也就是文字,字符串。说某个字符串匹配某个正则表达式,通常是指这个字符串里有一部分(或几部分分别)能满足表达式给出的条件。

入门

学习正则表达式的最好方法是从例子开始,理解例子之后再自己对例子进行修改,实验。下面给出了不少简单的例子,并对它们作了详细的说明。 假设你在一篇英文小说里查找hi,你可以使用正则表达式hi。 这几乎是最简单的正则表达式了,它可以精确匹配这样的字符串:由两个字符组成,前一个字符是h,后一个是i。通常,处理正则表达式的工具会提供一个忽略大小写的选项,如果选中了这个选项,它可以匹配hi,HI,Hi,hI这四种情况中的任意一种。 不幸的是,很多单词里包含hi这两个连续的字符,比如him,history,high等等。用hi来查找的话,这里边的hi也会被找出来。如果要精确地查找hi这个单词的话,我们应该使用\bhi\b。 \b是正则表达式规定的一个特殊代码(好吧,某些人叫它元字符,metacharacter),代表着单词的开头或结尾,也就是单词的分界处。虽然通常英文的单词是由空格,标点符号或者换行来分隔的,但是\b并不匹配这些单词分隔字符中的任何一个,它只匹配一个位置

如果需要更精确的说法,\b匹配这样的位置:它的前一个字符和后一个字符不全是(一个是,一个不是或不存在)\w。

假如你要找的是hi后面不远处跟着一个Lucy,你应该用\bhi\b.*\bLucy\b。 这里,.是另一个元字符,匹配除了换行符以外的任意字符。*同样是元字符,不过它代表的不是字符,也不是位置,而是数量——它指定*前边的内容可以连续重复使用任意次以使整个表达式得到匹配。因此,.*连在一起就意味着任意数量的不包含换行的字符。现在\bhi\b.*\bLucy\b的意思就很明显了:先是一个单词hi,然后是任意个任意字符(但不能是换行),最后是Lucy这个单词。 如果同时使用其它元字符,我们就能构造出功能更强大的正则表达式。比如下面这个例子:

  • 0\d\d-\d\d\d\d\d\d\d\d匹配这样的字符串:以0开头,然后是两个数字,然后是一个连字号“-”,最后是8个数字(也就是中国的电话号码。当然,这个例子只能匹配区号为3位的情形)。

这里的\d是个新的元字符,匹配一位数字(0,或1,或2,或……)。-不是元字符,只匹配它本身——连字符(或者减号,或者中横线,或者随你怎么称呼它)。 为了避免那么多烦人的重复,我们也可以这样写这个表达式:0\d{2}-\d{8}。这里\d后面的{2}({8})的意思是前面\d必须连续重复匹配2次(8次)。

换行符就是’\n’,ASCII编码为10(十六进制0x0A)的字符。

测试正则表达式

如果你不觉得正则表达式很难读写的话,要么你是一个天才,要么,你不是地球人。正则表达式的语法很令人头疼,即使对经常使用它的人来说也是如此。由于难于读写,容易出错,所以找一种工具对正则表达式进行测试是很有必要的。 不同的环境下正则表达式的一些细节是不相同的,本教程介绍的是微软 .Net Framework 4.5 下正则表达式的行为,所以,我向你推荐我编写的.Net下的工具 Regester。请参考该页面的说明来安装和运行该软件。 下载与安装 你可以使用以下两种方式中的任意一个来下载安装正则表达式测试器.

元字符

现在你已经知道几个很有用的元字符了,如\b,.,*,还有\d.正则表达式里还有更多的元字符,比如\s匹配任意的空白符,包括空格,制表符(Tab),换行符,中文全角空格等。\w匹配字母或数字或下划线或汉字等。 下面来看看更多的例子:

  • \ba\w*\b匹配以字母a开头的单词——先是某个单词开始处(\b),然后是字母a,然后是任意数量的字母或数字(\w*),最后是单词结束处(\b)。
  • \d+匹配1个或更多连续的数字。这里的+是和*类似的元字符,不同的是*匹配重复任意次(可能是0次),而+则匹配重复1次或更多次。
  • \b\w{6}\b 匹配刚好6个字符的单词。

表1.常用的元字符

代码

说明

.

匹配除换行符以外的任意字符

\w

匹配字母或数字或下划线或汉字

\s

匹配任意的空白符

\d

匹配数字

\b

匹配单词的开始或结束

^

匹配字符串的开始

$

匹配字符串的结束

元字符^(和数字6在同一个键位上的符号)和$都匹配一个位置,这和\b有点类似。^匹配你要用来查找的字符串的开头,$匹配结尾。这两个代码在验证输入的内容时非常有用,比如一个网站如果要求你填写的QQ号必须为5位到12位数字时,可以使用:^\d{5,12}$。 这里的{5,12}和前面介绍过的{2}是类似的,只不过{2}匹配只能不多不少重复2次,{5,12}则是重复的次数不能少于5次,不能多于12次,否则都不匹配。 因为使用了^和$,所以输入的整个字符串都要用来和\d{5,12}来匹配,也就是说整个输入必须是5到12个数字,因此如果输入的QQ号能匹配这个正则表达式的话,那就符合要求了。 和忽略大小写的选项类似,有些正则表达式处理工具还有一个处理多行的选项。如果选中了这个选项,^和$的意义就变成了匹配行的开始处和结束处。

字符转义

如果你想查找元字符本身的话,比如你查找.,或者*,就出现了问题:你没办法指定它们,因为它们会被解释成别的意思。这时你就得使用\来取消这些字符的特殊意义。因此,你应该使用\.和\*。当然,要查找\本身,你也得用\\. 例如:deerchao\.net匹配deerchao.net,C:\\Windows匹配C:\Windows。

重复

你已经看过了前面的*,+,{2},{5,12}这几个匹配重复的方式了。下面是正则表达式中所有的限定符(指定数量的代码,例如*,{5,12}等):

表2.常用的限定符

代码/语法

说明

*

重复零次或更多次

+

重复一次或更多次

?

重复零次或一次

{n}

重复n次

{n,}

重复n次或更多次

{n,m}

重复n到m次

下面是一些使用重复的例子:

  • Windows\d+匹配Windows后面跟1个或更多数字
  • ^\w+匹配一行的第一个单词(或整个字符串的第一个单词,具体匹配哪个意思得看选项设置)

字符类

要想查找数字,字母或数字,空白是很简单的,因为已经有了对应这些字符集合的元字符,但是如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办? 很简单,你只需要在方括号里列出它们就行了,像[aeiou]就匹配任何一个英文元音字母,[.?!]匹配标点符号(.或?或!)。 我们也可以轻松地指定一个字符范围,像[0-9]代表的含意与\d就是完全一致的:一位数字;同理[a-z0-9A-Z_]也完全等同于\w(如果只考虑英文的话)。 下面是一个更复杂的表达式:\(?0\d{2}[) -]?\d{8}。 这个表达式可以匹配几种格式的电话号码,像(010)88886666,或022-22334455,或02912345678等。我们对它进行一些分析吧:首先是一个转义字符\(,它能出现0次或1次(?),然后是一个0,后面跟着2个数字(\d{2}),然后是)或-或空格中的一个,它出现1次或不出现(?),最后是8个数字(\d{8})。

分枝条件

不幸的是,刚才那个表达式也能匹配010)12345678或(022-87654321这样的“不正确”的格式。要解决这个问题,我们需要用到分枝条件。正则表达式里的分枝条件指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用把不同的规则分隔开。听不明白?没关系,看例子: 0\d{2}-\d{8}0\d{3}-\d{7}这个表达式能匹配两种以连字号分隔的电话号码:一种是三位区号,8位本地号(如010-12345678),一种是4位区号,7位本地号(0376-2233445)。 \(0\d{2}\)[- ]?\d{8}0\d{2}[- ]?\d{8}这个表达式匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔。你可以试试用分枝条件把这个表达式扩展成也支持4位区号的。 \d{5}-\d{4}\d{5}这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题:使用分枝条件时,要注意各个条件的顺序。如果你把它改成\d{5}\d{5}-\d{4}的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了。

分组

我们已经提到了怎么重复单个字符(直接在字符后面加上限定符就行了);但如果想要重复多个字符又该怎么办?你可以用小括号来指定子表达式(也叫做分组),然后你就可以指定这个子表达式的重复次数了,你也可以对子表达式进行其它一些操作(后面会有介绍)。 (\d{1,3}\.){3}\d{1,3}是一个简单的IP地址匹配表达式。要理解这个表达式,请按下列顺序分析它:\d{1,3}匹配1到3位的数字,(\d{1,3}\.){3}匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复3次,最后再加上一个一到三位的数字(\d{1,3})。 不幸的是,它也将匹配256.300.888.999这种不可能存在的IP地址。如果能使用算术比较的话,或许能简单地解决这个问题,但是正则表达式中并不提供关于数学的任何功能,所以只能使用冗长的分组,选择,字符类来描述一个正确的IP地址:((2[0-4]\d25[0-5][01]?\d\d?)\.){3}(2[0-4]\d25[0-5][01]?\d\d?)。 理解这个表达式的关键是理解2[0-4]\d25[0-5][01]?\d\d?,这里我就不细说了,你自己应该能分析得出来它的意义。

反义

有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外,其它任意字符都行的情况,这时需要用到反义:

表3.常用的反义代码

代码/语法

说明

\W

匹配任意不是字母,数字,下划线,汉字的字符

\S

匹配任意不是空白符的字符

\D

匹配任意非数字的字符

\B

匹配不是单词开头或结束的位置

[^x]

匹配除了x以外的任意字符

[^aeiou]

匹配除了aeiou这几个字母以外的任意字符

例子:\S+匹配不包含空白符的字符串。 <a[^>]+>匹配用尖括号括起来的以a开头的字符串。

后向引用

使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。 后向引用用于重复搜索前面某个分组匹配的文本。例如,\1代表分组1匹配的文本。难以理解?请看示例: \b(\w+)\b\s+\1\b可以用来匹配重复的单词,像go go, 或者kitty kitty。这个表达式首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字(\b(\w+)\b),这个单词会被捕获到编号为1的分组中,然后是1个或几个空白符(\s+),最后是分组1中捕获的内容(也就是前面匹配的那个单词)(\1)。 你也可以自己指定子表达式的组名。要指定一个子表达式的组名,请使用这样的语法:(?\w+)(或者把尖括号换成’也行:(?’Word’\w+)),这样就把\w+的组名指定为Word了。要反向引用这个分组捕获的内容,你可以使用\k,所以上一个例子也可以写成这样:\b(?\w+)\b\s+\k\b。 使用小括号的时候,还有很多特定用途的语法。下面列出了最常用的一些:

表4.常用分组语法

分类

代码/语法

说明

捕获

(exp)

匹配exp,并捕获文本到自动命名的组里

(?exp)

匹配exp,并捕获文本到名称为name的组里,也可以写成(?’name’exp)

(?:exp)

匹配exp,不捕获匹配的文本,也不给此分组分配组号

零宽断言

(?=exp)

匹配exp前面的位置

(?<=exp)

匹配exp后面的位置

(?!exp)

匹配后面跟的不是exp的位置

(?<!exp)

匹配前面不是exp的位置

注释

(?#comment)

这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读

我们已经讨论了前两种语法。第三个(?:exp)不会改变正则表达式的处理方式,只是这样的组匹配的内容不会像前两种那样被捕获到某个组里面,也不会拥有组号。“我为什么会想要这样做?”——好问题,你觉得为什么呢?

零宽断言

接下来的四个用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像\b,^,$那样用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言。最好还是拿例子来说明吧: (?=exp)也叫零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式exp。比如\b\w+(?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找I’m singing while you’re dancing.时,它会匹配sing和danc。 (?<=exp)也叫零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式exp。比如(?<=\bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading。 假如你想要给一个很长的数字中每三位间加一个逗号(当然是从右边加起了),你可以这样查找需要在前面和里面添加逗号的部分:((?<=\d)\d{3})+\b,用它对1234567890进行查找时结果是234567890。 下面这个例子同时使用了这两种断言:(?<=\s)\d+(?=\s)匹配以空白符间隔的数字(再次强调,不包括这些空白符)。

负向零宽断言

前面我们提到过怎么查找不是某个字符或不在某个字符类里的字符的方法(反义)。但是如果我们只是想要确保某个字符没有出现,但并不想去匹配它时怎么办?例如,如果我们想查找这样的单词–它里面出现了字母q,但是q后面跟的不是字母u,我们可以尝试这样: \b\w*q[^u]\w*\b匹配包含后面不是字母u的字母q的单词。但是如果多做测试(或者你思维足够敏锐,直接就观察出来了),你会发现,如果q出现在单词的结尾的话,像Iraq,Benq,这个表达式就会出错。这是因为[^u]总要匹配一个字符,所以如果q是单词的最后一个字符的话,后面的[^u]将会匹配q后面的单词分隔符(可能是空格,或者是句号或其它的什么),后面的\w*\b将会匹配下一个单词,于是\b\w*q[^u]\w*\b就能匹配整个Iraq fighting。负向零宽断言能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。现在,我们可以这样来解决这个问题:\b\w*q(?!u)\w*\b。 零宽度负预测先行断言(?!exp),断言此位置的后面不能匹配表达式exp。例如:\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字;\b((?!abc)\w)+\b匹配不包含连续字符串abc的单词。 同理,我们可以用(?<!exp),零宽度负回顾后发断言来断言此位置的前面不能匹配表达式exp:(?<![a-z])\d{7}匹配前面不是小写字母的七位数字。 一个更复杂的例子:(?<=<(\w+)>).*(?=<\/\1>)匹配不包含属性的简单HTML标签内里的内容。(?<=<(\w+)>)指定了这样的前缀:被尖括号括起来的单词(比如可能是),然后是.*(任意的字符串),最后是一个后缀(?=<\/\1>)。注意后缀里的\/,它用到了前面提过的字符转义;\1则是一个反向引用,引用的正是捕获的第一组,前面的(\w+)匹配的内容,这样如果前缀实际上是的话,后缀就是了。整个表达式匹配的是之间的内容(再次提醒,不包括前缀和后缀本身)。

注释

小括号的另一种用途是通过语法(?#comment)来包含注释。例如:2[0-4]\d(?#200-249)25[0-5](?#250-255)[01]?\d\d?(?#0-199)。 要包含注释的话,最好是启用“忽略模式里的空白符”选项,这样在编写表达式时能任意的添加空格,Tab,换行,而实际使用时这些都将被忽略。启用这个选项后,在#后面到这一行结束的所有文本都将被当成注释忽略掉。例如,我们可以前面的一个表达式写成这样:

(?<= # 断言要匹配的文本的前缀
<(\w+)> # 查找尖括号括起来的字母或数字(即HTML/XML标签)
) # 前缀结束
.* # 匹配任意文本
(?= # 断言要匹配的文本的后缀
<\/\1> # 查找尖括号括起来的内容:前面是一个”/“,后面是先前捕获的标签
) # 后缀结束

贪婪与懒惰

当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。以这个表达式为例:a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配。 有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。现在看看懒惰版的例子吧: a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab(第一到第三个字符)和ab(第四到第五个字符)。

表5.懒惰限定符

代码/语法

说明

*?

重复任意次,但尽可能少重复

+?

重复1次或更多次,但尽可能少重复

??

重复0次或1次,但尽可能少重复

{n,m}?

重复n到m次,但尽可能少重复

{n,}?

重复n次以上,但尽可能少重复

处理选项

上面介绍了几个选项如忽略大小写,处理多行等,这些选项能用来改变处理正则表达式的方式。下面是.Net中常用的正则表达式选项:

表6.常用的处理选项

名称

说明

IgnoreCase(忽略大小写)

匹配时不区分大小写。

Multiline(多行模式)

更改^和$的含义,使它们分别在任意一行的行首和行尾匹配,而不仅仅在整个字符串的开头和结尾匹配。(在此模式下,$的精确含意是:匹配\n之前的位置以及字符串结束前的位置.)

Singleline(单行模式)

更改.的含义,使它与每一个字符匹配(包括换行符\n)。

IgnorePatternWhitespace(忽略空白)

忽略表达式中的非转义空白并启用由#标记的注释。

ExplicitCapture(显式捕获)

仅捕获已被显式命名的组。

一个经常被问到的问题是:是不是只能同时使用多行模式和单行模式中的一种?答案是:不是。这两个选项之间没有任何关系,除了它们的名字比较相似(以至于让人感到疑惑)以外。

平衡组/递归匹配

有时我们需要匹配像( 100 * ( 50 + 15 ) )这样的可嵌套的层次性结构,这时简单地使用\(.+\)则只会匹配到最左边的左括号和最右边的右括号之间的内容(这里我们讨论的是贪婪模式,懒惰模式也有下面的问题)。假如原来的字符串里的左括号和右括号出现的次数不相等,比如( 5 / ( 3 + 2 ) ) ),那我们的匹配结果里两者的个数也不会相等。有没有办法在这样的字符串里匹配到最长的,配对的括号之间的内容呢? 为了避免(和\(把你的大脑彻底搞糊涂,我们还是用尖括号代替圆括号吧。现在我们的问题变成了如何把xx <aa aa> yy这样的字符串里,最长的配对的尖括号内的内容捕获出来? 这里需要用到以下的语法构造:

  • (?’group’) 把捕获的内容命名为group,并压入堆栈(Stack)
  • (?’-group’) 从堆栈上弹出最后压入堆栈的名为group的捕获内容,如果堆栈本来为空,则本分组的匹配失败
  • (?(group)yesno) 如果堆栈上存在以名为group的捕获内容的话,继续匹配yes部分的表达式,否则继续匹配no部分
  • (?!) 零宽负向先行断言,由于没有后缀表达式,试图匹配总是失败

我们需要做的是每碰到了左括号,就在压入一个”Open”,每碰到一个右括号,就弹出一个,到了最后就看看堆栈是否为空--如果不为空那就证明左括号比右括号多,那匹配就应该失败。正则表达式引擎会进行回溯(放弃最前面或最后面的一些字符),尽量使整个表达式得到匹配。

< #最外层的左括号
[^<>]* #最外层的左括号后面的不是括号的内容
(
(
(?’Open’<) #碰到了左括号,在黑板上写一个”Open”
[^<>]* #匹配左括号后面的不是括号的内容
)+
(
(?’-Open’>) #碰到了右括号,擦掉一个”Open”
[^<>]* #匹配右括号后面不是括号的内容
)+
)*
(?(Open)(?!)) #在遇到最外层的右括号前面,判断黑板上还有没有没擦掉的”Open”;如果还有,则匹配失败

                    #最外层的右括号

平衡组的一个最常见的应用就是匹配HTML,下面这个例子可以匹配嵌套的

标签:<div[^>]*>[^<>]*(((?’Open’<div[^>]*>)[^<>]*)+((?’-Open’
)[^<>]*)+)*(?(Open)(?!))
.

还有些什么东西没提到

上边已经描述了构造正则表达式的大量元素,但是还有很多没有提到的东西。下面是一些未提到的元素的列表,包含语法和简单的说明。你可以在网上找到更详细的参考资料来学习它们–当你需要用到它们的时候。如果你安装了MSDN Library,你也可以在里面找到.net下正则表达式详细的文档。这里的介绍很简略,如果你需要更详细的信息,而又没有在电脑上安装MSDN Library,可以查看关于正则表达式语言元素的MSDN在线文档

表7.尚未详细讨论的语法

代码/语法

说明

\a

报警字符(打印它的效果是电脑嘀一声)

\b

通常是单词分界位置,但如果在字符类里使用代表退格

\t

制表符,Tab

\r

回车

\v

竖向制表符

\f

换页符

\n

换行符

\e

Escape

\0nn

ASCII代码中八进制代码为nn的字符

\xnn

ASCII代码中十六进制代码为nn的字符

\unnnn

Unicode代码中十六进制代码为nnnn的字符

\cN

ASCII控制字符。比如\cC代表Ctrl+C

\A

字符串开头(类似^,但不受处理多行选项的影响)

\Z

字符串结尾或行尾(不受处理多行选项的影响)

\z

字符串结尾(类似$,但不受处理多行选项的影响)

\G

当前搜索的开头

\p{name}

Unicode中命名为name的字符类,例如\p{IsGreek}

(?>exp)

贪婪子表达式

(?-exp)

平衡组

(?im-nsx:exp)

在子表达式exp中改变处理选项

(?im-nsx)

为表达式后面的部分改变处理选项

(?(exp)yesno)

把exp当作零宽正向先行断言,如果在这个位置能匹配,使用yes作为此组的表达式;否则使用no

(?(exp)yes)

同上,只是使用空表达式作为no

(?(name)yesno)

如果命名为name的组捕获到了内容,使用yes作为表达式;否则使用no

(?(name)yes)

同上,只是使用空表达式作为no

网上的资源及本文参考文献

来源:http://deerchao.net/tutorials/regex/regex.htm