前端开发 大前端 W3Cbest

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

0%

假如我们有一个多选和单选列表, 图片 我们从后端拿到数据后想在初始化的时候按照数据的值进行渲染,是选中的就显示选中状态,没有选中的就显示默认状态, 假如这是我们拿到的数据(理想中的数据应该是这样的),如下:

[
    { "code": "1", "name": "语文", "content": "1", "checked": false },
    { "code": "2", "name": "数学", "content": "2", "checked": true },
    { "code": "3", "name": "英语", "content": "3", "checked": true },
    { "code": "4", "name": "物理", "content": "4", "checked": false }
]

那么就很简单的了,就可以直接进行数据渲染,如下: html

<div class="form-check" id="formCheckBox"></div>

js

res = [
    { "code": "1", "name": "语文", "content": "1", "checked": false },
    { "code": "2", "name": "数学", "content": "2", "checked": true },
    { "code": "3", "name": "英语", "content": "3", "checked": true },
    { "code": "4", "name": "物理", "content": "4", "checked": false }
]
document.getElementById('formCheckBox').innerHTML = template('checkboxTemp', { data: res });

template

<script type="text/html" id="checkboxTemp">
{{each data item}}
<label for="checkbox{{item.code}}">
    <input class="form-check-input" type="checkbox" id="checkbox{{item.code}}" value="{{item.content}}" {{if item.checked == true}}checked{{/if}}> {{item.name}}
</label>
{{/each}}
</script>

如果拿到的数据是乱七八糟的后端给出来的怎么办,那我就需要处理一下数据,如下:

{"data": [
    { "code": "1", "name": "语文", "content": "1" },
    { "code": "2", "name": "数学", "content": "2" },
    { "code": "3", "name": "英语", "content": "3" },
    { "code": "4", "name": "物理", "content": "4" }
    ],
"select": [1, 4]
}

如果是这种的话我们就先渲染,然后再用JQ的each循环改变选中状态,如下: html

<div class="form-check" id="formCheckBox"></div>

js

res = {
    "data": [
        { "code": "1", "name": "语文", "content": "1" },
        { "code": "2", "name": "数学", "content": "2" },
        { "code": "3", "name": "英语", "content": "3" },
        { "code": "4", "name": "物理", "content": "4" }
    ],
    "select": [1, 4]
};
document.getElementById('formCheckBox').innerHTML = template('tpl', { data: res.data });
$('#formCheckBox label').each(function() {
    var cont = $(this).find('input').val();
    for (var i = 0; i < check.value.length; i++) {
        if (check.select[i] == val) {
            $(this).find('input').prop('checked', true)
        }
    }
})

template

<script type="text/html" id="tpl">
    {{each data}}
    <label for="checkbox{{$value.code}}">
    <input class="form-check-input" type="checkbox" id="checkbox{{$value.code}}" value="{{$value.content}}"> {{$value.name}}</label>
    {{/each}}
</script>

后记:其实拿到的数据基本上如果可以直接用的话就不必处理,但是有的数据是需要重新处理重新组合成我们用的那种结果,比如上面最后一个例子提到的,他有两个checkbox被选中,但是我们渲染的时候会用data数据渲染,那么你可以把select的两标记组合到data里面,如:

res = { 
    "data": [
        { 
            "code": "1", 
            "name": "语文", 
            "content": "1", 
            "select": true// 这个就是标记被选中
        },
        ....
    ]
}

这样就可以直接渲染数据了,方法有很多种,就看你怎么理解,想怎么用

现在你得导航还在用float来设置水平排列吗? 随着css的强大新的属性的增加,浏览器的内核升级,让我们的代码越写越简单,以前我们在写代码设置某一块水平排列用的最多的应该是float了吧,80后的程序员最有感受,深受低版本浏览器的残害(IE6),乃至现在还会随手敲一个float出来(包括我)。不过现在好了,随着浏览器的内核升级,很多企业也已抛弃低版本浏览器,css的很多新属性就可以用到了,我们今天就用这个新属性flex做个导航水平排列,如下:

  • 首页
  • 关于我们
  • 产品展示
  • 客户支持
  • 联系我们

html

css

.navbar{
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: stretch;
align-content: stretch;
}

Flex 是 Flexible Box 的缩写,意为”弹性布局”,用来为盒状模型提供最大的灵活性。 我们来测试一下他的兼容性

复制代码

在网站开发中我们时常用console.log来打印我们想要的数据,或返回的一些我们想要知道的结果,在PC端我们可以得心应手的使用,但是在移动端开发的时候,不能用F12了,无法看到代码,哪里出问题了也不知道,其实也可以用alert(),不过呢每刷新一次页面就会弹一次弹框,自我感觉比较麻烦,所以就有下面这段代码,虽然很简单,但是感觉挺实用的,代码如下:

// log打印
Tomd.log = function(s) {
if (typeof s == ‘object’) {
var s1 = ‘’;
for (var a in s) {
s1 += ‘{0}:{1}, ‘.format(a, s[a]);
}
s = s1;
}
var d = $(‘.dui-log’);
if (d.length == 0) {
$(‘body’).append(‘

‘);
d = $(‘.dui-log’);
}
d.append(‘

‘ + s + ‘

‘);
}
// 调用
Tomd.log(obj)

/*
* 格式化字符串
* 用法:’my name is {0}, age {1}’.format(‘Tom’, 30)
*/
String.prototype.format = function() {
var _this = this;
for (var i = 0; i < arguments.length; i++) {
_this = _this.replace(new RegExp(‘\\{‘ + i + ‘\\}’, ‘g’), arguments[i]);
}
return _this;
};

先来看这是一句常见的input类型为checkbox的复选框我们把它的值设置为“1”

```

``

1,如何获取选中的 jquery方法

$(“input[type=’checkbox’]“).attr(‘value’);
$(“input[type=’checkbox’]“).val();

js方法

document.getElementsByName(‘check’)[0].value;

2,如何设置为选中状态 jquery方法

$(“input[type=’checkbox’]“).attr(“checked”,”checked”);
$(“input[type=’checkbox’]“).attr(“checked”,true);
$(“input[type=’checkbox’]“).prop(“checked”,”checked”); //jquery1.6+
$(“input[type=’checkbox’]“).prop(“checked”,true); //jquery1.6+

js方法

document.getElementsByName(‘check’)[0].setAttribute(‘checked’, true);

3,如何获取是否选中 jquery方法

$(“input[type=’checkbox’]“).attr(‘checked);
$(“input[type=’checkbox’]“).prop(‘checked’); //推荐
$(“input[type=’checkbox’]“).is(‘:checked’);

js方法

document.getElementsByName(‘check’)[0].checked;

4,change()事件选中监听 jquery方法

$(“input[type=’checkbox’]“).change(function() {
console.log(“checked”);
});

js方法

document.getElementsByName(‘check’)[0].onchange = function(){
console.log(“checked”)
};

document.getElementsByName(‘check’)[0].addEventListener(‘click’, function(){
console.log(“checked”)
});

在今天,css预加载已经成为了前端开发的一个标准。预加载的一个主要优势就是可以让你使用变量。它可以让你避免复制黏贴你的代码,并且简化了开发和重构。 我们用预加载来存储颜色、字体偏好,布局的细节——几乎是我们在css里面用到的所有东西。 但是预加载的变量有一些使用上的限制:

  • 你不能动态的改变他们。
  • 他们不能认出DOM结构。
  • 他们不能用JavaScript读取和改变。

为了解决这样或那样的问题,社区发明了CSS自定义属性。本质上这看上去和实现起来就像CSS变量。并且他们的实现方式就像他们的名字一样。 自定义属性为前端开发打开了新的大门。

申明和使用自定义属性的语法

在你开始学习心得预处理器或者框架的使用通常的问题是你必须学习新的语法。 每一种预处理器都使用不同的方法申明变量。通常使用一个保留字符作为开始——举个例子,Sass的$、LESS的@。 CSS自定义属性同样也是用保留字符 –来引入声明。但是好处是你只需要学一遍语法就能在所有浏览器上使用。 你可能会问,“为什么不使用已经有的语法?” 这是有原因的。简单来说这提供了一种在任何预处理器中使用自定义属性的方式。在这种方式下我们可以使用自定义属性,预处理器也不会编译他们,所以这些属性会直接输出到编译后的CSS中。并且你也可以重复使用预处理器变量在源文件中,这个我稍后会细说。 (关于这个名字:因为他们的想法和目标非常相似,有些时候自定义属性被叫做CSS变量,尽管正确名称叫CSS自定义属性,往下读你就会明白为什么这个名字是最正确的。) 所以要声明一个变量来代替常用的CSS属性,就像color或者padding,用–连接一个自定义名称属性就可以:

.box{
–box-color: #4d4e53;
–box-padding: 0 10px;
}

属性的值可以是任何有效的CSS值:颜色、字符串、布局甚至是表达式。 这里是有效的自定义属性的例子:

:root{
–main-color: #4d4e53;
–main-bg: rgb(255, 255, 255);
–logo-border-color: rebeccapurple;

–header-height: 68px;
–content-padding: 10px 20px;

–base-line-height: 1.428571429;
–transition-duration: .35s;
–external-link: “external link”;
–margin-top: calc(2vh + 20px);

/* Valid CSS custom properties can be reused later in, say, JavaScript. */
–foo: if(x > 5) this.width = 10;
}

以防万一你不知道什么是:root匹配,在HTML里他就等同与html标签,但是具有更高的特异性。 自定义属性和其他的CSS属性一样是动态的、级联的。这意味着他们能在任何时候被改变是由浏览器来进行的。 为了使用自定义的变量,你需要使用 var() CSS函数,并且提供属性参数:

.box{
–box-color:#4d4e53;
–box-padding: 0 10px;

padding: var(–box-padding);
}

.box div{
color: var(–box-color);
}

声明和用例

var()函数有一个非常便利的提供默认值的方法。如果你不确信自定义属性已经被定义并且需要一个默认值,函数的第二个参数用来作为默认值的:

.box{
–box-color:#4d4e53;
–box-padding: 0 10px;

/* 10px is used because –box-margin is not defined. */
margin: var(–box-margin, 10px);
}

你可能会希望在声明新的变量的时候能重复使用已有的变量值:

.box{
/* The –main-padding variable is used if –box-padding is not defined. */
padding: var(–box-padding, var(–main-padding));

–box-text: ‘This is my box’;

/* Equal to –box-highlight-text:’This is my box with highlight’; */
–box-highlight-text: var(–box-text)’ with highlight’;
}

运算:+,-,*,/

既然我们习惯使用预处理器和其他语言,我们也希望在处理变量的时候能使用基本运算。为了达到这个目的,CSS提供了calc()函数,作用当自定义属性的值被改变的时候浏览器会重新计算表达式:

:root{
–indent-size: 10px;

–indent-xl: calc(2*var(–indent-size));
–indent-l: calc(var(–indent-size) + 2px);
–indent-s: calc(var(–indent-size) - 2px);
–indent-xs: calc(var(–indent-size)/2);
}

特别是当你想用一个没有单位的值得时候,就需要使用calc()函数:

:root{
–spacer: 10;
}

.box{
padding: var(–spacer)px 0; /* DOESN’T work */
padding: calc(var(–spacer)*1px) 0; /* WORKS */
}

作用域和继承

在讨论CSS自定义属性的作用域之前,我们先来回顾一下JavaScript和预处理器的作用域。这样就能更好的认识他们之间的区别。 我们知道在JavaScript中如果在函数中使用var关键字声明变量,那么他的作用域就在函数里面。 同样的我们可以使用letconst关键字,但他们的作用域相对于变量的块作用域。 在JavaScript中闭包(closure)是一个可以访问外部函数变量的函数——作用域链。闭包有三个作用域链:

  • 它自己的作用域(即 变量定义在大括号中)
  • 外部函数的变量
  • 全局变量

预处理器也是相同的,让我们用Sass来举个例子。因为这大概是今天最流行的预处理器了。 在Sass中有两种类型的变量:当前作用域变量(local) 和 全局变量。 一个全局变量能被申明在选择器和构造器(比如mixin)外,其他的变量就是当前作用域变量。 任何嵌套的代码块都可以访问封闭变量(如JavaScript)。 这意味着在Sass中变量的作用域完全依赖代码的嵌套结构。 然而CSS自定义属性完全和其他的CSS属性一样使用级联方式默认继承。 当然你也不能在CSS中定义一个在选择器外的属于全局的自定义属性变量,因为这不是有效的CSS。自定义属性的全局作用域实际上就是:root的作用域,这里面定义的属性就是全局的。 让我们用已知的语法知识和Sass的例子来创建一个使用原生的CSS自定义属性的例子,首先是HTML:

global

enclosing
closure

然后是CSS:

:root {
–globalVar: 10px;
}

.enclosing {
–enclosingVar: 20px;
}

.enclosing .closure {
–closureVar: 30px;

font-size: calc(var(–closureVar) + var(–enclosingVar) + var(–globalVar));
/* 60px for now */
}

See the Pen css-custom-properties-time-to-start-using 1 by Serg Hospodarets (@malyw) on CodePen.

对自定义属性的更改将立即应用于所有实例

到目前为止我们还没有看出他和Sass变量有什么区别。然而让我们重新给变量赋值: 在Sass中,是无效的:

.closure {
$closureVar: 30px; // local variable
font-size: $closureVar +$enclosingVar+ $globalVar;
// 60px, $closureVar: 30px is used

$closureVar: 50px; // local variable
}

See the Pen css-custom-properties-time-to-start-using 3 by Serg Hospodarets (@malyw) on CodePen.

但是在CSS中计算的值改变了。因为font-size的值因为--closureVar值得改变重新计算了:

.enclosing .closure {
–closureVar: 30px;
font-size: calc(var(–closureVar) + var(–enclosingVar) + var(–globalVar));
/* 80px for now, –closureVar: 50px is used */
–closureVar: 50px;
}

See the Pen css-custom-properties-time-to-start-using 2 by Serg Hospodarets (@malyw) on CodePen.

这是第一个非常大的区别:如果你对自定义属性重新赋值,浏览器会重新计算所有的变量和calc()表达式。

预处理器不能识别DOM结构

假如我们想在除了class是highlighted的div上使用默认的font-size 下面是 HTML代码:

default
default highlighted

让我们使用CSS自定义属性:

.highlighted {
–highlighted-size: 30px;
}

.default {
–default-size: 10px;

/* Use default-size, except when highlighted-size is provided. */
font-size: var(–highlighted-size, var(–default-size));
}

因为第二个div元素使用了highlighted类,在highlighted类上的属性就提供给这个元素了。 在这里就意味着,--hightlighted-size: 30px被提供了。是的font-size的属性被重新赋值了。 一切都是这么直截了当的运行:

See the Pen css-custom-properties-time-to-start-using 4 by Serg Hospodarets (@malyw) on CodePen.

接下来让我们尝试使用Sass来实现同样的例子:

.highlighted {
$highlighted-size: 30px;
}

.default {
$default-size: 10px;

/* Use default-size, except when highlighted-size is provided. */
@if variable-exists(highlighted-size) {
font-size: $highlighted-size;
}
@else {
font-size: $default-size;
}
}

结果显示他们都使用默认字体大小:

See the Pen css-custom-properties-time-to-start-using 5 by Serg Hospodarets (@malyw) on CodePen.

这是因为所有的Sass的计算和进程都发生在编译过程中,所以理所当然的他不知道DOM结构,所以依赖代码结构。 如你所见自定义属性在变量作用域和通常的css级联样式上具有优势。并且能够识别DOM结构。和普通的CSS属性使用相同的语法规则。 第二个例外是CSS自定义属性能动态的识别DOM结构

CSS关键字和all属性

CSS自定义属性遵守与常规CSS自定义属性相同的规则。这意味着您可以为其分配任何常见的CSS关键字:

  • inherit 此CSS关键字应用元素的父对象的值。
  • initial 这将应用CSS规范中定义的初始值(空值,或在某些CSS自定义属性的情况下)。
  • unset 在自定义属性中,如果属性是继承的,则应用继承的值,如果属性是初始化的值,则引用初始化值。
  • revert 这会将该属性重置为用户代理样式表建立的默认值(在CSS自定义属性的情况下为空值)。

以下是例子:

.common-values{
–border: inherit;
–bgcolor: initial;
–padding: unset;
–animation: revert;
}

我们来看另外一个例子。假设你想构建一个组件,并且想要确保没有其他样式或自定义属性被无意中应用(在这种情况下,通常会使用模块化的CSS解决方案)。 现在还有另一种方法:使用all CSS属性。这个简写将重置所有CSS属性。 与CSS关键字一起,我们可以执行以下操作:

.my-wonderful-clean-component{
all: initial;
}

这会为我们的组件重置所有的样式: 不幸的是,all关键字不会重置自定义属性关于是否添加 – 前缀,这将重置所有CSS自定义属性,正在进行讨论。 所以在将来,一个完整的重置会是这样的:

.my-wonderful-clean-component{
–: initial; /* reset all CSS custom properties */
all: initial; /* reset all other CSS styles */
}

CSS自定义属性用例

有许多自定义属性使用的方式,在这里我会展示他们中最有趣的部分。

模拟不存在的CSS规则

这些CSS变量的名称是自定义属性,那为什么我们不能用它来模拟不存在的CSS属性? 有很多比如translateX/Y/Z,background-repeat-x/y(仍然不能跨浏览器兼容),box-shadow-color。 让我们试着模拟最后一个属性。在这个例子里当hover的时候我们改变box-shadow的颜色。我们只想遵循DRY规则(不要重复你自:),所以我们只是改变它的颜色,而不是在:hover部分重复box-shadow的整个值。(变量的改变会重新计算var()calc()

.test {
–box-shadow-color: yellow;
box-shadow: 0 0 30px var(–box-shadow-color);
}

.test:hover {
–box-shadow-color: orange;
/* Instead of: box-shadow: 0 0 30px orange; */
}

See the Pen Emulating "box-shadow-color" CSS property using CSS Custom Properties by Serg Hospodarets (@malyw) on CodePen.

颜色主题

自定义属性有一个最常用的用例就是应用程序中的颜色主题。自定义属性就是用来解决这类问题的。所以,让我们为一个组件提供一个简单的颜色主题(应用程序可以遵循相同的步骤)。 这是button组件的代码

.btn {
background-image: linear-gradient(to bottom, #3498db, #2980b9);
text-shadow: 1px 1px 3px #777;
box-shadow: 0px 1px 3px #777;
border-radius: 28px;
color: #ffffff;
padding: 10px 20px 10px 20px;
}

我们假设要反转颜色主题。 第一步是将所有颜色变量扩展到CSS自定义属性并重写我们的组件。重写后的代码

.btn {
–shadow-color: #777;
–gradient-from-color: #3498db;
–gradient-to-color: #2980b9;
–color: #ffffff;

background-image: linear-gradient(
to bottom,
var(–gradient-from-color),
var(–gradient-to-color)
);
text-shadow: 1px 1px 3px var(–shadow-color);
box-shadow: 0px 1px 3px var(–shadow-color);
border-radius: 28px;
color: var(–color);
padding: 10px 20px 10px 20px;
}

这有我们需要的一切。使用它,我们可以将颜色变量重写为反转值,并在需要时应用它们。例如,我们可以添加全局inverted类(例如,body元素),并在应用颜色时更改颜色:

body.inverted .btn{
–shadow-color: #888888;
–gradient-from-color: #CB6724;
–gradient-to-color: #D67F46;
–color: #000000;
}

以下是一个演示,您可以在其中单击一个按钮来添加和删除全局类

See the Pen css-custom-properties-time-to-start-using 9 by Serg Hospodarets (@malyw) on CodePen.

如果不重复代码,在CSS预处理器中无法实现此行为。使用预处理器,您将始终需要覆盖实际的值和规则,这往往会导致额外的CSS。 使用CSS自定义属性,解决方案可以尽可能的干净,复制黏贴是可以避免的。因为只需要对变量进行重新赋值。

在JavaScript中使用自定义属性

以前,要将数据从CSS发送到JavaScript,我们经常不得不采取技巧,通过CSS输出中的纯JSON编写CSS值,然后从JavaScript读取它。 现在,我们可以轻松地使用JavaScript中的CSS变量进行交互,使用众所周知的.getPropertyValue().setProperty()方法读取和写入它们,这些方法用于通常的CSS属性:

/**
* Gives a CSS custom property value applied at the element
* element {Element}
* varName {String} without ‘–’
*
* For example:
* readCssVar(document.querySelector(‘.box’), ‘color’);
*/
function readCssVar(element, varName){
const elementStyles = getComputedStyle(element);
return elementStyles.getPropertyValue(`–${varName}`).trim();
}

/**
* Writes a CSS custom property value at the element
* element {Element}
* varName {String} without ‘–’
*
* For example:
* readCssVar(document.querySelector(‘.box’), ‘color’, ‘white’);
*/
function writeCssVar(element, varName, value){
return element.style.setProperty(`–${varName}`, value);
}

假设我们有一系列的媒体查询值

.breakpoints-data {
–phone: 480px;
–tablet: 800px;
}

因为我们只想在JavaScript中重用它们 - 例如,在Window.matchMedia()中,我们可以轻松地从CSS中获取它们

const breakpointsData = document.querySelector(‘.breakpoints-data’);

// GET
const phoneBreakpoint = getComputedStyle(breakpointsData)
.getPropertyValue(‘–phone’);

为了展示如何从JavaScript分配自定义属性,我创建了一个交互式3D CSS 立方体demo,以响应用户操作。 这不是很难我们只需要添加一个简单的背景,然后放置五个立方体面与transform属性的相关值:translateZ()translateY()rotateX()rotateY()。 为了提供正确的视角,我向页面添加了以下内容:

#world{
–translateZ:0;
–rotateX:65;
–rotateY:0;

transform-style:preserve-3d;
transform:
translateZ(calc(var(–translateZ) * 1px))
rotateX(calc(var(–rotateX) * 1deg))
rotateY(calc(var(–rotateY) * 1deg));
}

唯一缺少的是交互性。当鼠标移动时,演示应该更改X和Y视角(--rotateX-rotateY),当鼠标滚动(--translateZ)时应该放大和缩小)。 这是JavaScript的诀窍:

// Events
onMouseMove(e) {
this.worldXAngle = (.5 - (e.clientY / window.innerHeight)) * 180;
this.worldYAngle = -(.5 - (e.clientX / window.innerWidth)) * 180;
this.updateView();
};

onMouseWheel(e) {
/*…*/

this.worldZ += delta * 5;
this.updateView();
};

// JavaScript -> CSS
updateView() {
this.worldEl.style.setProperty(‘–translateZ’, this.worldZ);
this.worldEl.style.setProperty(‘–rotateX’, this.worldXAngle);
this.worldEl.style.setProperty(‘–rotateY’, this.worldYAngle);
};

现在,当用户移动鼠标时,演示会更改视图。您可以通过移动鼠标并使用鼠标滚轮放大和缩小来检查:

See the Pen css-custom-properties-time-to-start-using 10 by Serg Hospodarets (@malyw) on CodePen.

基本上,我们只是更改了CSS自定义属性的值。其他(旋转和放大和缩小)都是由CSS完成的。 提示:调整CSS自定义属性值的最简单方法之一就是在CSS生成的内容中显示其内容(在简单的情况下,例如使用字符串),以便浏览器将自动显示当前应用的值:

body:after {
content: ‘–screen-category : ‘var(–screen-category);
}

您可以在纯CSS演示(无HTML或JavaScript)中查看。 (调整窗口大小,查看浏览器会自动反映更改后的CSS自定义属性值。) 浏览器支持 主流浏览器都支持了CSS自定义属性: 这意味着你可以自己开始使用它们。 如果您需要支持旧版浏览器,您可以学习语法和使用示例,并考虑并行切换或使用CSS和预处理器变量的可能方法。 当然,我们需要能够检测CSS和JavaScript中的支持,以便提供回退或增强功能。 这很容易对于CSS,您可以使用带有虚拟功能查询的@supports条件

@supports ( (–a: 0)) {
/* supported */
}

@supports ( not (–a: 0)) {
/* not supported */
}

在JavaScript中,您可以使用与CSS.supports()静态方法相同的虚拟自定义属性:

const isSupported = window.CSS &&
window.CSS.supports && window.CSS.supports(‘–a’, 0);

if (isSupported) {
/* supported */
} else {
/* not supported */
}

我们看到,CSS自定义属性在每个浏览器中仍然不可用。知道这一点,您可以通过检查它们是否受支持来逐步增强您的应用程序。 例如,您可以生成两个主要的CSS文件:一个具有CSS自定义属性,另一个没有它们,其中属性是内联的(我们将在稍后讨论一些方法)。 默认加载第二个。然后,如果支持自定义属性,只需检查JavaScript并切换到增强版本即可:

// JavaScript
if(isSupported){
removeCss(‘without-css-custom-properties.css’);
loadCss(‘css-custom-properties.css’);
// + conditionally apply some application enhancements
// using the custom properties
}

这只是一个例子。往下看,有更好的选择。

如何开始使用它们

针对最近的一项调查,Sass已经成为了开发社区中预处理器的最佳选择。 所以,让我们考虑开始使用CSS自定义属性或使用Sass为他们做准备的方法。 我们有一些观点。

1. 手动检查代码支持

手动检查代码中自定义属性是否支持的方法的一个优点是如果它可行我们就可以直接用它(不要忘记我们已经切换到Sass):

$color: red;
:root {
–color: red;
}

.box {
@supports ( (–a: 0)) {
color: var(–color);
}
@supports ( not (–a: 0)) {
color: $color;
}
}

这种方法确实有许多缺点,其中不仅仅是代码变得复杂,而且复制和粘贴变得很难维护。

2. 使用自动转换CSS的插件

PostCSS生态系统今天提供了几十个插件。它们中的几个在生成的CSS输出中处理自定义属性(内联值),并使它们工作,假设您仅提供全局变量(即,您只声明或更改:根选择器中的CSS自定义属性),因此它们的值可以轻松内联。 其中一个例子就是postcss-custom-properties 这个插件提供了几个优点:它使语法工作;它与PostCSS的所有基础设施兼容;并且不需要太多的配置。 但是有一些缺点。该插件需要您使用CSS自定义属性,因此您没有准备项目以从Sass变量切换的路径。此外,您将无法对转换进行很多控制,因为在Sass被编译为CSS之后完成。最后,插件不提供很多调试信息。

3. css-vars Mixin

我开始在我大多数项目里使用CSS自定义属性并且尝试了很多策略:

  • cssnext从Sass切换到PostCSS
  • 从Sass变量切换到纯CSS自定义属性。
  • 在Sass中使用CSS变量来检测是否支持它们。

通过这些经验,我开始寻找一个可以满足我的标准的解决方案:

  • 它应该很容配合Sass来使用。
  • 应该直接使用,并且语法必须尽可能接近原生的CSS自定义属性。
  • 将CSS输出从内联值切换到CSS变量应该很容易。
  • 熟悉CSS自定义属性的团队成员将能够使用该解决方案。
  • 应该有一种方法有使用变量的调试信息。

因此,我创建了css-vars,一个Sass mixin,可以在Github上找到。使用它,你就可以使用CSS自定义属性语法。

使用 css-vars Mixin

声明变量,使用的mixin如下:

$white-color: #fff;
$base-font-size: 10px;

@include css-vars((
–main-color: #000,
–main-bg: $white-color,
–main-font-size: 1.5*$base-font-size,
–padding-top: calc(2vh + 20px)
));

使用这些变量,用var()函数:

body {
color: var(–main-color);
background: var(–main-bg, #f00);
font-size: var(–main-font-size);
padding: var(–padding-top) 0 10px;
}

这为您提供了一种从一个地方(从Sass)控制所有CSS输出并开始熟悉语法的方法。此外,您可以使用mixin重用Sass变量和逻辑。 当您想要支持的所有浏览器都使用CSS变量时,您需要做的就是添加:

$css-vars-use-native: true;

而不是调整生成的CSS中的变量属性,mixin将开始注册自定义属性,并且var()实例将转到生成的CSS而不进行任何转换。这意味着您将完全切换到CSS自定义属性,并具有我们讨论的所有优点。 如果你想打开有用的调试信息,如下:

$css-vars-debug-log: true;

这会给你

  • 当变量没有被定义却被使用的日志
  • 变量被重复定义的日志
  • 当默认值代替了未定义变量值的日志

结束语

现在你对CSS自定义属性有了更多的了解,包括语法、优势、一些有用的例子还有如何在JavaScript中进行交互 你需要知道如何确认他们是否被支持,他们和CSS预处理器的变量有什么区别,以及如何在浏览器支持之前开始使用原生的CSS变量。 这是开始使用CSS自定义属性并为浏览器支持做准备的最佳时机。

背景

在Flexbox Layout(柔性盒)模块(W3C候选推荐为2017年10月)的目的在于提供一种更有效的方式来布置,调整和项目之间在一个容器中分配空间,即使它们的大小是未知的和/或动态的(因此单词“flex”)。 flex布局背后的主要思想是让容器能够改变其项目的宽度/高度(和顺序),以最好地填充可用空间(主要是为了适应所有类型的显示设备和屏幕尺寸)。Flex容器扩展项目以填充可用空间,或缩小它们以防止溢出。 最重要的是,flexbox布局与方向无关,而不是常规布局(基于垂直的块和基于水平的内联块)。虽然这些页面适用于页面,但它们缺乏灵活性(没有双关语)来支持大型或复杂的应用程序(特别是在方向更改,调整大小,拉伸,缩小等方面)。 注意: Flexbox布局最适合应用程序的组件和小规模布局,而Grid布局则适用于更大规模的布局。

基础知识和术语

display

用来定义一个 flex 容器。如果设置为 flex 则容器呈现为块状元素,设置为inline-flex 则容器呈现为行内元素。它为所有直接子元素提供了 flex 上下文。

.container {
display: flex; /* or inline-flex */
}

请注意,CSS 列对 flex 容器没有影响。当然这是 Flexbox 布局的开始。

flex-direction

flex-direction 属性确立了主轴,从而定义了 flex 项在 flex 容器中的排布方向。 Flexbox 是单向布局,有些时候我们也称作一维布局。 可以将 flex 项视为主要沿着水平行或垂直列排布。

.container {
flex-direction: row row-reverse column column-reverse;
}

  • row (默认值) :行排布。在 ltr (left to right, 从左到右)排版方式下,flex 项从左到右排列,在 rtl (right to left, 从右到左)排版方式下,flex 项从右到左排列。
  • row-reverse: 反向行排布,即 row 的反方向,在 ltr 中从右向左,在 rtl 中从左到右。
  • column: 列排布,与 row 相似,但是 flex 项从上到下排布。
  • column-reverse: 反向列排布,即 column 反方向,与 row-reverse 相似,只是 flex 项从上到下排布。

flex-wrap

默认情况下,flex 项会尽可能地尝试排在同一行上(行或列),通过设置 flex-wrap 来决定 flex 项是否允需要换行。

.container{
flex-wrap: nowrap wrap wrap-reverse;
}

  • nowrap (默认值) : 所有的 flex 项都会在同一行上排布,也就是我们常说的单行,或不换行。
  • wrap: flex 项将从上到下根据实际情况排布再多行上,也就是我们常说的多行,或会换行。
  • wrap-reverse: flex 项将 从下到上 根据实际情况排布再多行上折行。

下面有个演示

  • 红色列表设置为 nowrap
  • 黄色列表设置为 wrap
  • 蓝色列表设置为 wrap-reverse

注意:将flex-direction的默认值设置为:row。

See the Pen Flex-wrap: demo by w3cbest.com (@w3cbest) on CodePen.

flex-flow (适用于父级 flex 容器)

这是 flex-direction 和 flex-wrap 属性的缩写形式。同时定义 flex 容器的主轴和交叉轴。默认是 row nowrap。

flex-flow: <’flex-direction’> <’flex-wrap’>

justify-content

justify-content 属性定义了flex 项沿主轴方向的对齐方式。 当一行中的所有 flex 项都是固定大小,或者是灵活大小但已经达到最大 main size 时,它可以帮助分配主轴上的剩余空间。当 flex 项溢出主轴的时候,它还可以用来控制flex 项的对齐方式。

.container {
justify-content: flex-start flex-end center space-between space-around space-evenly;
}

  • flex-start (默认值) : flex 项从主轴的开始位置(main-start)开始排布。
  • flex-end : flex 项从主轴的结束位置(main-end)开始排布
  • center: flex 项沿主轴居中排布。
  • space-between: flex 项沿主轴均匀排布,即我们常说的沿主轴 两端对齐 ,第一个flex 项在主轴开始位置,最后一个flex 项在主轴结束位置。
  • space-around: flex 项沿主轴均匀排布。要注意的是 flex 项看起来间隙是不均匀的,因为所有 flex 项两边的空间是相等的。第一项在容器边缘有一个单位的空间,但是在两个 flex 项之间有两个单位的间隙,因为每个 flex 项的两侧都有一个单位的间隙。
  • space-evenly: 任何两个 flex 项之间的间距(以及到 flex 容器边缘的空间)相等。

align-items

align-items 定义了 flex 项如何沿当前行在交叉轴上排布的默认行为。可以将其视为交叉轴(垂直于主轴)上的对齐方式。

.container {
align-items: flex-start flex-end center baseline stretch;
}

  • flex-start: flex 项按照交叉轴的开始位置(cross-start)对齐。
  • flex-end: flex 项按照交叉轴的结束位置(cross-end)对齐。
  • center: flex 项以交叉轴为中心,居中对齐。
  • baseline: flex 项按照他们的文字基线对齐。

stretch (默认值) : 拉伸 flex 项以填充整个容器(这里特别要注意:如果 flex 项有尺寸属性(min-width / max-width / width / min-height / max-height / height),那么首先应用这些尺寸属性。)

align-content

当交叉轴上有剩余空间时,align-content 可以设置 flex 容器中的 行 在交叉轴上如何分配剩余空间,类似于 justify-content 在主轴上对齐单个 flex 项的方式。

注意:当只有一行 flex 项时,此属性不起作用。

.container {
align-content: flex-start flex-end center space-between space-around stretch;
}

  • flex-start:多行在容器的开始位置排布
  • flex-end:多行在容器的结束位置排布
  • center:多行在容器的总结位置排布
  • space-between:多行均匀分布;第一行分布在容器的开始位置,最后一行分布在容器的结束位置
  • space-around: 多行均匀分布,并且每行的间距(包括离容器边缘的间距)相同;
  • strech (默认值):多行拉伸以填充满整个剩余空间

flex 项子属性(flex items)

order

默认情况下,flex 项按源(HTML结构)顺序排布。但是,order 属性可以控制它们在 flex 容器中的显示顺序。

.item {
order: ; /* 默认值是 0 */
}

flex-grow

flex-grow 定义了 flex 项在有可用剩余空间时拉伸比例。它接受的值作为比例,无单位。它规定了 flex 项应该占 flex 容器中可用空间的比例。 如果所有 flex 项的 flex-grow 都设置为 1 ,则父容器中的剩余空间将平均分配给所有子项。 如果其中一个子项的值为 2 ,则该子项占用的剩余空间是其他子项的两倍(或者至少会尽力获得)。

.item {
flex-grow: ; /* default 0 */
}

注:负值对于 flex-grow 无效。

flex-shrink

flex-shrink 定义了 flex 项的收缩的能力。(愚人码头注:与 flex-grow 拉伸正好相反,flex-shrink 决定 flex 项允许收缩多少比例。)

.item {
flex-shrink: ; /* default 1 */
}

注:负值对于 flex-shrink 无效。

flex-basis

flex-basis 定义了在分配剩余空间之前 flex 项默认的大小。可以设置为某个长度值(e.g. 20%, 5rem,等)或者关键字。关键字 auto 意味着 flex 项会按照其本来的大小显示(暂时由 main-size 关键字完成,直到弃用)。关键字 content 意味着根据内容来确定大小——这个关键字到目前没有被很好地支持,所以测试起来比较困难,与content的类似的关键字还有max-content, min-content, fit-content。

.item {
flex-basis: auto; /* default auto */
}

如果设置为 0 , 则 flex 项内容周围的空隙不会根据 flex-grow 按比例分配,如果设置为 auto,则 flex 项周围额外的空袭会根据 flex-grow 按照比例分配,如下图:

flex

flex 是 flex-grow、flex-shrink、flex-basis 三个属性的缩写。其中第二个和第三个参数(flex-shrink 和 flex-basis)是可选的。默认值为0 1 auto。

.item {
flex: none [ < ‘flex-grow’> < ‘flex-shrink’>? < ‘flex-basis’> ]
}

推荐使用缩写形式而不是单独地设置每一个属性,缩写形式中会更加智能地计算出相关值。

align-self

align-self 属性允许某个单独的 flex 项覆盖默认的对齐方式(或由 align-items 指定的对齐方式)。 具体的属性值得含义可以参考 align-items的解释。

.item {
align-self: auto flex-start flex-end center baseline stretch;
}

注:float,clear和vertical-align 对 flex 项没有任何作用。

示例

我们从一个非常非常简单的例子开始,解决一个几乎每天会遇到的问题:水平垂直居中。如果使用flexbox 布局会非常简单。

.parent {
display: flex;
height: 300px; /* 随意设定大小 */
}
.child {
width: 100px; /* 随意设定大小,比父元素要小 */
height: 100px; /* 同上 */
margin: auto; /* 见证奇迹的时刻 */
}

这依赖于在 flex 容器中设置 margin 为 auto 会自动吸收额外空间。 因此,设置水平垂直的margin都为 auto 会使flex 项在水平垂直方向上都完美居中。 现在我们考虑用更多的属性。考虑有 6 个有固定的尺寸的 flex 项,但是我们希望他们能够在改变浏览器宽度的时候仍然可以在水平轴上完美地显示(没有使用媒体查询(media queries))。

.flex-container {
/* 首先我们先创建一个flex布局上下文 */
display: flex;

/* 然后我们定义flex方向和是否允许flex 项换行
* 注意这与以下代码等价:
* flex-direction: row;
* flex-wrap: wrap;
*/
flex-flow: row wrap;

/* 然后我们定义在剩余空间上flex 项如何排布 */
justify-content: space-around;
}

完成。剩下的就是一些其他样式如颜色的设置了。下面是在 CodePen 中实现的这个例子。一定要去CodePen,并尝试调整你的窗口看看会发生什么。

See the Pen Demo Flexbox 1 by CSS-Tricks (@css-tricks) on CodePen.

让我们再尝试一些别的东西。假设我们有一个向右对齐的导航栏在我们网页的最上端,但是我们希望它在中屏上显示时为居中,在小屏上显示为单列。同样使用flex布局,实现起来会很简单。

/* 大屏 */
.navigation {
display: flex;
flex-flow: row wrap;
/* 这里设置对齐主轴方向的末端 */
justify-content: flex-end;
}

/* 中屏 */
@media all and (max-width: 800px) {
.navigation {
/* 当在中屏上,设置居中,并设置剩余空间环绕在flex 项左右 */
justify-content: space-around;
}
}

/* 小屏 */
@media all and (max-width: 500px) {
.navigation {
/* 在小屏上,我们不在使用行作为主轴,而以列为主轴 */
flex-direction: column;
}
}

See the Pen Demo Flexbox 2 by CSS-Tricks (@css-tricks) on CodePen.

我们通过灵活使用 flexbox 布局尝试一些更好玩的布局。来做一个移动优先的 3 列布局并带有全屏宽的header和footer。

.wrapper {
display: flex;
flex-flow: row wrap;
}

/* 我们要告诉所有的flex 项宽度 100% */
.wrapper > * {
flex: 1 100%;
}

/* 移动优先依赖于源代码默认的渲染顺序
* in this case:
* 1. header
* 2. nav
* 3. main
* 4. aside
* 5. footer
*/

/* 中屏 */
@media all and (min-width: 600px) {
/* 我们要告诉两边的sidebar共享一个行 */
.aside { flex: 1 auto; }
}

/* 大屏幕 */
@media all and (min-width: 800px) {
/* 通过order设定各个面板的渲染顺序
* 告诉主要面板元素占用侧栏两倍的空间
*/
.main { flex: 2 0px; }

.aside-1 { order: 1; }
.main { order: 2; }
.aside-2 { order: 3; }
.footer { order: 4; }
}

See the Pen Demo Flexbox 3 by Chris Coyier (@chriscoyier) on CodePen.

浏览器前缀

Flexbox 布局需要一些浏览器前缀来最大力度地兼容大多数的浏览器。Flex布局的前缀不只是在属性前面添加浏览器前缀,不同浏览器下的属性名和属性值都不同,这是因为Flexbox布局的标准一直在变,一共有 “old”, “tweener”, 和 “new” 三个版本。 可能处理前缀的最好方法是使用新的语法书写CSS并通过 Autoprefixer 运行CSS,能够很好地处理这个问题。 另外,这里有一个Sass中 @mixin 来处理一些前缀,也可以给你一些处理前缀的启发:

@mixin flexbox() {
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
}

@mixin flex($values) {
-webkit-box-flex: $values;
-moz-box-flex: $values;
-webkit-flex: $values;
-ms-flex: $values;
flex: $values;
}

@mixin order($val) {
-webkit-box-ordinal-group: $val;
-moz-box-ordinal-group: $val;
-ms-flex-order: $val;
-webkit-order: $val;
order: $val;
}

.wrapper {
@include flexbox();
}

.item {
@include flex(1 200px);
@include order(2);
}

相关属性

其他资源

Bugs

我见过的最棒的flexbox bug总结是Philip Walton 和 Greg Whitworth的Flexbugs,是开源的,你可以在上面跟踪动态。

浏览器支持

  • 首先看一下 flexbox 布局的三个版本
  • (new)是指标准中最近的语法(e.g. display:flex;)。
  • (tweener)是指2011年以后非官方的临时版本(e.g. display:flexbox;)。
  • (old)是指2009年以后的旧语法(e.g. display:box;)

复制代码

Blackberry browser 10+ 支持新语法。

更多混合使用语法达到最佳浏览器兼容,可以参考 这篇文章 (CSS-Tricks) 或者 这篇文章(DevOpera)。 原文链接:https://css-tricks.com/

var arr = [23, 3, 24, 67, 21, 60];

1 冒泡排序

for (let j = arr.length - 1; j > 0; j--) {
    for (let i = 0; i < j; i++) {
        if (arr[i] > arr[i + 1]) {
            let temp = arr[i];
            arr[i] = arr[i + 1];
            arr[i + 1] = temp
        }
    }
    console.log('第'+j+'次循环',arr);
}

2 选择排序

for (let j = 0; j<arr.length; j++){
    for (let i = 0; i < arr.length; i++) {
        if(arr[i] > arr[j]){
            let temp = arr[i];
            arr[i] = arr[j]
            arr[j] = temp
        }
    }
    console.log('第'+(j+1)+'次循环',arr);
}

Grid(网格) 布局最终使我们能够在CSS中定义网格,并将网格项放置到网格单元格中。这本身就很棒,但事实上我们不需要指定每个网格轨道,也不必手动放置每一个网格项。因为 Grid(网格) 布局足够灵活,可以适应它们的网格项。 这些都由所谓的显式和隐式网格来处理的。 这篇文章中所有代码示例都附有图片,以显示网格线和轨道。 如果你想自己修改代码,我建议你下载Firefox Nightly,因为它目前有调试网格最好的DevTools

显式的 Grid(网格)

我们可以使用 grid-template-rows,grid-template-columns 和 grid-template-areas 属性来定义形成网格的固定数量的网格线和网格轨道。这种手动定义的 Grid 称为显式网格。 具有4个垂直轨道(列)和2个水平轨道(行)的显式网格。

See the Pen CSS Grid Layout: The explicit grid by Manuel Matuzovic (@matuzo) on CodePen.

.grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-rows: 100px 100px;
grid-gap: 20px;
}

重复轨道

当我们定义grid-template-columns: 1fr 1fr 1fr 1fr; 时,我们得到4条垂直轨道,每条轨道的宽度为1fr。 我们可以通过使用repeat()表示法来自动化,如grid-template-columns: repeat(4, 1fr);。第一个参数指定重复次数,第二个参数指定轨道列表。 轨道列表? 是的,你实际上可以重复多个轨道。例如和上面代码等价的代码:grid-template-columns: repeat(2, 1fr 1fr);。

See the Pen CSS Grid Layout: Repeating track lists by Manuel Matuzovic (@matuzo) on CodePen.

自动重复轨道

一个带有4个垂直轨道的显式网格,每个100px宽,由重复表示法生成。

See the Pen CSS Grid Layout: Auto-fitting by Manuel Matuzovic (@matuzo) on CodePen.

repeat()函数非常有用,但它可以进一步自动化。我们可以使用auto-fillauto-fit关键字,来替代设置固定数量的重复。

自动填充轨道(auto-fill)

auto-fill 关键字创建适合网格容器的轨道数,而不会导致网格溢出。 重复放入宽度为100px的垂直轨道,以适应网格容器。

See the Pen CSS Grid Layout: auto-fill by Manuel Matuzovic (@matuzo) on CodePen.

.grid {
display: grid;
grid-template-columns: repeat(auto-fill, 100px);
grid-gap: 20px;
}

注意:repeat(auto-fill, 1fr); 只会创建一个轨道,因为宽度为1fr的单个轨道已经填满了整个网格容器。

自动调整轨道(auto-fit)

auto-fit关键字的行为与auto-fill相同,只是在网格项目放置后,它只会根据需要创建任意数量的轨道,并且任何空的重复轨道都会折叠在一起。

.grid {
display: grid;
grid-template-columns: repeat(auto-fit, 100px);
grid-gap: 20px;
}

上例的代码,使用repeat(auto-fit, 100px); 和repeat(4, 100px)创建的网格看上去相同。当有超过4个网格项时,就可以看到差异了。 如果有更多网格项,则auto-fit会创建更多列。 在 repeat() 中使用 auto-fit 关键词可根据需要创建尽可能多的轨道,并将轨道尽可能多地放入网格容器中。

See the Pen CSS Grid Layout: auto-fit by Manuel Matuzovic (@matuzo) on CodePen.

另一方面,如果在repeat()中使用固定数量的垂直轨道,并且网格项数超过该值,则添加更多行。您可以在下一节中阅读更多相关内容:隐式网格 如果网格项多于垂直轨道,则会添加更多行。

See the Pen CSS Grid Layout: implicit rows by Manuel Matuzovic (@matuzo) on CodePen.

为方便起见,我在上面的示例中使用了grid-template-columns,但所有规则也适用于grid-template-rows

.grid {
display: grid;
grid-template-columns: repeat(auto-fill, 100px);
grid-template-rows: repeat(auto-fill, 100px);
grid-gap: 20px;
height: 100%;
}

html, body {
height: 100%;
}

在两个轴(行和列)上使用auto-fill关键字重复表示法。

See the Pen CSS Grid Layout: auto-fill on both axes by Manuel Matuzovic (@matuzo) on CodePen.

隐式的 Grid(网格)

如果网格项的数量多于网格单元格,或者网格项位于显式网格外部,则网格容器会通过向网格添加网格线自动生成网格轨道。 显式网格与这些额外的隐式轨道和网格线线一起形成所谓的隐式网格。 两个网格项放置在显式网格之外,导致创建隐式网格线条和网格轨道。

See the Pen CSS Grid Layout: implicit tracks by Manuel Matuzovic (@matuzo) on CodePen.

.item:first-child {
grid-column-start: -1;
}

.item:nth-child(2) {
grid-row-start: 4;
}

隐式轨道的宽度和高度是自动设置。它们的大小足以适合放置的网格项,但可以更改其默认行为。

调整隐式轨道

grid-auto-rowsgrid-auto-columns属性使我们可以控制隐式轨道的大小。

.grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: 100px 100px;
grid-gap: 20px;
grid-auto-columns: 200px;
grid-auto-rows: 60px;
}

无论网格项是否适合,隐式轨道现在总是具有200px的宽度和60px的高度。 固定宽度和高度的隐式轨道。

See the Pen CSS Grid Layout: Sizing implicit tracks by Manuel Matuzovic (@matuzo) on CodePen.

通过使用 minmax() 函数来指定范围,可以使调整隐式轨道更灵活。

.grid {
grid-auto-columns: minmax(200px, auto);
grid-auto-rows: minmax(60px, auto);
}

隐式轨道现在至少有200px宽和60px高,但如果内容需要它将会扩展。

将网格扩展到开始

隐式轨道可能不仅仅被添加到显式网格的末尾。也可能扩展到显式网格的开始处。 一个隐式网格,由一行和一列扩展到开头

See the Pen CSS Grid Layout: Implicit grid by Manuel Matuzovic (@matuzo) on CodePen.

.item:first-child {
grid-row-end: 2;
grid-row-start: span 2;
}

.item:nth-child(2) {
grid-column-end: 2;
grid-column-start: span 2;
}

每个网格项在第二行结束,并跨越2个单元格(一个垂直,另一个水平)。 由于在第二行之前只有一个单元,因此在每一侧的开始处将另一个隐式轨道添加到网格中。

自动放置

如前所述,如果项目数超过单元格数,也会添加隐式轨道。 默认情况下,自动放置算法通过连续填充每一行来放置网格项,并根据需要添加新行。 我们可以使用 grid-auto-flow 属性来指定如何把网格项自动放置到网格容器。 如果网格项数超过单元格数量,则添加新列而不是行。

See the Pen CSS Grid Layout: grid-auto-flow by Manuel Matuzovic (@matuzo) on CodePen.

.grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: 100px 100px;
grid-gap: 20px;
grid-auto-flow: column;
}

使用网格项填充列,不是填充行,并创建其他隐式列。

未定义显式网格

由于可以使用grid-auto-rowsgrid-auto-columns自动调整单元格大小,因此不必定义显式网格。 没有显式行和轨道的隐式网格。

See the Pen CSS Grid Layout: Implicit grid only by Manuel Matuzovic (@matuzo) on CodePen.

.grid {
display: grid;
grid-auto-columns: minmax(60px, 200px);
grid-auto-rows: 60px;
grid-gap: 20px;
}

.item:first-child {
grid-row: span 2;
}

.item:nth-child(2) {
grid-column: 1 / span 2;
}

.item:nth-child(5) {
grid-column: 3;
}

完全依赖于隐式网格可能会变得混乱和难以理解。我们建议与显式网格相结使用。 在此示例中,第一个网格项自动放置并跨越 2 行,第二个项目显式放置在第一列中,并跨越 2 列创建第二个垂直轨道。 第三和第四网格项实际上都会自动放在第四行,但第五网格项明确地放在先前不存在的第三列中。 这将创建第三个垂直轨道,并且由于网格自动放置,第三个项目向上移动一行以填充空间。

总结

本文并未涵盖有关显式和隐式网格的所有内容,它应该为您提供的不仅仅是对该概念的深入理解。 了解创建隐式网格线和隐式轨迹的原因和方式对于使用网格布局至关重要。 你可以在 CodePen 集合 中找到本文中使用的所有示例。 文章来源:https://css-tricks.com

很难追踪 JavaScript(ECMAScript)中的新功能。 想找到有用的代码示例更加困难。 因此,在本文中,我将介绍 TC39 已完成 ES2016,ES2017 和 ES2018(最终草案)提案中所有添加的 18 个功能,并展示有用的示例。 很难跟踪各种版本的ECMAScript中的新内容,而且如果没有跳转就更难获得有用的示例。因此,在本文中,我将介绍在ES2016,ES2017和ES2018(最终草案)中添加的TC39完成提案中列出的所有18个功能,并向他们展示有用的示例。

ECMAScript 2016

1.Array.prototype.includes

includes 是 Array 上的一个简单实例方法,有助于轻松查找某项元素是否在数组中(包括NaN ,与 indexOf不同)。 人们想要 contains 来命名该规范,但显然 Mootools 已经使用过这个命名,所以使用了 includes。

2.指数运算符

加法和减法等数学运算分别具有 + 和 - 等中缀运算符。与它们类似,** 中缀运算符通常用于指数运算。在 ECMAScript 2016 中,引入了 ** 代替 Math.pow 。

ECMAScript 2017

1.Object.values()

Object.values() 是一个与 Object.keys() 类似的新函数,但返回 Object 自身属性的所有值,不包括原型链中的任何值。

2.Object.entries()

Object.entries() 与 Object.keys 相关,但它不仅仅返回 keys ,而是以数组方式返回 keys 和 values 。这使得在循环中使用对象或将对象转换为 Maps 等操作变得非常简单。

3.字符串填充

String 中添加了两个实例方法,String.prototype.padStart 和 String.prototype.padEnd – 允许将空字符串或其他字符串附加到原始字符串的开头或结尾。

‘someString’.padStart(numberOfCharcters [,stringForPadding]);
‘5’.padStart(10) // ‘ 5’
‘5’.padStart(10, ‘=*‘) //‘=*=*=*=*=5’
‘5’.padEnd(10) // ‘5 ‘
‘5’.padEnd(10, ‘=*‘) //‘5=*=*=*=*=’

3.1 padStart 示例

在下面的示例中,我们列出了不同长度的数字。我们希望前置“0”,以便所有项具有相同的 10 位数长度显示。我们可以使用 padStart(10, ‘0’) 轻松实现这一目标。

3.2 padEnd 示例

当我们打印不同长度的多个项并希望正确对齐它们时,padEnd 真的很方便。 下面的示例是 padEnd,padStart 和 Object.entries 组合在一起以产生漂亮输出的一个很好的现实示例。

const cars = {
‘?BMW’: ‘10’,
‘?Tesla’: ‘5’,
‘?Lamborghini’: ‘0’
}
Object.entries(cars).map(([name, count]) => {
//padEnd appends ‘ -‘ until the name becomes 20 characters
//padStart prepends ‘0’ until the count becomes 3 characters.
console.log(`${name.padEnd(20, ‘ -‘)} Count: ${count.padStart(3, ‘0’)}`)
});
//Prints..
// ?BMW - - - - - - - Count: 010
// ?Tesla - - - - - - Count: 005
// ?Lamborghini - - - Count: 000

3.3 ⚠️padStart 和 padEnd 用于 Emojis 表情和其他双字节字符

Emojis 和其他双字节字符使用多个字节的 unicode 表示。 所以 padStart 和 padEnd 可能无法按预期工作!⚠️ 例如:假设我们把字符串 heart 通过 emoji表情 ❤️ 使用 padStart 延长到十个字节,这个时候我们得到如下的输出:

//Notice that instead of 5 hearts, there are only 2 hearts and 1 heart that looks odd!
‘heart’.padStart(10, “❤️”); // prints.. ‘❤️❤️❤heart’

这是因为 ❤️ 本身占据两个字节(\u2764\uFE0F),而 heart 本身有 5 个字节,所以我们只剩 5 个字节的位置可以填充,JS使用\u2764\uFE0F 来填充两颗心并产生 ❤️❤️ 。对于最后一个,它只使用 heart \u2764 的第一个字节产生 ❤ 所以我们最终得到:❤️❤️❤heart

4.Object.getOwnPropertyDescriptors

此方法返回给定对象的所有属性的所有详细信息(包括 getter get 和 setter set方法)。 添加它的主要动机是允许浅复制/克隆对象到另一个对象,该对象也复制 getter 和 setter 函数而不像 Object.assign。 Object.assign 浅复制除原始源对象的 getter 和 setter 函数之外的所有信息。 下面的示例显示了 Object.assign 和 Object.getOwnPropertyDescriptors 以及 Object.defineProperties 之间的区别,以将原始对象 Car 复制到新对象 ElectricCar 中。 你将看到,通过使用 Object.getOwnPropertyDescriptors,discount getter 和 setter 函数也会复制到目标对象中。 之前… 以后…

var Car = {
name: ‘BMW’,
price: 1000000,
set discount(x) {
this.d = x;
},
get discount() {
return this.d;
},
};
//Print details of Car object’s ‘discount’ property
console.log(Object.getOwnPropertyDescriptor(Car, ‘discount’));
//prints..
// {
// get: [Function: get],
// set: [Function: set],
// enumerable: true,
// configurable: true
// }
//Copy Car’s properties to ElectricCar using Object.assign
const ElectricCar = Object.assign({}, Car);
//Print details of ElectricCar object’s ‘discount’ property
console.log(Object.getOwnPropertyDescriptor(ElectricCar, ‘discount’));
//prints..
// {
// value: undefined,
// writable: true,
// enumerable: true,
// configurable: true

// }
//⚠️Notice that getters and setters are missing in ElectricCar object for ‘discount’ property !??
//Copy Car’s properties to ElectricCar2 using Object.defineProperties
//and extract Car’s properties using Object.getOwnPropertyDescriptors
const ElectricCar2 = Object.defineProperties({}, Object.getOwnPropertyDescriptors(Car));
//Print details of ElectricCar2 object’s ‘discount’ property
console.log(Object.getOwnPropertyDescriptor(ElectricCar2, ‘discount’));
//prints..
// { get: [Function: get], ??????
// set: [Function: set], ??????
// enumerable: true,
// configurable: true
// }
// Notice that getters and setters are present in the ElectricCar2 object for ‘discount’ property!

5.在函数参数中添加尾逗号

这是一个小更新,允许我们在函数最后一个参数后面有逗号。 为什么? 帮助使用像 git blame 这样的工具,防止添加一个参数却需要修改两行代码。 以下示例显示了问题和解决方案。

6. Async/Await

到目前为止,这个特性应该是目前为止是最重要和最有用的功能。async 函数解决了回调地狱的问题,并使整个代码看起来简单。 async 关键字告诉 JavaScript 编译器以不同方式处理函数。 只要到达该函数中的 await 关键字,编译器就会暂停。 它假定 await 之后的表达式返回一个 promise 并等待,直到 promise 被 resolved 或被 rejected ,然后才继续执行。 在下面的示例中,getAmount 函数调用两个异步函数 getUser 和 getBankBalance。 我们可以用 Promise 做到这一点,但是使用 async await 更加优雅和简单。

6.1 Async 函数本身返回一个 Promise 。

如果你正在等待 async 函数的结果,则需要使用 Promise 的 then 语法来捕获其结果。 在以下示例中,我们希望使用 console.log 但不在 doubleAndAdd 中记录结果。 所以我们想等待并使用 then 语法将结果传递给console.log 。

6.2 并行调用 async/await

在前面的例子中,我们调用 await 两次,但每次我们等待一秒钟(总共2秒)。相反,我们可以并行调用它,因为使用 Promise.all 并行调用 a 和 b 。

6.3 async/await 函数的错误处理

使用 async/await 时,有多种方法可以处理错误。

选项1-在函数中使用try catch

//Option 1 - Use try catch within the function
async function doubleAndAdd(a, b) {
try {
a = await doubleAfter1Sec(a);
b = await doubleAfter1Sec(b);
} catch (e) {
return NaN; //return something
}
return a + b;
}
//?Usage:
doubleAndAdd(‘one’, 2).then(console.log); // NaN
doubleAndAdd(1, 2).then(console.log); // 6
function doubleAfter1Sec(param) {
return new Promise((resolve, reject) => {
setTimeout(function() {
let val = param * 2;
isNaN(val) ? reject(NaN) : resolve(val);
}, 1000);
});
}

选项2-捕获(Catch) await 表达式

由于每一个 await 表达式返回的都是 Promise,我们可以直接在每一行上面添加 catch。

//Option 2 - *Catch* errors on every await line
//as each await expression is a Promise in itself
async function doubleAndAdd(a, b) {
a = await doubleAfter1Sec(a).catch(e => console.log(‘“a” is NaN’)); // ?
b = await doubleAfter1Sec(b).catch(e => console.log(‘“b” is NaN’)); // ?
if (!a !b) {
return NaN;
}
return a + b;
}
//?Usage:
doubleAndAdd(‘one’, 2).then(console.log); // NaN and logs: “a” is NaN
doubleAndAdd(1, 2).then(console.log); // 6
function doubleAfter1Sec(param) {
return new Promise((resolve, reject) => {
setTimeout(function() {
let val = param * 2;
isNaN(val) ? reject(NaN) : resolve(val);
}, 1000);
});
}

选项3-捕获(Catch) 整个async-await函数

//Option 3 - Dont do anything but handle outside the function
//since async / await returns a promise, we can catch the whole function’s error
async function doubleAndAdd(a, b) {
a = await doubleAfter1Sec(a);
b = await doubleAfter1Sec(b);
return a + b;
}
//?Usage:
doubleAndAdd(‘one’, 2)
.then(console.log)
.catch(console.log); // ???<——- use “catch”
function doubleAfter1Sec(param) {
return new Promise((resolve, reject) => {
setTimeout(function() {
let val = param * 2;
isNaN(val) ? reject(NaN) : resolve(val);
}, 1000);
});
}

ECMAScript 2018

1. 共享内存和 atomics

这是一个巨大的,非常先进的功能,并且是对 JS 引擎的核心增强。 这个特性的主要目的是给 JavaScript 提供多线程功能,以便JS开发人员通过自己管理内存来编写高性能的并发程序,而不是让JS引擎管理内存。 这是通过一种名为SharedArrayBuffer的新型全局对象完成的,该对象实质上将数据存储在共享内存空间中。因此,这些数据可以在主JS线程和 Web-worker 线程之间共享。 之前,如果我们想在主JS线程和 web-worker 之间共享数据,我们必须复制数据并使用postMessage将其发送到另一个线程。 现在,你只需使用SharedArrayBuffer,主线程和多个web-worker线程都可以立即访问数据。 但是在线程之间共享内存会导致竞争条件(即多个进程同时操作一个内存)。为了帮助避免竞争条件,引入了Atomics全局对象。 Atomics提供了各种方法来在线程使用其数据时锁定共享内存。它还提供了安全地更新共享内存中的此类数据的方法。 建议通过某个库使用此功能,但是现在没有基于此功能构建的库。 如果你有兴趣,我建议阅读:

  1. From Workers to Shared Memor_y — _lucasfcosta
  2. A cartoon intro to SharedArrayBuffers_ — _Lin Clark
  3. Shared memory and atomics_ — _Dr. Axel Rauschmayer

2.移除了标记模板字面量的限制

首先,我们需要澄清“标记模板字面量”是什么,以便我们更好地理解这个功能。 在 ES2015+ 中,有一个称为标记模板文字的功能,允许开发人员自定义字符串的插值方式。 例如,在标准方式中,字符串被插入如下… 在标记的字面量中,你可以编写一个函数来接收字符串字面量的硬编码部分,例如 [‘Hello’,’!’] 并且替换变量,例如 [‘Raja’] ,作为参数进入一个自定义函数(例如 greet ),并从该自定义函数返回任何你想要的内容。 下面的示例显示我们的自定义 “Tag” 函数 greet,如“Good Morning” “Good afternoon”,等等,取决于当天到字符串字面量的时间,并返回自定义字符串。

//A “Tag” function returns a custom string literal.
//In this example, greet calls timeGreet() to append Good //Morning/Afternoon/Evening depending on the time of the day.
function greet(hardCodedPartsArray, …replacementPartsArray) {
console.log(hardCodedPartsArray); //[ ‘Hello ‘, ‘!’ ]
console.log(replacementPartsArray); //[ ‘Raja’ ]
let str = ‘’;
hardCodedPartsArray.forEach((string, i) => {
if (i < replacementPartsArray.length) {
str += `${string} ${replacementPartsArray[i] ‘’}`;
} else {
str += `${string} ${timeGreet()}`; //<– append Good morning/afternoon/evening here
}
});
return str;
}
//?Usage:
const firstName = ‘Raja’;
const greetings = greet `Hello ${firstName}!`; //??<– Tagged literal
console.log(greetings); //‘Hello Raja! Good Morning!’ ?
function timeGreet() {
const hr = new Date().getHours();
return hr < 12 ?
‘Good Morning!’ :
hr < 18 ? ‘Good Afternoon!’ : ‘Good Evening!’;
}

现在我们讨论了 “Tagged” 函数是什么,许多人想要在不同的场景下中使用此功能,例如在终端中使用命令和 HTTP 请求来编写 URI ,等等。

⚠️标签字符串模版存在的问题

ES2015 和 ES2016 规范不允许使用转义字符,如 \u(unicode),\x(十六进制),除非它们看起来完全像 \u00A9 或 \u{2F804} 或 \xA9 。 因此,如果你有一个内部使用其他域规则(如终端规则)的 Tagged 函数,可能需要使用 \ubla123abla ,而不能是 \u0049 或 \u{@F804} ,这样你会得到一个语法错误。 在 ES2018 中,只要 Tagged 函数返回具有 “cooked” 属性(无效字符为 “undefined” )的对象中的值,然后是 “raw” 属性( 无论你想要什么)。

function myTagFunc(str) { return { “cooked”: “undefined”, “raw”: str.raw[0] }} var str = myTagFunc `hi \

3. 正则表达式中的 ‘dotall’ 标记

目前在RegEx中,点(“.”)可以表示任何的单一字符,但它不能与 \n , \r,\f 等换行符匹配。 例如:

//Before
/first.second/.test(‘first\nsecond’); //false

此增强功能使点运算符可以匹配任何单个字符。为了确保不会破坏任何内容,我们需要在创建RegEx时使用 \s 标记才能使其正常工作。

//ECMAScript 2018
/first.second/s.test(‘first\nsecond’); //true Notice: /s ??

4. 正则表达式捕获命名组

这个增强功能带来了其他语言(如Python,Java等)的有用 RegExp 功能,称为“命名组”。这个功能允许允许正则表达式给每一个捕获组起一个名字(?…),然后,我们可以使用该名称轻松获取我们需要的任何群组。

4.1 基本的命名组例子

在下面的例子中,我们使用(?) (?) and (?year)来为正则表达式中的不同部分分组,结果对象中会包含一个 groups 属性,其拥有 year month day 三个对象。

4.2 在正则表达式本身内使用命名组

我们可以使用\k格式来反向引用正则表达式本身中的组。以下示例显示了它的工作原理。

4.3 在 String.prototype.replace 中使用命名组

命名组也可以在 String 的 replace 方法中使用,比如用来交换一个字符串中各个部分的位置。 例如,将firstName, lastName 更改为 lastName, firstName。

5. 对象的剩余属性

Rest 运算符 …(三个点)允许我们提取 Object 的剩余属性。

5.1 我们可以使用展开运算符展开我们想要的属性:

5.2 或者我们借助展开运算符,移除我们不想要的属性 ??

6.展开对象的属性

展开属性看起来就像 Rest 运算符,都是三个点 …,但不同之处在于你使用展开操作符来创建(重构)新对象。 提示:展开(spread)运算符用于等号的右侧。剩余(Rest)运算符用在等号的左侧。

7.正则表达式后行断言(Lookbehind)

这是 RegEx 的一个增强,它允许我们确保某些子字符串恰好出现在某些子字符串之前。 你现在可以使用一个组 (?<=…)(问号,小于,等于)来查看先行断言。 此外,你可以使用 (?<!…)(问号,小于,感叹号)来查看后行断言。基本上,只要-ve断言通过,这将匹配。 肯定断言:假设我们要确保 # 符号存在于 winning 之前(即:#winning),并希望正则表达式只返回字符串 “winning” 。下面是我们的做法: 否定断言:假设我们想要从具有 € 符号的行中提取数字,而不是 $ 。

8. RegExp Unicode属性转义

编写 RegEx 以匹配各种 unicode 字符并不容易。像 \w,\W,\d 等只匹配英文字符和数字。但是其他语言中的数字如印地语,希腊语等等该怎么办呢? 这就是 Unicode 属性转义的用武之地。事实证明,Unicode 为每个符号(字符)添加元数据属性,并使用它来分组或表征各种符号。 例如,Unicode 数据库将所有印地语字符(??????)归为一个名为 Script 的属性,其值为 Devanagari ,另一个属性为Script_Extensions,其值为 Devanagari 。所以我们可以搜索 Script=Devanagari 并获得所有印地语字符。 梵文可以用于各种印度语言,如马拉地语,印地语,梵语等。 从 ECMAScript 2018 开始,我们可以使用 \p 来转义字符以及 {Script = Devanagari} 以匹配所有这些印度字符。也就是说,我们可以在 RegEx 中使用:\p{Script=Devanagari} 来匹配所有梵文字符。

//The following matches multiple hindi character
/^\p{Script=Devanagari}+$/u.test(‘हिन्दी’); //true
//PS:there are 3 hindi characters h

同样,Unicode 数据库将 Script_Extensions(和 Script )属性下的所有希腊字符组合为希腊语。 所以我们可以使用 Script_Extensions=Greek 或 Script=Greek 搜索所有希腊字符。 也就是说,我们可以在RegEx中使用: \p{Script=Greek} 来匹配所有希腊字符。

//The following matches a single Greek character
/\p{Script_Extensions=Greek}/u.test(‘π’); // true

此外,Unicode数据库在布尔属性 Emoji ,Emoji_Component, Emoji_Presentation ,Emoji_Modifier 和 Emoji_Modifier_Base 下存储各种类型的 Emojis,其属性值为 true。 因此,我们只需选择 Emoji 符号即可搜索所有表情符号。 也就是说,我们可以使用:\p{Emoji},\Emoji_Modifier 等来匹配各种 Emojis 。 以下示例将使一切清楚。

//The following matches an Emoji character
/\p{Emoji}/u.test(‘❤️’); //true
//The following fails because yellow emojis don’t need/have Emoji_Modifier!
/\p{Emoji}\p{Emoji_Modifier}/u.test(‘✌️’); //false
//The following matches an emoji character\p{Emoji} followed by \p{Emoji_Modifier}
/\p{Emoji}\p{Emoji_Modifier}/u.test(‘✌?’); //true
//Explaination:
//By default the victory emoji is yellow color.
//If we use a brown, black or other variations of the same emoji, they are considered
//as variations of the original Emoji and are represented using two unicode characters.
//One for the original emoji, followed by another unicode character for the color.
//
//So in the below example, although we only see a single brown victory emoji,
//it actually uses two unicode characters, one for the emoji and another
// for the brown color.
//
//In Unicode database, these colors have Emoji_Modifier property.
//So we need to use both \p{Emoji} and \p{Emoji_Modifier} to properly and
//completely match the brown emoji.
/\p{Emoji}\p{Emoji_Modifier}/u.test(‘✌?’); //true

最后,我们可以使用大写“P”( \P )转义字符,而不是小写“p”( \p )来否定匹配。 参考阅读:

  1. ECMAScript 2018 Proposal
  2. https://mathiasbynens.be/notes/es-unicode-property-escapes

9.Promise.prototype.finally()

finally() 是一个添加到 Promise 实例的新方法。 主要考虑是允许在 resolve 或 reject 调用之后执行一些清理性质的代码。finally 被执行的时候不会被传入任何函数,并且无论什么时候都会被执行。 我们来看看各种情况。

10.异步迭代

这是一个非常有用的特性。 基本上它允许我们轻松创建异步代码循环! 此特性添加了一个新的“for-await-of”循环,允许我们在循环中调用返回 promises(或带有一堆 promise 的 Arrays )的异步函数。 循环会等待每个 Promise 在进行下一个循环之前 resolve 。

相关阅读

文章来源:https://school.geekwall.in

不论是在网页设计中,还是在其他传统媒介中(比如杂志和墙纸等),各种尺寸、颜色、角度的条纹图案在视觉设计中无处不在。要想在网页中实现条纹图案,其过程还远远不够理想。通常,我们的方法是创建一个单独的位图文件,然后每次需要做些调整时,都用图像编辑器来修改它。可能有人试过用 SVG 来取代位图,但这样还是会有一个独立的文件,而且它的语法也远远不够友好。如果可以直接在 CSS 中创建条纹图案,那该有多棒啊!你可能会惊讶地发现,我们居然真的可以。

解决方案

假设我们有一条基本的垂直线性渐变,颜色从 #fb3 过渡到 #58a(参见图 1):

这是一个示例 (图1)

渐变现在出现在总高的 60% 区域,剩下的部分显示为实色;色标的位置用虚线标示出来了  

background: linear-gradient(#fb3, #58a);

现在,让我们试着把这两个色标拉近一点(参见图 2):

这是一个示例 (图2)

 

background: linear-gradient(#fb3 20%, #58a 80%);

现在容器顶部的 20% 区域被填充为 #fb3 实色,而底部 20% 区域被填充为 #58a 实色。真正的渐变只出现在容器 60% 的高度区域。如果我们把两个色标继续拉近(分别改为 40% 和 60%,参见图 3),那真正的渐变区域就变得更窄了。你是不是开始好奇,如果我们把两个色标重合在一起,会发生什么?

这是一个示例 (图3)

 

background: linear-gradient(#fb3 50%, #58a 50%);

“如果多个色标具有相同的位置,它们会产生一个无限小的过渡区域,过渡的起止色分别是第一个和最后一个指定值。从效果上看,颜色会在那个位置突然变化,而不是一个平滑的渐变过程。” ——CSS 图像(第三版)(http://w3.org/TR/css3-images) 你在图 3 中可以看到,已经没有任何渐变效果了,只有两块实色,各占据了 background-image 一半的面积。本质上,我们已经创建了两条巨大的水平条纹。 因为渐变是一种由代码生成的图像,我们能像对待其他任何背景图像那样对待它,而且还可以通过 background-size 来调整其尺寸:

这是一个示例 (图4)

 

background: linear-gradient(#fb3 50%, #58a 50%);
background-size: 100% 30px;

在图 24 中可以看到,我们把这两条条纹的高度都缩小到了 15px。由于背景在默认情况下是重复平铺的,整个容器其实已经被填满了水平条纹(参见图 4)。 我们还可以用相同的方法来创建不等宽的条纹,只需调整色标的位置值即可(参见图 5):

这是一个示例 (图5)

 

background: linear-gradient(#fb3 30%, #58a 30%);
background-size: 100% 30px;

为了避免每次改动条纹宽度时都要修改两个数字,我们可以再次从规范那里找到捷径。 “如果某个色标的位置值比整个列表中在它之前的色标的位置值都要小,则该色标的位置值会被设置为它前面所有色标位置值的最大值。” ——CSS 图像(第三版)(http://w3.org/TR/css3-images) 这意味着,如果我们把第二个色标的位置值设置为 0,那它的位置就总是会被浏览器调整为前一个色标的位置值,这个结果正是我们想要的。 因此,下面的代码会产生跟图 5 完全一样的条纹背景,但代码会更加DRY:  

background: linear-gradient(#fb3 30%, #58a 0);
background-size: 100% 30px;

如果要创建超过两种颜色的条纹,也是很容易的。举例来说,下面的代码可以生成三种颜色的水平条纹(参见图 6):

这是一个示例 (图6)

 

background: linear-gradient(#fb3 33.3%, #58a 0, #58a 66.6%, yellowgreen 0);
background-size: 100% 45px;

垂直条纹 水平条纹是最容易用代码写出来的,但我们在网页上看到的条纹图案并不都是水平的。有些条纹是垂直的(参见图 7),而且某些形态的斜条纹或许更受欢迎,或者看起来更加有趣。幸运的是,CSS 渐变同样也能帮助我们创建出这些效果,只是难度稍有不同。 垂直条纹的代码跟水平条纹几乎是一样的,差别主要在于:我们需要在开头加上一个额外的参数来指定渐变的方向。在水平条纹的代码中,我们其实也可以加上这个参数,只不过它的默认值 to bottom 本来就跟我们的意图一致,于是就省略了。最后,我们还需要把 background-size 的值颠倒一下,原因应该不用多说了吧:

这是一个示例 (图7)

 

background: linear-gradient(to right, #fb3 50%, #58a 0);
background-size: 30px 100%;

斜向条纹 在完成了水平和垂直条纹之后,我们可能会顺着往下想:如果我们再次改变 background-size的值和渐变的方向,是不是就可以得到斜向(比如45°)的条纹图案呢?比如这样(结果如图 8 所示):

这是一个示例 (图8)

 

background: linear-gradient(45deg, #fb3 50%, #58a 0);
background-size: 30px 30px;

可以发现,这个办法行不通。原因在于我们只是把每个“贴片”1①内部的渐变旋转了 45°,而不是把整个重复的背景都旋转了。试着回忆一下我们以前用位图来生成斜向条纹时是怎么做的吧,做法类似图 9。单个贴片包含了四条条纹,而不是两条,只有这样才有可能做到无缝拼接。它正是我们需要在 CSS 代码中重新实现的贴片,因此我们需要增加一些色标:

这是一个示例 (图9)

background: linear-gradient(45deg, #fb3 25%, #58a 0, #58a 50%, #fb3 0, #fb3 75%, #58a 0);
background-size: 30px 30px;

我们可以在图 9 中看到结果。如你所见,我们成功地创建了斜向条纹,但这些条纹看起来要比我们在前面制作的水平和垂直条纹细一些。为了理解这其中的道理,我们需要再次回忆起在学校里学过的勾股定理,用它来计算直角三角形的斜边长度。这个定理表示,当 a 和 b 是直角三角形的直角边时,则斜边的长度等于  。对于一个 45°的直角三角形来说,它的两条直角边是等长的,因此这个算式会变成 =a 。在我们的斜向条纹中,背景尺寸指定的长度值决定了直角三角形的斜边长度,但条纹的宽度实际上是直角三角形的高。在图 10 中可以看到图形化的解释。 这意味着,如果想让条纹的宽度变化为我们原本想要的 15px,就需要把 background-size 指定为 2x 15 ≈ 42.426 406 871 像素:

这是一个示例 (图10)

background: linear-gradient(45deg, #fb3 25%, #58a 0, #58a 50%, #fb3 0, #fb3 75%, #58a 0);
background-size: 42.426406871px 42.426406871px;

你可以在图 10 中看到最终效果。但是,除非有人拿枪顶着你的脑袋威胁你必须把斜向条纹的宽度设置为完全精确的 15 像素,我会强烈推荐你把这一长串数字取整,写成 42.4px,或者甚至是 42px。(当然,在上述情形之下,你还是会被干掉。因为  不是整数,我们最终得到的条纹宽度永远都只能是一个近似值——尽管它已经相当精确了。)

更好的斜向条纹

在前面的段落中展示的方法还不够灵活。假设我们想让条纹不是 45°而是 60°怎么办?或者是 30°?又或者是 3.141 592 653 5°?如果我们只是把渐变的角度改一下,那么结果看起来会相当糟糕。(比如在图 10 中,我们尝试实现 60°条纹,但以失败告终。) 幸运的是,我们还有更好的方法来创建斜向条纹。一个鲜为人知的真相是 linear-gradient() 和 radial-gradient() 还各有一个循环式的加强版:repeating-linear-gradient() 和 repeating-radial-gradient()。它们的工作方式跟前两者类似,只有一点不同:色标是无限循环重复的,直到填满整个背景。下面是一个重复渐变的例子(效果参见图 10):

这是一个示例 (图10)

background: repeating-linear-gradient(45deg, #fb3, #58a 30px);

相关阅读

CSS 图像 http://w3.org/TR/css-images CSS 背景与边框 http://w3.org/TR/css-backgrounds CSS 图像(第四版) http://w3.org/TR/css4-images