前端开发 大前端 W3Cbest

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

0%

在 CSS 中对元素进行水平居中是非常简单的:如果它是一个行内元素,就对它的父元素应用 text-align: center;如果它是一个块级元素,就对它自身应用 margin: auto。然而如果要对一个元素进行垂直居中,可能光是想想就令人头皮发麻了。 多年以来,垂直居中已经成为了 CSS 领域的圣杯,它同样也是前端开发圈内广为流传的笑话。原因在于它同时具备以下几条特征。

  • 它是极其常见的需求。
  • 从理论上来看,它似乎极其简单。
  • 在实践中,它往往难如登天,当涉及尺寸不固定的元素时尤其如此。

长久以来,为了解决这一绝世难题,前端开发者们殚精竭虑,琢磨出了各种解决方法,大多数并不实用。在本篇攻略中,我们将探索现代 CSS 的强大威力,以全新的思路去攻克各种场景下的垂直居中难题。请注意,有几种技巧十分流行,但在这里并不会深入探讨,原因如下。

  • 表格布局法(利用表格的显示模式)需要用到一些冗余的 HTML 元素,因此这里不多介绍。
  • 行内块法也不作讨论,因为在我看来这种方法 hack 的味道很浓。

如果你有兴趣,可以去看看 Chris Coyier 写的“不为人知的居中方法”(http://css-tricks.com/centering-in-the-unknown)。这篇出色的文章详细讲述了这两种技巧。 除非特别注明,我们将一直使用如下所示的结构代码,并直接插入 元素中(但实际上我们将要探索的这些技巧是与容器无关的):

我居中了吗?

然后再用一些基本的 CSS 来设置背景、内边距等样式,运行一下就可以看到效果了。我们将以此作为起点。  

基于绝对定位的解决方案

我们先来看一个早期的垂直居中方法,它要求元素具有固定的宽度和高度:

main {
  position: absolute;
  top: 50%;
  left: 50%;
  margin-top: -3em; /* 6/2 = 3 */
  margin-left: -9em; /* 18/2 = 9 */
  width: 18em;
  height: 6em;
}

这段代码在本质上做了这样几件事情:先把这个元素的左上角放置在视口(或最近的、具有定位属性的祖先元素)的正中心,然后再利用负外边距把它向左、向上移动(移动距离相当于它自身宽高的一半),从而把元素的正中心放置在视口的正中心。借助强大的 calc() 函数,这段代码还可以省掉两行声明:

main {
  position: absolute;
  top: calc(50% - 3em);
  left: calc(50% - 9em);
  width: 18em;
  height: 6em;
}

显然,这个方法最大的局限在于它要求元素的宽高是固定的。在通常情况下,对那些需要居中的元素来说,其尺寸往往是由其内容来决定的。如果能找到一个属性的百分比值以元素自身的宽高作为解析基准,那我们的难题就迎刃而解了!遗憾的是,对于绝大多数 CSS 属性(包括 margin)来说,百分比都是以其父元素的尺寸为基准进行解析的。 CSS 领域有一个很常见的现象,真正的解决方案往往来自于我们最意想不到的地方。在这个例子中,答案来自于 CSS 变形属性。当我们在translate() 变形函数中使用百分比值时,是以这个元素自身的宽度和高度为基准进行换算和移动的,而这正是我们所需要的。接下来,只要换用基于百分比的 CSS 变形来对元素进行偏移,就不需要在偏移量中把元素的尺寸写死了。这样我们就可以彻底解除对固定尺寸的依赖:

main {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

你可以在下面demo中看到结果:这个容器已经完美居中了,完全满足我们的期望。 当然,没有任何技巧是十全十美的,上面这个方法也有一些需要注意的地方。

  • 我们有时不能选用绝对定位,因为它对整个布局的影响太过强烈。
  • 如果需要居中的元素已经在高度上超过了视口,那它的顶部会被视口裁切掉。有一些办法可以绕过这个问题,但 hack味道过浓。
  • 在某些浏览器中,这个方法可能会导致元素的显示有一些模糊,因为元素可能被放置在半个像素上。这个问题可以用 transformstyle:preserve-3d 来修复,不过这个修复手段也可以认为是一个hack,而且很难保证它在未来不会出问题。

实践证明,想要找到最先提出这个实用技巧的人确实不容易,不过所能挖掘到的最早起源似乎是 Stack Overflow(http://stackoverflow.com)的用户“Charlie”(http://stackoverflow.com/users/479836/charlie),他在2013年4月16日回答了“如何使用 CSS3 实现垂直对齐”(http://stackoverflow.com/a/16026893/90826)这个问题。

基于视口单位的解决方案

假设我们不想使用绝对定位,仍然可以采用 translate() 技巧来把这个元素以其自身宽高的一半为距离进行移动;但是在缺少 left 和 top 的情况下,如何把这个元素的左上角放置在容器的正中心呢? 我们的第一反应很可能是用 margin 属性的百分比值来实现,就像这样:

main {
  width: 18em;
  padding: 1em 1.5em;
  margin: 50% auto 0;
  transform: translateY(-50%);
}

不过,这段代码会产生十分离谱的结果。原因在于margin 的百分比值是以父元素的宽度作为解析基准的。没错,即使对于margin-top 和 margin-bottom 来说也是这样! 不过幸运的是,如果只是想把元素相对于视口进行居中,仍然是有希望 的。CSS 值 与 单 位( 第 三 版 )(http://w3.org/TR/css-values-3/#viewportrelative-lengths)定义了一套新的单位,称为视口相关的长度单位。

  • vw 是与视口宽度相关的。与常人的直觉不符的是,1vw 实际上表示视口宽度的 1%,而不是 100%。
  • 与 vw 类似,1vh 表示视口高度的 1%。
  • 当视口宽度小于高度时,1vmin 等于 1vw,否则等于 1vh。
  • 当视口宽度大于高度时,1vmax 等于 1vw,否则等于 1vh。

在我们的这个例子中,适用于外边距的是 vh 单位:

main {
  width: 18em;
  padding: 1em 1.5em;
  margin: 50vh auto 0;
  transform: translateY(-50%);
}

“使用视口相关的长度单位,我们还可以生成一个正好铺满视口的区块,无需脚本的辅助。更多细节请参阅“用一行 CSS 实现全屏区块”(https://medium.com/@ckor/make-full-screensections-with-1-line-of-css-b82227c75cbd)。”

在下面DEMO中可以看到,其效果堪称完美。当然,这个技巧的实用性是相当有限的,因为它只适用于在视口中居中的场景。

基于 Flexbox 的解决方案

这是毋庸置疑的最佳解决方案,因为 Flexbox(伸缩盒)(http://w3.org/TR/css-flexbox)是专门针对这类需求所设计的。我们之所以要讨论其他方案,仅仅是因为那些方案在浏览器的支持程度上稍微好一些而已。其实目前现代浏览器对 Flexbox 的支持度已经相当不错了。 我们只需写两行声明即可:先给这个待居中元素的父元素设置 display:flex(在这个例子中是 元素),再给这个元素自身设置我们再熟悉不过的 margin: auto(在这个例子中是

元素):

body {
  display: flex;
  min-height: 100vh;
  margin: 0;
}
main {
  margin: auto;
}

请注意,当我们使用 Flexbox 时,margin: auto 不仅在水平方向上将元素居中,垂直方向上也是如此。还有一点,我们甚至不需要指定任何宽度(当然,如果需要的话,也是可以指定的):这个居中元素分配到的宽度等于 maxcontent。(还记得“自适应内部元素”中提到的那些内部尺寸关键字吗?) 如果浏览器不支持 Flexbox,页面渲染结果看起来就跟我们的开篇代码是一样的了(如果设置了宽度的话)。虽然没有垂直居中效果,但也是完全可以接受的。 Flexbox 的另一个好处在于,它还可以将匿名容器(即没有被标签包裹的文本节点)垂直居中。举个例子,假设我们的结构代码是:

我居中了吗?

我们先给这个 main 元素指定一个固定的尺寸,然后借助 Flexbox 规范所引入的 align-items 和 justify-content 属性,我们可以让它内部的文本也实现居中(参见图 7-22):

main {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18em;
  height: 10em;
}

根据盒对齐模型(第三版)(http://w3.org/TR/css-align-3)的计划,在未来,对于简单的垂直居中需求,我们完全不需要动用特殊的布局模式了。因为只需要下面这行代码就可以搞定: align-self: center; 不管这个元素上还应用了其他什么属性,这样写就够了。这听起来可能如美梦一般令人难以置信,但或许你手边的浏览器很快就会让它成为现实!

相关规范

在过去的几个月,我发现我的拉取请求中存在四个完全相同的 JavaScript 错误。于是我写了这篇文章,总结了如何在 JavaScript 中正确使用地使用 Array 的方法!

Array.includes 代替 Array.indexOf

“如果你要在数组中查找元素,请使用 Array.indexOf”。我记得在学习 JavaScript 的时候,在教材中读到这样的一句话。毫无疑问,这句话是真的! MDN 文档写道,Array.indexOf 将“返回第一次出现给定元素的索引”。因此,如果我们稍后要在代码中使用这个返回的索引,那么使用 Array.indexOf 找到索引就对了。 但是,如果我们只想知道数组是否包含某个值,该怎么办?这似乎是一个是与否的问题,或者说是一个布尔值问题。对于这种情况,我建议使用返回布尔值的 Array.includes

‘use strict’;

const characters = [
‘ironman’,
‘black_widow’,
‘hulk’,
‘captain_america’,
‘hulk’,
‘thor’,
];

console.log(characters.indexOf(‘hulk’));
// 2
console.log(characters.indexOf(‘batman’));
// -1

console.log(characters.includes(‘hulk’));
// true
console.log(characters.includes(‘batman’));
// false

使用 Array.find 而不是 Array.filter

Array.filter 是一个非常有用的方法。它接受一个回调函数作为参数,基于一个包含所有元素的数组创建出一个新的数组。正如它的名字一样,我们使用这个方法来过滤元素,获得更短的数组。 但是,如果回调函数只能返回一个元素,那么我就不推荐使用这个方法,例如使用回调函数来过滤唯一 ID。在这种情况下,Array.includes 将返回一个只包含一个元素的新数组。我们的意图可能是通过查找特定的 ID 找到数组中包含的唯一值。 我们来看看这个方法的性能。要返回与回调函数匹配的所有元素,Array.filter 必须遍历整个数组。此外,我们假设有数百个元素可以满足回调参数,那么过滤后的数组会非常大。 为了避免这种情况,我建议使用 Array.find。它需要一个像 Array.filter 一样的回调函数作为参数,并返回满足回调函数的第一个元素的值。此外,只要找到第一个满足回调函数的元素,Array.find 就会停止,无需遍历整个数组。通过 Array.find 来查找元素,我们可以更好地理解我们的意图。

‘use strict’;

const characters = [
{ id: 1, name: ‘ironman’ },
{ id: 2, name: ‘black_widow’ },
{ id: 3, name: ‘captain_america’ },
{ id: 4, name: ‘captain_america’ },
];

function getCharacter(name) {
return character => character.name === name;
}

console.log(characters.filter(getCharacter(‘captain_america’)));
// [
// { id: 3, name: ‘captain_america’ },
// { id: 4, name: ‘captain_america’ },
// ]

console.log(characters.find(getCharacter(‘captain_america’)));
// { id: 3, name: ‘captain_america’ }

Array.some 代替 Array.find

我承认这个错误我犯了很多次。然后,一位善良的朋友告诉我,最好可以先参考 MDN 文档。这与上面的 Array.indexOf/Array.includes 非常相似。 在前面的例子中,我们看到 Array.find 需要一个回调函数作为参数,并返回一个元素。如果我们想要知道数组是否包含某个值,Array.find 是最好的解决方案吗?可能不是,因为它返回的是一个元素值,而不是一个布尔值。 对于这种情况,我建议使用 Array.some,它返回所需的布尔值。另外,从语义上看,Array.some 表示我们只想知道某个元素是否存在,而不需要得到这个元素。

‘use strict’;

const characters = [
{ id: 1, name: ‘ironman’, env: ‘marvel’ },
{ id: 2, name: ‘black_widow’, env: ‘marvel’ },
{ id: 3, name: ‘wonder_woman’, env: ‘dc_comics’ },
];

function hasCharacterFrom(env) {
return character => character.env === env;
}

console.log(characters.find(hasCharacterFrom(‘marvel’)));
// { id: 1, name: ‘ironman’, env: ‘marvel’ }

console.log(characters.some(hasCharacterFrom(‘marvel’)));
// true

使用 Array.reduce 而不是链接 Array.filterArray.map

让我们面对现实吧,Array.reduce 不容易理解。事实确实如此!但是,如果我们使用 Array.filter 和 Array.map的组合,感觉缺少了什么,对吧? 我的意思是,我们遍历了两次数组。第一次过滤数组并创建一个较短的数组,第二次又基于 Array.filter 获得数组创建一个包含新值的数组。为了获得我们想要的新数组,我们使用了两个 Array 方法。每个方法都有自己的回调函数和一个用不到的数组——由 Array.filter 创建的那个数组。 为了避免这种性能损耗,我的建议是使用 Array.reduce。结果是一样的,代码却更简单! 我们可以使用 Array.reduce 进行过滤,并将目标元素添加到累加器中。累加器可以是递增的数字、要填充的对象、要连接的字符串或数组。 在我们的例子中,因为之前使用了 Array.map,所以我建议使用 Array.reduce 将满足条件的数组元素加入到累加器中。在下面的示例中,根据 env 值的具体情况,我们将它添加到累加器中或保持累加器不变。

‘use strict’;

const characters = [
{ name: ‘ironman’, env: ‘marvel’ },
{ name: ‘black_widow’, env: ‘marvel’ },
{ name: ‘wonder_woman’, env: ‘dc_comics’ },
];

console.log(
characters
.filter(character => character.env === ‘marvel’)
.map(character => Object.assign({}, character, { alsoSeenIn: [‘Avengers’] }))
);
// [
// { name: ‘ironman’, env: ‘marvel’, alsoSeenIn: [‘Avengers’] },
// { name: ‘black_widow’, env: ‘marvel’, alsoSeenIn: [‘Avengers’] }
// ]

console.log(
characters
.reduce((acc, character) => {
return character.env === ‘marvel’
? acc.concat(Object.assign({}, character, { alsoSeenIn: [‘Avengers’] }))
acc;
}, [])
)
// [
// { name: ‘ironman’, env: ‘marvel’, alsoSeenIn: [‘Avengers’] },
// { name: ‘black_widow’, env: ‘marvel’, alsoSeenIn: [‘Avengers’] }
// ]

英文原文 https://medium.freecodecamp.org/heres-how-you-can-make-better-use-of-javascript-arrays-3efd6395af3c

关于background-attachment 也不算是一个比较生僻的属性,基本上在做背景图片固定特别是全屏页面时用时就会用到。

概述节

如果指定了 background-image ,那么 background-attachment 决定背景是在视口中固定的还是随包含它的区块滚动的。

语法

background-attachment: scroll;
background-attachment: fixed;
background-attachment: local;
background-attachment: inherit;

取值节

fixed

此关键字表示背景相对于视口固定。即使一个元素拥有滚动机制,背景也不会随着元素的内容滚动。

local

此关键字表示背景相对于元素的内容固定。如果一个元素拥有滚动机制,背景将会随着元素的内容滚动,且背景的绘制区域和定位区域是相对于可滚动的区域而不是包含他们的边框。

scroll

此关键字表示背景相对于元素本身固定, 而不是随着它的内容滚动(对元素边框是有效的)。

注意: fixed是相对视口固定,而scroll是相对元素本身固定,它和 position 定位的 absolute 和 fixed有点像。

 

简单的例子

section {
background-image: url(“https://picjumbo.com/wp-content/uploads/christmas-backgrounds-2210x1473.jpg");
background-attachment: fixed;
}

子曰:“学而时习之,不亦说乎?有朋自远方来,不亦乐乎?人不知而不愠,不亦君子乎?”

有子曰:“其为人也孝弟,而好犯上者,鲜矣;不好犯上而好作乱者,未之有也。君子务本,本立而道生。孝弟也者,其为仁之本与!”

子曰:“巧言令色,鲜矣仁!”

曾子曰:“吾日三省吾身:为人谋而不忠乎?与朋友交而不信乎?传不习乎?”

子曰:“道千乘之国,敬事而信,节用而爱人,使民以时。”

子曰:“弟子入则孝,出则弟,谨而信,泛爱众,而亲仁,行有余力,则以学文。”

 

See the Pen 单背景支持 by xianzhiding (@xianzhiding) on CodePen.

多背景图支持

此属性支持多张背景图片。你可以用逗号分隔来为每一张背景图片指定不同的。每一张背景图片顺序对应相应的attachment 类型。

section{
background-image: url(“https://picjumbo.com/wp-content/uploads/gingerbread-decorating-1080x1620.jpg"), url(“https://picjumbo.com/wp-content/uploads/woman-working-in-modern-office-space-2210x3315.jpg");
background-attachment: fixed, scroll;
background-repeat: no-repeat, repeat-y;
}

子曰:“学而时习之,不亦说乎?有朋自远方来,不亦乐乎?人不知而不愠,不亦君子乎?”

有子曰:“其为人也孝弟,而好犯上者,鲜矣;不好犯上而好作乱者,未之有也。君子务本,本立而道生。孝弟也者,其为仁之本与!”

子曰:“巧言令色,鲜矣仁!”

曾子曰:“吾日三省吾身:为人谋而不忠乎?与朋友交而不信乎?传不习乎?”

子曰:“道千乘之国,敬事而信,节用而爱人,使民以时。”

子曰:“弟子入则孝,出则弟,谨而信,泛爱众,而亲仁,行有余力,则以学文。”

See the Pen 多背景图支持节 by xianzhiding (@xianzhiding) on CodePen.

使用 fixed 实现多模块背景固定效果

fixed 此关键字表示背景相对于视口固定。即使一个元素拥有滚动机制,背景也不会随着元素的内容滚动。也就是说,背景图从一开始就已经被固定死在初始所在的位置。  

See the Pen 视差效果 by w3cbest.com (@w3cbest)on CodePen.

本文将通过简单的术语和真实世界的例子解释 JavaScript 中 this 及其用途,并告诉你写出好的代码为何如此重要。

this 适合你吗?

我看到许多文章在介绍 JavaScript 的 this 时都会假设你学过某种面向对象的编程语言,比如 Java、C++ 或 Python 等。但这篇文章面向的读者是那些不知道 this 是什么的人。我尽量不用任何术语来解释 this 是什么,以及 this 的用法。 也许你一直不敢解开 this 的秘密,因为它看起来挺奇怪也挺吓人的。或许你只在 StackOverflow 说你需要用它的时候(比如在 React 里实现某个功能)才会使用。 在深入介绍 this 之前,我们首先需要理解函数式编程和面向对象编程之间的区别。  

函数式编程 vs 面向对象编程

你可能不知道,JavaScript 同时拥有面向对象和函数式的结构,所以你可以自己选择用哪种风格,或者两者都用。 我在很早以前使用 JavaScript 时就喜欢函数式编程,而且会像躲避瘟疫一样避开面向对象编程,因为我不理解面向对象中的关键字,比如 this。我不知道为什么要用 this。似乎没有它我也可以做好所有的工作。 而且我是对的。 在某种意义上 。也许你可以只专注于一种结构并且完全忽略另一种,但这样你只能是一个 JavaScript 开发者。为了解释函数式和面向对象之间的区别,下面我们通过一个数组来举例说明,数组的内容是 Facebook 的好友列表。 假设你要做一个 Web 应用,当用户使用 Facebook 登录你的 Web 应用时,需要显示他们的 Facebook 的好友信息。你需要访问 Facebook 并获得用户的好友数据。这些数据可能是 firstName、lastName、username、numFriends、friendData、birthday 和 lastTenPosts 等信息。

const data = [
{
firstName: ‘Bob’,
lastName: ‘Ross’,
username: ‘bob.ross’,
numFriends: 125,
birthday: ‘2/23/1985’,
lastTenPosts: [‘What a nice day’, ‘I love Kanye West’, …],
},

]

假设上述数据是你通过 Facebook API 获得的。现在需要将其转换成方便你的项目使用的格式。我们假设你想显示的好友信息如下:

  • 姓名,格式为`${firstName} ${lastName}`
  • 三篇随机文章
  • 距离生日的天数

 

函数式方式

函数式的方式就是将整个数组或者数组中的某个元素传递给某个函数,然后返回你需要的信息:

const fullNames = getFullNames(data)
// [‘Ross, Bob’, ‘Smith, Joanna’, …]

首先我们有 Facebook API 返回的原始数据。为了将其转换成需要的格式,首先要将数据传递给一个函数,函数的输出是(或者包含)经过修改的数据,这些数据可以在应用中向用户展示。 我们可以用类似的方法获得随机三篇文章,并且计算距离好友生日的天数。 函数式的方式是:将原始数据传递给一个函数或者多个函数,获得对你的项目有用的数据格式。  

面向对象的方式

对于编程初学者和 JavaScript 初学者,面向对象的概念可能有点难以理解。其思想是,我们要将每个好友变成一个对象,这个对象能够生成你一切开发者需要的东西。 你可以创建一个对象,这个对象对应于某个好友,它有 fullName 属性,还有两个函数 getThreeRandomPosts 和 getDaysUntilBirthday。

function initializeFriend(data) {
return {
fullName: `${data.firstName} ${data.lastName}`,
getThreeRandomPosts: function() {
// get three random posts from data.lastTenPosts
},
getDaysUntilBirthday: function() {
// use data.birthday to get the num days until birthday
}
};
}
const objectFriends = data.map(initializeFriend)
objectFriends[0].getThreeRandomPosts()
// Gets three of Bob Ross’s posts

面向对象的方式就是为数据创建对象,每个对象都有自己的状态,并且包含必要的信息,能够生成需要的数据。  

这跟 this 有什么关系?

你也许从来没想过要写上面的 initializeFriend 代码,而且你也许认为,这种代码可能会很有用。但你也注意到,这并不是真正的面向对象。 其原因就是,上面例子中的 getThreeRandomPosts 或 getdaysUntilBirtyday 能够正常工作的原因其实是闭包。因为使用了闭包,它们在 initializeFriend 返回之后依然能访问 data。关于闭包的更多信息可以看看这篇文章:作用域和闭包(https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch5.md)。 还有一个方法该怎么处理?我们假设这个方法叫做 greeting。注意方法(与 JavaScript 的对象有关的方法)其实只是一个属性,只不过属性值是函数而已。我们想在 greeting 中实现以下功能:

function initializeFriend(data) {
return {
fullName: `${data.firstName} ${data.lastName}`,
getThreeRandomPosts: function() {
// get three random posts from data.lastTenPosts
},
getDaysUntilBirthday: function() {
// use data.birthday to get the num days until birthday
},
greeting: function() {
return `Hello, this is ${fullName}’s data!`
}
};
}

这样能正常工作吗? 不能! 我们新建的对象能够访问 initializeFriend 中的一切变量,但不能访问这个对象本身的属性或方法。当然你会问,

难道不能在 greeting 中直接用 data.firstName 和 data.lastName 吗?

当然可以。但要是想在 greeting 中加入距离好友生日的天数怎么办?我们最好还是有办法在 greeting 中调用 getDaysUntilBirthday。 这时轮到 this 出场了!  

终于——this 是什么

this 在不同的环境中可以指代不同的东西。默认的全局环境中 this 指代的是全局对象(在浏览器中 this 是 window 对象),这没什么太大的用途。而在 this 的规则中具有实用性的是这一条: 如果在对象的方法中使用 this,而该方法在该对象的上下文中调用,那么 this 指代该对象本身。

你会说“在该对象的上下文中调用”……是啥意思?

别着急,我们一会儿就说。 所以,如果我们想从 greeting 中调用 getDaysUntilBirtyday 我们只需要写 this.getDaysUntilBirthday,因为此时的 this 就是对象本身。 附注:不要在全局作用域的普通函数或另一个函数的作用域中使用 this!this 是个面向对象的东西,它只在对象的上下文(或类的上下文)中有意义。 我们利用 this 来重写 initializeFriend:

function initializeFriend(data) {
return {
lastTenPosts: data.lastTenPosts,
birthday: data.birthday,
fullName: `${data.firstName} ${data.lastName}`,
getThreeRandomPosts: function() {
// get three random posts from this.lastTenPosts
},
getDaysUntilBirthday: function() {
// use this.birthday to get the num days until birthday
},
greeting: function() {
const numDays = this.getDaysUntilBirthday()
return `Hello, this is ${this.fullName}’s data! It is ${numDays} until ${this.fullName}’s birthday!`
}
};
}

现在,在 initializeFriend 执行结束后,该对象需要的一切都位于对象本身的作用域之内了。我们的方法不需要再依赖于闭包,它们只会用到对象本身包含的信息。 好吧,这是 this 的用法之一,但你说过 this 在不同的上下文中有不同的含义。那是什么意思?为什么不一定会指向对象自己? 有时候,你需要将 this 指向某个特定的东西。一种情况就是事件处理函数。比如我们希望在用户点击好友时打开好友的 Facebook 首页。我们会给对象添加下面的 onClick 方法:

function initializeFriend(data) {
return {
lastTenPosts: data.lastTenPosts,
birthday: data.birthday,
username: data.username,
fullName: `${data.firstName} ${data.lastName}`,
getThreeRandomPosts: function() {
// get three random posts from this.lastTenPosts
},
getDaysUntilBirthday: function() {
// use this.birthday to get the num days until birthday
},
greeting: function() {
const numDays = this.getDaysUntilBirthday()
return `Hello, this is ${this.fullName}’s data! It is ${numDays} until ${this.fullName}’s birthday!`
},
onFriendClick: function() {
window.open(`https://facebook.com/${this.username}\`)
}
};
}

注意我们在对象中添加了 username 属性,这样 onFriendClick 就能访问它,从而在新窗口中打开该好友的 Facebook 首页。现在只需要编写 HTML:

还有 JavaScript:

const bobRossObj = initializeFriend(data[0])
const bobRossDOMEl = document.getElementById(‘Bob_Ross’)
bobRossDOMEl.addEventListener(“onclick”, bobRossObj.onFriendClick)

在上述代码中,我们给 Bob Ross 创建了一个对象。然后我们拿到了 Bob Ross 对应的 DOM 元素。然后执行 onFriendClick 方法来打开 Bob 的 Facebook 主页。似乎没问题,对吧? 有问题! 哪里出错了? 注意我们调用 onclick 处理程序的代码是 bobRossObj.onFriendClick。看到问题了吗?要是写成这样的话能看出来吗?

bobRossDOMEl.addEventListener(“onclick”, function() {
window.open(`https://facebook.com/${this.username}\`)
})

现在看到问题了吗?如果把事件处理程序写成 bobRossObj.onFriendClick,实际上是把 bobRossObj.onFriendClick 上保存的函数拿出来,然后作为参数传递。它不再“依附”在 bobRossObj 上,也就是说,this 不再指向 bobRossObj。它实际指向全局对象,也就是说 this.username 不存在。似乎我们没什么办法了。 轮到绑定上场了!  

明确绑定 this

我们需要明确地将 this 绑定到 bobRossObj 上。我们可以通过 bind 实现:

const bobRossObj = initializeFriend(data[0])
const bobRossDOMEl = document.getElementById(‘Bob_Ross’)
bobRossObj.onFriendClick = bobRossObj.onFriendClick.bind(bobRossObj)
bobRossDOMEl.addEventListener(“onclick”, bobRossObj.onFriendClick)

之前,this 是按照默认的规则设置的。但使用 bind 之后,我们明确地将 bobRossObj.onFriendClick 中的 this 的值设置为 bobRossObj 对象本身。 到此为止,我们看到了为什么要使用 this,以及为什么要明确地绑定 this。最后我们来介绍一下,this 实际上是箭头函数。  

箭头函数

你也许注意到了箭头函数最近很流行。人们喜欢箭头函数,因为很简洁、很优雅。而且你还知道箭头函数和普通函数有点区别,尽管不太清楚具体区别是什么。 简而言之,两者的区别在于: 在定义箭头函数时,不管 this 指向谁,箭头函数内部的 this 永远指向同一个东西。

嗯……这貌似没什么用……似乎跟普通函数的行为一样啊?

我们通过 initializeFriend 举例说明。假设我们想添加一个名为 greeting 的函数:

function initializeFriend(data) {
return {
lastTenPosts: data.lastTenPosts,
birthday: data.birthday,
username: data.username,
fullName: `${data.firstName} ${data.lastName}`,
getThreeRandomPosts: function() {
// get three random posts from this.lastTenPosts
},
getDaysUntilBirthday: function() {
// use this.birthday to get the num days until birthday
},
greeting: function() {
function getLastPost() {
return this.lastTenPosts[0]
}
const lastPost = getLastPost()
return `Hello, this is ${this.fullName}’s data!
${this.fullName}’s last post was ${lastPost}.`
},
onFriendClick: function() {
window.open(`https://facebook.com/${this.username}\`)
}
};
}

这样能运行吗?如果不能,怎样修改才能运行? 答案是不能。因为 getLastPost 没有在对象的上下文中调用,因此getLastPost 中的 this 按照默认规则指向了全局对象。

你说没有“在对象的上下文中调用”……难道它不是从 initializeFriend 返回的内部调用的吗?如果这还不叫“在对象的上下文中调用”,那我就不知道什么才算了。

  我知道“在对象的上下文中调用”这个术语很模糊。也许,判断函数是否“在对象的上下文中调用”的好方法就是检查一遍函数的调用过程,看看是否有个对象“依附”到了函数上。 我们来检查下执行 bobRossObj.onFriendClick() 时的情况。“给我对象 bobRossObj,找到其中的 onFriendClick 然后调用该属性对应的函数”。 我们同样检查下执行 getLastPost() 时的情况。“给我名为 getLastPost 的函数然后执行。”看到了吗?我们根本没有提到对象。 好了,这里有个难题来测试你的理解程度。假设有个函数名为 functionCaller,它的功能就是调用一个函数: functionCaller(fn) { fn() } 如果调用 functionCaller(bobRossObj.onFriendClick) 会怎样?你会认为 onFriendClick 是“在对象的上下文中调用”的吗?this.username有定义吗? 我们来检查一遍:“给我 bobRosObj 对象然后查找其属性 onFriendClick。取出其中的值(这个值碰巧是个函数),然后将它传递给 functionCaller,取名为 fn。然后,执行名为 fn 的函数。”注意该函数在调用之前已经从 bobRossObj 对象上“脱离”了,因此并不是“在对象的上下文中调用”的,所以 this.username 没有定义。 这时可以用箭头函数解决这个问题:

function initializeFriend(data) {
return {
lastTenPosts: data.lastTenPosts,
birthday: data.birthday,
username: data.username,
fullName: `${data.firstName} ${data.lastName}`,
getThreeRandomPosts: function() {
// get three random posts from this.lastTenPosts
},
getDaysUntilBirthday: function() {
// use this.birthday to get the num days until birthday
},
greeting: function() {
const getLastPost = () => {
return this.lastTenPosts[0]
}
const lastPost = getLastPost()
return `Hello, this is ${this.fullName}’s data!
${this.fullName}’s last post was ${lastPost}.`
},
onFriendClick: function() {
window.open(`https://facebook.com/${this.username}\`)
}
};
}

上述代码的规则是: 在定义箭头函数时,不管 this 指向谁,箭头函数内部的 this 永远指向同一个东西。 箭头函数是在 greeting 中定义的。我们知道,在 greeting 内部的 this 指向对象本身。因此,箭头函数内部的 this 也指向对象本身,这正是我们需要的结果。  

结论

this 有时很不好理解,但它对于开发 JavaScript 应用非常有用。本文当然没能介绍 this 的所有方面。一些没有涉及到的话题包括:

  • call 和 apply;
  • 使用 new 时 this 会怎样;
  • 在 ES6 的 class 中 this 会怎样。

我建议你首先问问自己在这些情况下的 this,然后在浏览器中执行代码来检验你的结果。   想学习更多关 于this 的内容,可参考《你不知道的 JS:this 和对象原型》: https://github.com/getify/You-Dont-Know-JS/tree/master/this%20%26%20object%20prototypes 如果你想测试自己的知识,可参考《你不知道的JS练习:this和对象原型》: https://ydkjs-exercises.com/this-object-prototypes 原文:https://medium.freecodecamp.org/a-deep-dive-into-this-in-javascript-why-its-critical-to-writing-good-code-7dca7eb489e7

黑白图像

这段代码会让你的彩色照片显示为黑白照片,是不是很酷?

img.desaturate {
filter: grayscale(100%);
-webkit-filter: grayscale(100%);
-moz-filter: grayscale(100%);
-ms-filter: grayscale(100%);
-o-filter: grayscale(100%);
}

使用 :not() 在菜单上应用/取消应用边框

先给每一个菜单项添加边框

/* add border */
.nav li {
border-right: 1px solid #666;
}

……然后再除去最后一个元素……

// remove border //
.nav li:last-child {
border-right: none;
}

这样代码就干净,易读,易于理解了。 当然,如果你的新元素有兄弟元素的话,也可以使用通用的兄弟选择符(~):

.nav li:first-child ~ li {
border-left: 1px solid #666;
}

页面顶部阴影

下面这个简单的 css3 代码片段可以给网页加上漂亮的顶部阴影效果:

body:before {
content: “”;
position: fixed;
top: -10px;
left: 0;
width: 100%;
height: 10px;
-webkit-box-shadow: 0px 0px 10px rgba(0,0,0,.8);
-moz-box-shadow: 0px 0px 10px rgba(0,0,0,.8);
box-shadow: 0px 0px 10px rgba(0,0,0,.8);
z-index: 100;
}

给 body 添加行高

你不需要分别添加 line-height 到每个p,h标记等。只要添加到 body 即可:

body {
line-height: 1;
}

这样文本元素就可以很容易地从 body 继承。

所有一切都垂直居中

要将所有元素垂直居中,太简单了

html, body {
height: 100%;
margin: 0;
}
body {
-webkit-align-items: center;
-ms-flex-align: center;
align-items: center;
display: -webkit-flex;
display: flex;
}

注意:在IE11中要小心flexbox。  

逗号分隔的列表

让HTML列表项看上去像一个真正的,用逗号分隔的列表:

ul > li:not(:last-child)::after {
content: “,”;
}

对最后一个列表项使用 :not() 伪类。  

使用负的 nth-child 选择项目

在CSS中使用负的 nth-child 选择项目1到项目n。

li {
display: none;
}
/* select items 1 through 3 and display them */
li:nth-child(-n+3) {
display: block;
}

 

对图标使用 SVG

我们没有理由不对图标使用SVG:

.logo {
background: url(“logo.svg”);
}

SVG对所有的分辨率类型都具有良好的扩展性,并支持所有浏览器都回归到IE9。这样可以避开.png、.jpg或.gif文件了。  

优化显示文本

有时,字体并不能在所有设备上都达到最佳的显示,所以可以让设备浏览器来帮助你:

html {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}

注:请负责任地使用 optimizeLegibility。此外,IE /Edge没有 text-rendering 支持。  

对纯 CSS 滑块使用 max-height

使用 max-height 和溢出隐藏来实现只有CSS的滑块:

.slider ul {
max-height: 0;
overlow: hidden;
}
.slider:hover ul {
max-height: 1000px;
transition: .3s ease;
}

 

继承 box-sizing

让 box-sizing 继承 html:

html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}

这样在插件或杠杆其他行为的其他组件中就能更容易地改变 box-sizing 了。  

表格单元格等宽

表格工作起来很麻烦,所以务必尽量使用 table-layout: fixed 来保持单元格的等宽:

.calendar {
table-layout: fixed;
}

 

用 Flexbox 摆脱外边距的各种 hack

当需要用到列分隔符时,通过flexbox的 space-between 属性,你就可以摆脱nth-,first-,和 last-child 的hack了:

.list {
display: flex;
justify-content: space-between;
}
.list .person {
flex-basis: 23%;
}

现在,列表分隔符就会在均匀间隔的位置出现。

使用属性选择器用于空链接

当a元素没有文本值,但 href 属性有链接的时候显示链接:

a[href^=”http”]:empty::before {
content: attr(href);
}

 

检测鼠标双击

.test3 span {
position: relative;
}
.test3 span a {
position: relative;
z-index: 2;
}
.test3 span a:hover, .test3 span a:active {
z-index: 4;
}
.test3 span input {
background: transparent;
border: 0;
cursor: pointer;
position: absolute;
top: -1px;
left: 0;
width: 101%; /* Hacky */
height: 301%; /* Hacky */
z-index: 3;
}
.test3 span input:focus {
background: transparent;
border: 0;
z-index: 1;
}

 

CSS 写出三角形

利用border来写三角形代码,并且兼容IE6.

/* create an arrow that points up */
div.arrow-up {
width:0px;
height:0px;
border-left:5px solid transparent; /* left arrow slant */
border-right:5px solid transparent; /* right arrow slant */
border-bottom:5px solid #2f2f2f; /* bottom, add background color here */
font-size:0px;
line-height:0px;
}
/* create an arrow that points down */
div.arrow-down {
width:0px;
height:0px;
border-left:5px solid transparent;
border-right:5px solid transparent;
border-top:5px solid #2f2f2f;
font-size:0px;
line-height:0px;
}
/* create an arrow that points left */
div.arrow-left {
width:0px;
height:0px;
border-bottom:5px solid transparent; /* left arrow slant */
border-top:5px solid transparent; /* right arrow slant */
border-right:5px solid #2f2f2f; /* bottom, add background color here */
font-size:0px;
line-height:0px;
}
/* create an arrow that points right */
div.arrow-right {
width:0px;
height:0px;
border-bottom:5px solid transparent; /* left arrow slant */
border-top:5px solid transparent; /* right arrow slant */
border-left:5px solid #2f2f2f; /* bottom, add background color here */
font-size:0px;
line-height:0px;
}

 

CSS3 calc() 的使用

calc() 用法类似于函数,能够给元素设置动态的值:

/* basic calc */
.simpleBlock {
width: calc(100% - 100px);
}
/* calc in calc */
.complexBlock {
width: calc(100% - 50% / 3);
padding: 5px calc(3% - 2px);
margin-left: calc(10% + 10px);
}

文本渐变

文本渐变效果很流行,使用 CSS3 能够很简单就实现:

h2[data-text] {
position: relative;
}
h2[data-text]::after {
content: attr(data-text);
z-index: 10;
color: #e3e3e3;
position: absolute;
top: 0;
left: 0;
-webkit-mask-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0,0,0,0)), color-stop(50%, rgba(0,0,0,1)), to(rgba(0,0,0,0)));
}

 

禁用鼠标事件

CSS3 新增的 pointer-events 让你能够禁用元素的鼠标事件,例如,一个连接如果设置了下面的样式就无法点击了。

.disabled { pointer-events: none; }

 

模糊文本

简单但很漂亮的文本模糊效果,简单又好看!

.blur {
color: transparent;
text-shadow: 0 0 5px rgba(0,0,0,0.5);
}

:matches() CSS 伪类函数将选择器列表作为参数,并选择该列表中任意一个选择器可以选择的元素。这对于以更紧凑的形式编写大型选择器非常有用。

注意,许多浏览器通过一个更旧的、带前缀的伪类:any()来支持这个功能,包括旧版本的Chrome、Firefox和Safari。这与:matches()的工作方式完全相同,只是它需要厂商前缀,不支持复杂的选择器。

/* 选择header, main, footer里的任意一个悬浮状态的段落(p标签) */
:matches(header, main, footer) p:hover {
color: red;
cursor: pointer;
}
/* 以上内容相当于以下内容 */
header p:hover,
main p:hover,
footer p:hover {
color: red;
cursor: pointer;
}
/* 向后兼容的版本:-*-any() */
:-moz-any(header, main, footer) p:hover,
:-webkit-any(header, main, footer) p:hover,
:matches(header, main, footer) p:hover {
color: red;
cursor: pointer;
}

来个例子:

<header>
  <p>一段标题信息</p>
</header>

<main>
  <ul>
    <li><p>一段列表</p><p>列表项</p></li>
    <li><p>一段列表</p><p>列表项</p></li>
  </ul>
</main>

<footer>
<p>一段底部信息</p>
</footer>

:matches(header, main, footer) p:hover {
  color: red;
  cursor: pointer;
}

:-webkit-any(header, main, footer) p:hover {
  color: red;
  cursor: pointer;
}

:-moz-any(header, main, footer) p:hover {
  color: red;
  cursor: pointer;
}

选择器列表简化

:matches() 伪类可以大大简化CSS选择器。例如,下面的CSS:

/* 多层无须列表 */
ol ol ul, ol ul ul, ol menu ul, ol dir ul,
ol ol menu, ol ul menu, ol menu menu, ol dir menu,
ol ol dir, ol ul dir, ol menu dir, ol dir dir,
ul ol ul, ul ul ul, ul menu ul, ul dir ul,
ul ol menu, ul ul menu, ul menu menu, ul dir menu,
ul ol dir, ul ul dir, ul menu dir, ul dir dir,
menu ol ul, menu ul ul, menu menu ul, menu dir ul,
menu ol menu, menu ul menu, menu menu menu, menu dir menu,
menu ol dir, menu ul dir, menu menu dir, menu dir dir,
dir ol ul, dir ul ul, dir menu ul, dir dir ul,
dir ol menu, dir ul menu, dir menu menu, dir dir menu,
dir ol dir, dir ul dir, dir menu dir, dir dir dir {
  list-style-type: square;
}
可以被替换为:

:matches(ol, ul, menu, dir) :matches(ol, ul, menu, dir) ul,
:matches(ol, ul, menu, dir) :matches(ol, ul, menu, dir) menu,
:matches(ol, ul, menu, dir) :matches(ol, ul, menu, dir) dir {
  list-style-type: square;
}

但是,不要像下面那么做: (参见 the section on performance 。)

:matches(ol, ul, menu, dir) :matches(ol, ul, menu, dir) :matches(ul, menu, dir) {
  list-style-type: square;
}

简化部分选择器

:matches 伪类在处理HTML5 sections and headings特别有用。 由于 <section>, <article>, <aside>, <nav> 经常嵌套在一起, 没有 :matches()的话匹配其他元素将会很棘手。 例如, 在没有 :matches()的情况下, 在不同深度对所有素进行样式化可能非常复杂:

/* Level 0 */
h1 {
  font-size: 30px;
}
/* Level 1 */
section h1, article h1, aside h1, nav h1 {
  font-size: 25px;
}
/* Level 2 */
section section h1, section article h1, section aside h1, section nav h1,
article section h1, article article h1, article aside h1, article nav h1,
aside section h1, aside article h1, aside aside h1, aside nav h1,
nav section h1, nav article h1, nav aside h1, nav nav h1, {
  font-size: 20px;
}
/* Level 3 */
/* ... */

使用 :matches()之后,它变的非常简单:

/* Level 0 */
h1 {
  font-size: 30px;
}
/* Level 1 */
:matches(section, article, aside, nav) h1 {
  font-size: 25px;
}
/* Level 2 */
:matches(section, article, aside, nav)
:matches(section, article, aside, nav) h1 {
  font-size: 20px;
}
/* Level 3 */
:matches(section, article, aside, nav)
:matches(section, article, aside, nav)
:matches(section, article, aside, nav) h1 {
  font-size: 15px;
}

这个函数暂时还不被完全支持,相信不久的将来一定会被完美支持

我们将用一组图片缩略图列表将它们转换为具有模糊悬停效果的响应式CSS Grid图库。我们还将使用一个很棒的CSS技巧来确保触摸屏用户也能体验到这种效果! 我们将执行以下操作:

  • 使用CSS Grid排列缩略图,为我们提供响应式图库。
  • 使用CSS filter和transitions创建悬停效果。
  • 使用漂亮的CSS媒体查询来确保触摸屏用户仍然可以看到每个缩略图标题,即使没有悬停。

首先要把列表的结构罗列出来

响应式CSS网格

只要有几个规则,我们就可以把缩略图变成网格:

.grid-container {
display: grid;
grid-gap: 1em;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}

这里的简单线条是display:grid;(它将所有子项转换成网格项并加以布局)和grid-gap:1em;(它定义了模块之间的间隔)。 稍微的复杂的是我们定义的grid-template-columns属性的值,它定义了我们的列。你平常可能看到的是类似repeat(3,200px)的内容,它将定义的是三列200px。在这种情况下,我们将使用auto-fill来填充关键字repeat(),然后使用了一些值。这给了我们尽可能多的列,最小为300px,最大为1fr,将适合网格容器。 调整浏览器窗口大小,看看它是如何运行的!

你需要补充的一个细节:

img {
width: 100%;
height: auto;
vertical-align: middle;
}

悬停效果

我们将使用标题作为缩略图的叠加层,在悬停时显示它们。我们还将为悬停的图像提供红色效果,并使其略微模糊,以帮助覆盖文本的可读性。

覆盖标题

要叠加标题,我们需要定位它,所以我们首先要将

设置position: relative;和标题position: absolute;。我们将其背景填充为红色背景:

.location-listing {
position: relative;
}

.location-title {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background: rgba(90, 0, 10, 0.4);
}

已经很好的显示了

标题的风格

一些印刷样式将改善我们的标题的外观,并且三行flexbox魔术将集中为我们对齐:

.location-title {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background: rgba(90, 0, 10, 0.4);

color: white;
font-size: 1.5em;
font-weight: bold;
text-decoration: none;

display: flex;
align-items: center;
justify-content: center;
}

好多了:

隐藏标题

现在让我们通过设置它的不透明度隐藏标题,这样我们可以在悬停时看到它。.location-title应该这样做:

opacity: 0;
transition: opacity .5s;

在这里,我们还设置了一个transition规则,以便当我们将不透明度恢复时,它将有0.5秒的延时过程。我们现在将hover悬停时不透明度设置为1

.location-listing:hover .location-title {
opacity: 1;
}

到这里我们标题悬停效果已经完美实现了:

模糊效果

我们已经创造了一个漂亮的悬停效果,接下来为图像添加模糊滤镜。首先将模糊滤镜设置为正常状态,以便为我们提供一些过渡。然后我们会为悬停状态模糊设置为2px(这里你可以根据你的意愿设置,但我认为2px是一个很棒的视觉效果):

.location-image img {
filter: blur(0px);
transition: filter 0.3s ease-in;
}

.location-listing:hover .location-image img {
filter: blur(2px);
}

这就是效果了:

需要注意的两件事:

  • 标题已经消失,因为浏览器现在正在顶部呈现模糊的图形。
  • 模糊效果看起来不错,但它也有边缘模糊效果。(可以把它去掉)

因为层级的关系标题被遮住了把.location-title设置z-index:1 把边缘模糊去掉,首先我们对图像进行缩放,使其稍微大一些,然后对图像容器(.location-list)设置overflow: hidden;以便当较大的图像模糊时,有效地裁剪其边缘。下面是两个元素已设置的属性:

.location-image img {
filter: blur(0px);
transition: filter 0.3s ease-in;
transform: scale(1.1);
}

.location-listing {
position: relative;
overflow: hidden;
}

触摸屏问题

有些网页应用都有很多悬停效果,而且是都被隐藏,在不触发的时候是看不到的(大量的平板电脑和智能手机不能模仿悬停“按下”动作),这是不容易访问的。 幸运的是,CSS有一些非常有用的交互媒体查询可以帮助我们(他们也享受相当不错的浏览器支持)。这些查询将检测浏览器的输入机制 - 指针设备质量,悬停能力以及一些其他特殊定义 - 因此我们可以相当准确地确定是否在触摸屏设备上查看我们的缩略图。 以此媒体查询为例(它完全符合我们的预期)

@media (hover: none) { }

在这些花括号中,我们将我们想要应用的任何样式应用于无法处理的浏览器:hover。我们将声明对于悬停不可能或至少不方便的设备,缩略图图像将始终模糊,标题将始终可见:

@media (hover: none) {
.location-title {
opacity: 1;
}
.location-image img {
filter: blur(2px);
}
}

注意:如上所述,对此的支持非常合理,但有关交互媒体查询实施的讨论仍在进行中。这个规范很可能会改变或删除部分。

See the Pen 创建CSS Grid 图像库 by w3cbest.com (@w3cbest) on CodePen.

关于给Echarts柱子设置不同颜色的方案网上已经有了,不多啰嗦。其实就是通过设置itemStyle的color函数(可以当作函数回调),使其设置一组颜色值通过函数返回值的下标一一对应将颜色赋给柱子上,大致操作就是下面的一段代码

var colors = [‘#4587E7’,’#35AB33’,’#F5AD1D’,’#ff7f50’,’#da70d6’,’#32cd32’,’#6495ed’];
option = {

series:[{

itemStyle: {
color: function(params) {
//通过返回值的下标一一对应将颜色赋给柱子上
return colors[params.dataIndex];
}
}

}]
}

 

See the Pen dawKPV by w3cbest.com (@w3cbest) on CodePen.

 

下面我们讲一下如何点击改变颜色

为啥子要讲这个呢,这是我在项目中遇到的需求,看下图:

See the Pen Echarts给柱子设置不同颜色点击变色 by w3cbest.com (@w3cbest) on CodePen.

需求是这样的,分值在小于20%的时候柱子显示红色,默认是蓝色,点击后为深蓝色,需求知道了接下来该如何实现呢?通过循环判断小于20%的值赋上颜色这样把data数组结构就改变成数组对象,这样效果已经出来了,就是在点击的时候不会换色,红色柱子已被定死了不能改变了,也就是说这个方法是行不通的,换其他方法,再不改变data数组的前提下是否可以呢,然后就结合了文章开头的给柱子添加不同颜色的思想和刚刚的那个点击变色没实现的方法实现了下面的效果,也就是我的需求

data = [7, 10, 20, 30, 10, 29, 16];
var colors = [];
for (var i = 0; i < data.length; i++) {
if(data[i] < 20){
colors.push(‘#EE4B46’);
}else{
colors.push(‘#00A1E9’);
}
};

option = {

data: data,
itemStyle: {
color: function(params) {
var key = params.dataIndex + 1;
if (key === curInt) {
return ‘#4587E7’;
} else {
return colors[params.dataIndex]
}
}
}

}

 

See the Pen Echarts给柱子设置不同颜色点击变色 by w3cbest.com (@w3cbest) on CodePen.

本文为 JavaScript Class 类的私有和公共属性 续篇

之前我们学习了如何在 ES5 和 ES6 中创建 Animal 类。我们还学习了如何使用 JavaScrip t的原型在这些类之间共享方法。查看我们在之前文章中看到的代码。

function Animal(name, energy) {
    this.name = name
    this.energy = energy
}

Animal.prototype.eat = function(amount) {
    console.log(`${this.name} is eating.`)
    this.energy += amount
}

Animal.prototype.sleep = function(length) {
    console.log(`${this.name} is sleeping.`)
    this.energy += length
}

Animal.prototype.play = function(length) {
    console.log(`${this.name} is playing.`)
    this.energy -= length
}

const leo = new Animal('Leo', 7)

class Animal {
constructor(name, energy) {
this.name = name
this.energy = energy
}
eat(amount) {
console.log(`${this.name} is eating.`)
this.energy += amount
}
sleep() {
console.log(`${this.name} is sleeping.`)
this.energy += length
}
play() {
console.log(`${this.name} is playing.`)
this.energy -= length
}
}

const leo = new Animal('Leo', 7)

现在我们想为特定动物建一个别 class(类) 。 例如,如果我们想要开始制作一堆狗实例,该怎么办? 这些狗有哪些属性和方法? 嗯,类似于我们的 Animal 类,我们可以给每只狗一个 name ,一个 energy 等级,以及 eat ,sleep 和 play 的能力。 我们的 Dog 类是独一无二的,我们也可以给Dog 类一些独一无二的的属性,比如一个 breed(品种) 属性以及 bark(吠叫) 的能力。 在 ES5 中,我们的 Dog 类可能看起来像这样:

function Dog(name, energy, breed) {
    this.name = name
    this.energy = energy
    this.breed = breed
}

Dog.prototype.eat = function(amount) {
    console.log(`${this.name} is eating.`)
    this.energy += amount
}

Dog.prototype.sleep = function(length) {
    console.log(`${this.name} is sleeping.`)
    this.energy += length
}

Dog.prototype.play = function(length) {
    console.log(`${this.name} is playing.`)
    this.energy -= length
}

Dog.prototype.bark = function() {
    console.log('Woof-Woof!')
    this.energy -= .1
}

const charlie = new Dog('Charlie', 10, 'Goldendoodle')

你应该看出来了,我们刚刚重新创建了 Animal 类并为它添加了一些新属性。 如果我们想创建另一个动物,比如说 Cat ,那么我们必须再次创建一个 Cat 类,将 Animal 类中的所有常用逻辑复制到 Cat ,然后像 Dog 类一样添加 Cat 特定属性。 就是说,我们必须对我们创造的每一种不同类型的动物都这样做。

function Dog (name, energy, breed) {}

function Cat (name, energy, declawed) {}

function Giraffe (name, energy, height) {}

function Monkey (name, energy, domesticated) {}

这项工作似乎很浪费。 Animal 类是完美的基类。 这意味着它具有我们每只动物的共同特征。 无论我们是创造 狗,猫,长颈鹿还是猴子,它们都会有一个name ,energy 等级,以及 eat ,sleep 和 play 的能力。 那么每当我们为每个不同的动物创建单独的类时,我们是否可以利用 Animal类? 我们来试试吧。 我将在下面再次粘贴 Animal 类以便于参考。

function Animal(name, energy) {
    this.name = name
    this.energy = energy
}

Animal.prototype.eat = function(amount) {
    console.log(`${this.name} is eating.`)
    this.energy += amount
}

Animal.prototype.sleep = function(length) {
    console.log(`${this.name} is sleeping.`)
    this.energy += length
}

Animal.prototype.play = function(length) {
    console.log(`${this.name} is playing.`)
    this.energy -= length
}

function Dog(name, energy, breed) {

}

我们对上面的 Dog 构造函数你了解多少? 首先,我们知道它需要3个参数,nameenergy 和 breed。 其次,我们知道它将使用 new 关键字调用,因此我们将拥有一个 this 对象。 第三,我们知道我们需要利用 Animal 函数,这样任何狗的实例都会有一个name ,energy 等级,以及 eat ,sleep 和 play 的能力。 第三点是有点棘手的问题。 你“利用”一个函数的方式就是调用它。 所以我们知道在 Dog 里面,我们想要调用 Animal 。 我们需要弄清楚的是我们如何在Dog 的上下文中调用 Animal。 这意味着我们想用 Dog 中的 this 关键字调用 Animal。 如果我们正确地做到了,那么 Dog 函数内部将具有 Animal 的所有属性(name ,energy)。 如果你记得 上一节我们所讨论的内容,JavaScript 中的每个函数都有一个 .call 方法。

.call() 是函数的一个方法,它允许你调用函数时,指定该函数的上下文。

听起来正是我们所需要的。我们想在 Dog 上下文中调用 Animal 。

function Dog(name, energy, breed) {
    Animal.call(this, name, energy)

    this.breed = breed
}

const charlie = new Dog('Charlie', 10, 'Goldendoodle')

charlie.name // Charlie
charlie.energy // 10
charlie.breed // Goldendoodle

知道这个,我们就已经成功一半了。你将在上面的代码中注意到,因为这一行是 Animal.call(this, name, energy), Dog 的每个实例都将有一个 name 和 energy 属性。同样,这样做的原因是,就好像我们使用从 Dog 生成的 this 关键字运行 Animal 函数一样。在我们添加了一个name 和 energy 属性之后,我们又像往常一样添加了一个 breed 属性。 请记住,这里的目标是让 Dog 的每个实例不仅具有 Animal 的所有属性,而且还具有所有方法。如果你运行上面的代码,你会注意到如果你尝试运行 charlie.eat(10) ,你将收到一个错误。目前 Dog 的每个实例都具有 Animalname 和 energy)的属性,但我们没有做任何事情来确保他们也有方法(eat ,sleep 和 play)。 让我们考虑如何解决这个问题。我们知道所有 Animal 的方法都位于 Animal.prototype 上。这意味着我们想要确保 Dog 的所有实例都可以访问Animal.prototype 上的方法。如果我们在这里使用我们的好朋友 Object.create 怎么办?如果你还记得,Object.create 允许你创建一个对象,该对象将在失败的查找中委托给另一个对象。所以在我们的例子中,我们想要创建的对象将是 Dog 的原型,而我们想要在失败的查找中委托的对象是Animal.prototype

function Dog(name, energy, breed) {
    Animal.call(this, name, energy)

    this.breed = breed
}

Dog.prototype = Object.create(Animal.prototype)

现在,只要在 Dog 实例上查找失败,JavaScript 就会将该查找委托给 Animal.prototype 。 如果这仍然有点模糊,请重新阅读 JavaScript Prototype(原型) 初学者指南,其中我们讨论了 Object.create 和 JavaScript 的 原型(prototype) 。 让我们一起看完整个代码,然后我们将了解发生的事情。

function Animal(name, energy) {
    this.name = name
    this.energy = energy
}

Animal.prototype.eat = function(amount) {
    console.log(`${this.name} is eating.`)
    this.energy += amount
}

Animal.prototype.sleep = function(length) {
    console.log(`${this.name} is sleeping.`)
    this.energy += length
}

Animal.prototype.play = function(length) {
    console.log(`${this.name} is playing.`)
    this.energy -= length
}

function Dog(name, energy, breed) {
    Animal.call(this, name, energy)

    this.breed = breed
}

Dog.prototype = Object.create(Animal.prototype)

现在我们已经创建了我们的基类( Animal )以及我们的子类( Dog ),让我们在创建 Dog 实例时看看它的样子。

const charlie = new Dog('Charlie', 10, 'Goldendoodle')

charlie.name // Charlie
charlie.energy // 10
charlie.breed // Goldendoodle

到目前为止没有任何花哨的东西,但让我们来看看当我们调用位于 Animal 上的方法时会发生什么。

charlie.eat(10)

/*
1) JavaScript checks if charlie has an eat property - it doesn't.
2) JavaScript then checks if Dog.prototype has an eat property
- it doesn't.
3) JavaScript then checks if Animal.prototype has an eat property
- it does so it calls it.
*/

Dog.prototype 被检查的原因是因为当我们创建一个新的 Dog 实例时,我们使用了 new 关键字。在引擎中,为我们创建的 this 对象委托给Dog.prototype(见下面的注释)。

function Dog(name, energy, breed) {
    // this = Object.create(Dog.prototype)
    Animal.call(this, name, energy)

    this.breed = breed
    // return this
}

之所以检查 Animal.prototype 是因为我们用这一行覆盖了 Dog.prototype 以委托给失败的查找的 Animal.prototype

Dog.prototype = Object.create(Animal.prototype)

现在我们还没有谈到的一件事是,如果 Dog 有自己的方法呢? 嗯,这是一个简单的解决方案。 就像 Animal 一样,如果我们想在该类的所有实例之间共享一个方法,我们将它添加到函数的原型中。

function Dog(name, energy, breed) {
    Animal.call(this, name, energy)

    this.breed = breed
}

Dog.prototype = Object.create(Animal.prototype)

Dog.prototype.bark = function() {
    console.log('Woof Woof!')
    this.energy -= .1
}

非常好。我们需要做一个小小的补充。如果你不记得了请回到 JavaScript Prototype(原型) 初学者指南了解详情,我们可以通过使用 instance.constructor 来访问实例的构造函数。

function Animal(name, energy) {
    this.name = name
    this.energy = energy
}

const leo = new Animal('Leo', 7)
console.log(leo.constructor) // Logs the constructor function

正如前一篇文章中所解释的那样,“其工作原因是因为任何 Animal 实例都会在失败的查找中委托给 Animal.prototype 。 因此,当你尝试访问leo.prototype 时,leo 没有 prototype 属性,因此它会将该查找委托给 Animal.prototype ,它确实具有 constructor 属性。“ 我提出这个问题的原因是因为在我们的实现中,我们用一个委托给 Animal.prototype 的对象覆盖了 Dog.prototype 。

function Dog(name, energy, breed) {
    Animal.call(this, name, energy)

    this.breed = breed
}

Dog.prototype = Object.create(Animal.prototype)

Dog.prototype.bark = function() {
    console.log('Woof Woof!')
    this.energy -= .1
}

这意味着现在,任何打印 Dog 的实例 instance.constructor 都将获得 Animal 构造函数而不是 Dog 构造函数。你可以通过运行此代码自行查看

function Animal(name, energy) {
    this.name = name
    this.energy = energy
}

Animal.prototype.eat = function(amount) {
    console.log(`${this.name} is eating.`)
    this.energy += amount
}

Animal.prototype.sleep = function(length) {
    console.log(`${this.name} is sleeping.`)
    this.energy += length
}

Animal.prototype.play = function(length) {
    console.log(`${this.name} is playing.`)
    this.energy -= length
}

function Dog(name, energy, breed) {
    Animal.call(this, name, energy)

    this.breed = breed
}

Dog.prototype = Object.create(Animal.prototype)

Dog.prototype.bark = function() {
    console.log('Woof Woof!')
    this.energy -= .1
}

const charlie = new Dog('Charlie', 10, 'Goldendoodle')
console.log(charlie.constructor)

请注意,即使 charlie 是 Dog 的直接实例,它也会为你提供 Animal 构造函数。同样,我们可以像上面一样了解这里发生的事情。

const charlie = new Dog('Charlie', 10, 'Goldendoodle') 
console.log(charlie.constructor)  
/* 
1) JavaScript checks if charlie has a constructor property - it doesn't. 
2) JavaScript then checks if Dog.prototype has a constructor property - it doesn't because it was deleted when we overwrote Dog.prototype. 
3) JavaScript then checks if Animal.prototype has a constructor property - it does so it logs that. 
*/

我们该如何解决这个问题?嗯,这很简单。一旦我们覆盖它,我们就可以向 Dog.prototype 添加正确的 constructor 属性。

function Dog(name, energy, breed) {
    Animal.call(this, name, energy)

    this.breed = breed
}

Dog.prototype = Object.create(Animal.prototype)

Dog.prototype.bark = function() {
    console.log('Woof Woof!')
    this.energy -= .1
}

Dog.prototype.constructor = Dog

此时如果我们想要创建另一个子类,比如 Cat ,我们将遵循相同的模式。

function Cat(name, energy, declawed) {
    Animal.call(this, name, energy)

    this.declawed = declawed
}

Cat.prototype = Object.create(Animal.prototype)
Cat.prototype.constructor = Cat

Cat.prototype.meow = function() {
    console.log('Meow!')
    this.energy -= .1
}

这种具有委托给它的子类的基类的概念称为继承,它是面向对象编程(OOP)的主要部分。 如果你来自不同的编程语言,你可能已经熟悉OOP和继承了。 在 ES6 classes 之前,在 JavaScript 中,继承是一项非常艰巨的任务,正如你在上面所看到的。你现在只需要了解什么时候使用继承,以及 .call 和 Object.create, this ,和 FN.prototype 的良好组合。- 这些都是高级 JS 主题。让我们看看如何使用 ES6 类来完成同样的事情。 首先,让我们回顾一下使用我们的 Animal 类从 ES5 “类” 到 ES6 类的样子。

function Animal(name, energy) {
    this.name = name
    this.energy = energy
}

Animal.prototype.eat = function(amount) {
    console.log(`${this.name} is eating.`)
    this.energy += amount
}

Animal.prototype.sleep = function(length) {
    console.log(`${this.name} is sleeping.`)
    this.energy += length
}

Animal.prototype.play = function(length) {
    console.log(`${this.name} is playing.`)
    this.energy -= length
}

const leo = new Animal('Leo', 7)

class Animal {
    constructor(name, energy) {
        this.name = name
        this.energy = energy
    }
    eat(amount) {
        console.log(`${this.name} is eating.`)
        this.energy += amount
    }
    sleep() {
        console.log(`${this.name} is sleeping.`)
        this.energy += length
    }
    play() {
        console.log(`${this.name} is playing.`)
        this.energy -= length
    }
}

const leo = new Animal('Leo', 7)

现在我们已经将我们的 Animal 构造函数重构为 ES6 类,接下来我们需要做的是弄清楚如何重构我们的基类( Dog )。好消息是它更加直观。作为参考,在ES5 中,这是我们所拥有的。

function Dog(name, energy, breed) {
    Animal.call(this, name, energy)

    this.breed = breed
}

Dog.prototype = Object.create(Animal.prototype)

Dog.prototype.bark = function() {
    console.log('Woof Woof!')
    this.energy -= .1
}

Dog.prototype.constructor = Dog

在我们进入继承之前,让我们使用 ES6 类来重构 Dog ,就像我们在之前的帖子中学到的那样。

class Dog {
    constructor(name, energy, breed) {
        this.breed = breed
    }
    bark() {
        console.log('Woof Woof!')
        this.energy -= .1
    }
}

看起来很棒。现在,让我们弄清楚如何确保 Dog 继承自 Animal 。我们需要做的第一步是非常直接的。使用 ES6 类,你可以使用此语法 extend 基类

class Subclass extends Baseclass {}

翻译成我们的例子,这将使我们的 Dog 类看起来像这样:

class Animal {
    constructor(name, energy) {
        this.name = name
        this.energy = energy
    }
    eat(amount) {
        console.log(`${this.name} is eating.`)
        this.energy += amount
    }
    sleep() {
        console.log(`${this.name} is sleeping.`)
        this.energy += length
    }
    play() {
        console.log(`${this.name} is playing.`)
        this.energy -= length
    }
}

class Dog extends Animal {
    constructor(name, energy, breed) {
        this.breed = breed
    }
    bark() {
        console.log('Woof Woof!')
        this.energy -= .1
    }
}

在ES5中,为了确保 Dog 的每个实例都具有name 和 energy 属性,我们使用 .call 以在 Dog 实例的上下文中调用 Animal 构造函数。 幸运的是,在 ES6 中,它更直接。 每当你扩展一个基类并且你需要调用那个基类的构造函数时,你调用 super 传递它需要的任何参数即可。 所以在我们的例子中,我们的 Dog 构造函数被重构为这样:

class Animal {
    constructor(name, energy) {
        this.name = name
        this.energy = energy
    }
    eat(amount) {
        console.log(`${this.name} is eating.`)
        this.energy += amount
    }
    sleep() {
        console.log(`${this.name} is sleeping.`)
        this.energy += length
    }
    play() {
        console.log(`${this.name} is playing.`)
        this.energy -= length
    }
}
class Dog extends Animal {
    constructor(name, energy, breed) {
        super(name, energy) // calls Animal's constructor
        this.breed = breed
    }
    bark() {
        console.log('Woof Woof!')
        this.energy -= .1
    }
}

就是这样。不使用 .call ,不使用 Object.create ,不用担心重置原型上的构造函数 – 只需 extends 基类并确保调用 super 即可。 JavaScript 的有趣之处在于你学到的相同模式,最后几篇文章直接融入语言本身。 以前你了解到 Array 的所有实例都可以访问 pop ,slice ,filter 等数组方法的原因是因为所有这些方法都存在于 Array.prototype 中。

console.log(Array.prototype)
/*
concat: ?n concat()
constructor: ?n Array()
copyWithin: ?n copyWithin()
entries: ?n entries()
every: ?n every()
fill: ?n fill()
filter: ?n filter()
find: ?n find()
findIndex: ?n findIndex()
forEach: ?n forEach()
includes: ?n includes()
indexOf: ?n indexOf()
join: ?n join()
keys: ?n keys()
lastIndexOf: ?n lastIndexOf()
length: 0n
map: ?n map()
pop: ?n pop()
push: ?n push()
reduce: ?n reduce()
reduceRight: ?n reduceRight()
reverse: ?n reverse()
shift: ?n shift()
slice: ?n slice()
some: ?n some()
sort: ?n sort()
splice: ?n splice()
toLocaleString: ?n toLocaleString()
toString: ?n toString()
unshift: ?n unshift()
values: ?n values()
*/

你知道,所有 Object 实例都可以访问 hasOwnProperty 和 toString 等方法的原因是因为这些方法存在于 Object.prototype 上。

console.log(Object.prototype)
/*
constructor: ?n Object()
hasOwnProperty: ?n hasOwnProperty()
isPrototypeOf: ?n isPrototypeOf()
propertyIsEnumerable: ?n propertyIsEnumerable()
toLocaleString: ?n toLocaleString()
toString: ?n toString()
valueOf: ?n valueOf()
*/

这对你来说是一个挑战。使用上面的 Array 方法和 Object 方法列表,为什么下面的代码有效?

const friends = ['Mikenzi', 'Jake', 'Ean']
friends.hasOwnProperty('push') // false

如果查看 Array.prototype ,则没有 hasOwnProperty 方法。 好吧,如果 Array.prototype 上没有 hasOwnProperty 方法,上面示例中的 friends 数组如何访问 hasOwnProperty? 原因是因为 Array 类扩展了 Object 类。 因此,在上面的示例中,当 JavaScript 看到friends 没有 hasOwnProperty 属性时,它会检查 Array.prototype 是否具有该方法。 当 Array.prototype 没有时,它会检查 Object.prototype 是否有该方法,然后再调用它。 这是我们在这篇博客文章中看到的相同过程。 JavaScript 有两种类型 – 原始类型 和 引用类型 。 原始类型是 boolean , number, stringnull 和 undefined 并且是不可变的。 其他所有内容都是引用类型,它们都扩展了 Object.prototype 。 这就是为什么你可以为函数和数组添加属性,这就是为什么函数和数组都可以访问 Object.prototype 上的方法。

function speak(){} speak.woahFunctionsAreLikeObjects = true console.log(speak.woahFunctionsAreLikeObjects) // true const friends = ['Mikenzi', 'Jake', 'Ean'] friends.woahArraysAreLikeObjectsToo = true console.log(friends.woahArraysAreLikeObjectsToo) // true

原文地址:https://tylermcginnis.com/javascript-inheritance-and-the-prototype-chain/

当Web项目变得越来越大时,他的CSS会变得像天文数字那么大而且还变得混乱。为了帮助我们解决这个问题,新的CSS变量很快就会出现在主流浏览器中,它让开发人员能够重用并轻松编辑重复出现的CSS属性。用过SASS或Less的人应该知道他的变量功能有多棒,但这些变量是预处理器,需要在使用前进行编译。现在变量在vanilla CSS中可用,您可以立即在浏览器中使用它们!

定义和使用CSS变量

与任何其他CSS定义一样,变量遵循相同的范围和继承规则。使用它们的最简单方法是通过将声明添加到:root伪类来使它们全局可用,以便所有其他选择器都可以继承它。

:root {
  –awesome-blue:#2196F3;
}

要访问变量中的值,我们可以使用var(…)语法。请注意,名称区分大小写,因此–foo != –FOO。

.element {
background-color:var(–awesome-blue);
}

浏览器支持

常用的浏览器除了IE都完美支持,您可以在此处获取更多详细信息 - 我可以使用CSS变量。下面是几个例子,展示了CSS变量的典型用法。为确保它们正常工作,请尝试在我们上面提到的其中一个浏览器上查看它们。

示例1 - 主题颜色

当我们需要对多个元素一遍又一遍地应用相同的规则时,CSS中的变量是最有用的,例如主题中的重复颜色。我们不是每次想要重复使用相同颜色时进行复制和粘贴,而是将其放在变量中并从那里访问它。 现在,如果我们的客户不喜欢我们选择的蓝色阴影,我们可以在一个地方(变量的定义)改变样式来改变整个主题的颜色。没有变量,我们必须手动搜索和替换每一次出现。 可将代码复制下来在你的编辑器里面测试

* {margin: 0;padding: 0;box-sizing: border-box;}html {padding: 30px;font: normal 13px/1.5 sans-serif;color: #546567;background-color: var(–primary-color);}.container {background: #fff;padding: 20px;}h3 {padding-bottom: 10px;margin-bottom: 15px;}p {background-color: #fff;margin: 15px 0;}button {font-size: 13px;padding: 8px 12px;background-color: #fff;border-radius: 3px;box-shadow: none;text-transform: uppercase;font-weight: bold;cursor: pointer;opacity: 0.8;outline: 0;}button:hover {opacity: 1;}

:root {
--primary-color: #B1D7DC;
--accent-color: #FF3F90;

}
html {
background-color: var(–primary-color);
}
h3 {
border-bottom: 2px solid var(–primary-color);
}
button {
color: var(–accent-color);
border: 1px solid var(–accent-color);
}

对话框窗口

过放荡不羁的生活,容易得像顺水推舟,但是要结识良朋益友,却难如登天。

See the Pen 示例1 – 主题颜色 by w3cbest.com (@w3cbest) on CodePen.

示例2 - 属性类名可读性

变量的另一个重要用途是当我们想要保存更复杂的属性值时,我们不必记住它。最好的例子就是有多个参数,如CSS规则box-shadow,transform和font。 通过将属性放在变量中,我们可以使用语义可读的名称来访问它。

html{background-color: #F9F9F9;}
ul{padding: 20px;list-style: none;width: 300px;}
li{font: normal 18px sans-serif;padding: 20px;transition: 0.4s;margin: 10px;color: #444;background-color: #fff;cursor: pointer;}

:root{
–tiny-shadow: 0 2px 1px 0 rgba(0, 0, 0, 0.2);
–animate-right: translateX(20px);
}
li{
box-shadow: var(–tiny-shadow);
}
li:hover{
transform: var(–animate-right);
}

  • 我在这里!
  • 我在这里!
  • 我在这里!

See the Pen 示例2 – 属性类名可读性 by w3cbest.com (@w3cbest) on CodePen.

示例3 - 动态更改变量

当多次声明自定义属性时,标准规则有助于解决冲突,样式表中最后定义的会覆盖上面定义的。 下面的示例演示了用户动态操作改变属性是多么容易,同时仍然保持代码清晰简洁。

*{margin: 0;padding: 0;box-sizing: border-box;}
html{background: #eee;padding: 30px;font: 500 14px sans-serif;color: #333;line-height: 1.5;}
.blue-container{background: #64B5F6;padding-left: 50px;}
.green-container{background: #AED581;padding-left: 50px;}
.container{background: #fff;padding: 20px;}
p{transition: 0.4s;}
.title{font-weight: bold;}

.blue-container{
–title-text: 18px;
–main-text: 14px;
}
.blue-container:hover{
–title-text: 24px;
–main-text: 16px;
}
.green-container:hover{
–title-text: 30px;
–main-text: 18px;
}
.title{
font-size: var(–title-text);
}
.content{
font-size: var(–main-text);
}

这是个标题

将鼠标悬停在不同的颜色区域上可以更改此文本和标题的大小。

See the Pen 示例3 – 动态更改变量 by w3cbest.com (@w3cbest) on CodePen.

正如您所看到的,CSS变量非常简单易用,开发人员不必花费太多时间在各处开始应用它们。以下是扩展内容:

  • var()函数有两个参数,如果自定义属性失败,它可用于提供回退值: width: var(–custom-width, 20%);
  • 可以嵌套自定义属性: –base-color: #f93ce9; –background-gradient: linear-gradient(to top, var(–base-color), #444);
  • 变量可以与CSS的另一个新增功能- calc() 函数结合使用。 –container-width: 1000px; max-width: calc(var(–container-width) / 2);