前端开发 大前端 W3Cbest

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

0%

改变markPoint 标注(气泡)颜色的方法很简单,其实还是多翻文档,markPoint属性有很多下面列了几个也不一一介绍了,自己翻文档。

markPoint:{
symbol: ‘pin’, //标记(气泡)的图形
symbolSize: 50, //标记(气泡)的大小

itemStyle: {…}, //标注(气泡)的样式
}

symbol

标记的图形,默认是个气泡型的,看上图很清晰,

symbolSize

标记的大小,设置标记的大小,可根据自己的嗜好设置大小

itemStyle

标注(气泡)的样式就是这个属性,其实文档讲的都很清晰,只是有的人真的很懒只会问不会找

itemStyle:{
color: ‘#4587E7’,
borderColor: ‘#000’,
borderWidth: 0,
borderType: ‘solid’,

}

color [ default: 自适应 ]

图形的颜色。

borderColor [ default: “#000” ]

图形的描边颜色。支持的颜色格式同 color,不支持回调函数。

borderWidth [ default: 0 ]

描边线宽。为 0 时无描边。

borderType [ default: ‘solid’ ]

柱条的描边类型,默认为实线,支持 ‘solid’, ‘dashed’, ‘dotted’。   在结束之前,提一下如何给每个值都加上标注(气泡),在文档代码看到的气泡是在最大值或最小值上,那么如何给每个值都加上标注(气泡)呢,其实这些值就是你的坐标轴上data值,如果想在每个值的上面显示标注,就要循环你的data值,但是在那循环呢,还是在markPoint对象里面,看文档API里面markPoint还有个data数组对象属性,文档是这样说的;

series[i]-line.markPoint.data[i]

标注的数据数组。每个数组项是一个对象,有下面几种方式指定标注的位置。

  • 通过 x, y 属性指定相对容器的屏幕坐标,单位像素,支持百分比。
  • 用 coord 属性指定数据在相应坐标系上的坐标位置,单个维度支持设置 ‘min’, ‘max’, ‘average’。
  • 直接用 type 属性标注系列中的最大值,最小值。这时候可以使用 valueIndex 指定是在哪个维度上的最大值、最小值、平均值。或者可以使用 valueDim 指定在哪个维度上的最大值、最小值、平均值。

当多个属性同时存在时,优先级按上述的顺序。 那么我们可以把数据组装成多个对象,例如我有一个数组

series: {
data: [820, 932, 901, 934, 129, 670],

markPoint: {…},
}

那么我要在markPoint里面应该是这样组装的

series: {
data: [820, 932, 901, 934, 129, 670],

markPoint: {

data: [
{value: 820, xAxis: 0, yAxis: 820},
{value: 932, xAxis: 1, yAxis: 932},
{value: 901, xAxis: 2, yAxis: 901},
{value: 934, xAxis: 3, yAxis: 934},
{value: 129, xAxis: 4, yAxis: 129},
{value: 670, xAxis: 5, yAxis: 670}
]
}

}

这样就如上图一样效果就出来了 来个?,假设一组数组,大于50显示红色,小于则显示绿色

See the Pen echarts markPoint 气泡 by w3cbest.com (@w3cbest) on CodePen.

本文为JavaScript Prototype(原型) 初学者指南续篇。

我最喜欢的 JavaScript 社区的一部分原因是每个人似乎总是问 “为什么?” 。 为什么我们要按照这种方式做事? 一般来说,这个问题的答案需要充满理性和回顾历史背景。 但有时,答案往往更简单 – “因为我们一直以来都是这么做的。” 在 上一篇文章 中,我们学习了如何在 ES5 和 ES6 中创建 JavaScript 类。 我们还讨论了如何通过构造函数向这些类的实例添加 state(状态) ,以及如何通过类的原型在实例之间共享方法。 这是一个简单的 Player 类,它包含了我们讨论的有关 ES6 类的所有内容。

class Player {
constructor() {
this.points = 0
this.assists = 0
this.rebounds = 0
this.steals = 0
}
addPoints(amount) {
this.points += amount
}
addAssist() {
this.assists++
}
addRebound() {
this.rebounds++
}
addSteal() {
this.steals++
}
}

我们看看这段代码,我们能不能让它更直观一点呢?方法很好理解,都很自然。那么构造函数呢?什么是 constructor ?为什么我们必须在这里定义实例值?现在,这些问题已经有了答案,但是为什么我们不能向实例中添加 state(状态) ,就像方法那样?比如:

class Player {
points = 0
assists = 0
rebounds = 0
steals = 0
addPoints(amount) {
this.points += amount
}
addAssist() {
this.assists++
}
addRebound() {
this.rebounds++
}
addSteal() {
this.steals++
}
}

事实上,这是 Class Fields Declaration 提案的基础,该提案目前处于 TC-39 流程的 第3阶段 。 此提议允许您直接将实例属性添加为类的属性,而无需使用构造方法。 非常漂亮,但是如果我们看一些 React 代码,这个提案真的很棒。 这是一个典型的 React 组件。 它具有本地 state(状态) ,一些方法以及一些静态属性被添加到类中。

class PlayerInput extends Component {
constructor(props) {
super(props)
this.state = {
username: ‘’
}

this.handleChange = this.handleChange.bind(this)

}
handleChange(event) {
this.setState({
username: event.target.value
})
}
render() {

}
}

PlayerInput.propTypes = {
id: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
onSubmit: PropTypes.func.isRequired,
}

PlayerInput.defaultProps = {
label: ‘Username’,
}

让我们看看新的 Class Fields 提议如何改进上面的代码首先,我们可以将 state(状态) 变量从构造函数中取出,并将其直接定义为类的属性(或“字段”)。

class PlayerInput extends Component {
state = {
username: ‘’
}
constructor(props) {
super(props)

this.handleChange = this.handleChange.bind(this)

}
handleChange(event) {
this.setState({
username: event.target.value
})
}
render() {

}
}

PlayerInput.propTypes = {
id: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
onSubmit: PropTypes.func.isRequired,
}

PlayerInput.defaultProps = {
label: ‘Username’,
}

很酷,但没什么好兴奋的。 我们继续吧。 在上一篇文章中,我们讨论了如何使用 static 关键字向类本身添加静态方法。 但是,根据 ES6 类规范,这只对方法有效,对于值则无效。 这就是为什么在上面的代码中,我们必须在我们定义完 PlayerInput 之后,再在 class 外面将 propTypes 和 defaultProps 添加到 PlayerInput ,而不是在 class 体内定义他们的原因。 再说一遍,它们不能像静态方法那样直接放入 class 体内呢? 好消息是,这也包含在 Class Fields 提案中。 所以现在不仅可以在类体中定义静态方法,还可以定义静态值。 这对我们的代码意味着我们可以将 propTypes 和 defaultProps 移动到 class 体内定义。

class PlayerInput extends Component {
static propTypes = {
id: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
onSubmit: PropTypes.func.isRequired,
}
static defaultProps = {
label: ‘Username’
}
state = {
username: ‘’
}
constructor(props) {
super(props)

this.handleChange = this.handleChange.bind(this)

}
handleChange(event) {
this.setState({
username: event.target.value
})
}
render() {

}
}

这样代码看上去好多了,但我们仍然有丑陋的 constructor 方法和 super 调用。 同样,我们现在需要构造函数的原因是为了将 handleChange 方法绑定到恰当的上下文中。 如果我们能找到另一种方法来确保始终在恰当的上下文中调用 handleChange ,那么我们可以摆脱掉 constructor 。 如果您以前使用过箭头函数,就会知道它们没有自己的 this 关键字。相反,this 关键字是按 lexically(词法) 绑定的。这是一种奇特的说法,当你在箭头函数中使用 this 关键字时,事情会按照你所期望的方式运行。利用这些知识并将其与 “Class Fields” 提案相结合起来,如果我们将 handleChange 方法替换为箭头函数呢?这看起来有点奇怪,但是通过这样做,我们可以解决绑定问题,因为,箭头函数是通过 lexically(词法) 绑定 this 的。

class PlayerInput extends Component {
static propTypes = {
id: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
onSubmit: PropTypes.func.isRequired,
}
static defaultProps = {
label: ‘Username’
}
state = {
username: ‘’
}
handleChange = (event) => {
this.setState({
username: event.target.value
})
}
render() {

}
}

你看上面的代码,这比我们开始的原始类要好得多,这都要感谢 “Class Fields” 提案,它将很快成为 EcmaScript 规范的一部分。 从开发者体验的角度来看,Class Fields 提案优势很明显。 然而,他们有一些缺点,很少被谈论。 在上一篇文章中,我们讨论了 ES6 类实际上只是 Pseudoclassical Instantiation(伪类实例化) 模式的语法糖。也就是说,当你向类添加方法时,这就像在函数原型中添加方法一样。

class Animal {
eat() {}
}

// 等价于

function Animal () {}
Animal.prototype.eat = function () {}

这是高效的,因为 eat 定义一次并在类的所有实例之间共享。 这与 Class Fields 有什么关系? 好吧,正如我们上面所看到的, Class Fields 被添加到实例中。 这意味着对于我们创建的每个实例,我们将创建一个新的 eat 方法。

class Animal {
eat() {}
sleep = () => {}
}

// 等价于

function Animal () {
this.sleep = function () {}
}

Animal.prototype.eat = function () {}

请注意 sleep 如何放在实例上,而不是放在 Animal.prototype 上。这是件坏事吗?嗯,有可能。在不进行度量的情况下对性能进行宽泛的描述通常不是一个好主意。您需要在应用程序中回答的问题是,您从 Class Fields 中获得的开发人员体验是否超过了潜在的性能损失。

如果你想在你的应用程序中使用我们之前谈到的任何内容,你需要使用 babel-plugin-transform-class-properties 插件。

Private(私有) 属性

Class Fields 提案的另一个内容时是 “private fields (私有属性)” 。 有时,当您构建一个类时,您希望拥有不暴露给外界的私有值。 从历史上看, JavaScript 缺乏真正私有值 的能力,所以我们通过约定,用下划线标记它们。

class Car {
_milesDriven = 0
drive(distance) {
this._milesDriven += distance
}
getMilesDriven() {
return this._milesDriven
}
}

在上面的示例中,我们依靠 Car class(类)的实例通过调用 getMilesDriven 方法来获取汽车的里程数。但是,因为没有什么能使 _milesDriven成为私有的,所以任何实例都可以访问它。

const tesla = new Car()
tesla.drive(10)
console.log(tesla._milesDriven)

有个奇特的(hacky)方法,就是使用 WeakMaps 可以解决这个问题,但如果存在更简单的解决方案,那将会很好。 同样,Class Fields 提案正在拯救我们。 根据提议,您可以使用 # 创建私有字段。 是的,你没有看错, # 。 我们来看看它对我们的代码有什么影响,

class Car {
#milesDriven = 0
drive(distance) {
this.#milesDriven += distance
}
getMilesDriven() {
return this.#milesDriven
}
}

我们可以用速记语法更进一步简化

class Car {
#milesDriven = 0
drive(distance) {
#milesDriven += distance
}
getMilesDriven() {
return #milesDriven
}
}

const tesla = new Car()
tesla.drive(10)
tesla.getMilesDriven() // 10
tesla.#milesDriven // Invalid

如果您对私有属性背后的更多细节/决策感兴趣,那么这里有一篇 很好的文章

目前 有一个 PR 将私有属性添加到 Babel ,以便您可以在应用中使用它们。

文章来源:https://tylermcginnis.com

属性选择器非常神奇。它们可以使你摆脱棘手的问题,帮助你避免添加类,并指出代码中的一些问题。但是不要担心,虽然属性选择器非常复杂和强大,但是它们很容易学习和使用。在本文中,我们将讨论它们是如何运行的,并给出一些如何使用它们的想法。

通常将 HTML 属性放在方括号中,称为属性选择器,如下:

[href] {
color: red;
}

这样任何具有href属性的且没有更特定选择器的元素的文本颜色都会是红色的。属性选择器的特性与类相同。 注:更多关于笼匹配的CSS特异性,你可以阅读CSS特性:你应该知道的事情,或者如果你喜欢星球大战:CSS特性战争 但是你可以使用属性选择器做得更多。就像你的 DNA 一样,它们有内在的逻辑来帮助你选择各种属性组合和值。它们可以匹配属性中的任何属性,甚至字符串值,而不是像标签、类或id选择器那样精确匹配。

属性选择器

属性选择器可以独立存在,更具体地说,如果需要选择所有具有title属性的div标签,可以这么做:

div[title]

但你也可以通过以下操作选择具有 title 属性的 div 的子元素

div [title]

需要说明的是,它们之间没有空格意味着属性位于相同的元素上(就像元素和类之间没有空格一样),而它们之间的空格意味着后代选择器,即选择具有该属性的元素的子元素。 你可以更精细地选择具体属性值的属性。

div[title=”dna”]

上面选择了所有具有确切名称dna的div,虽然有选择器算法可以处理每种情况(以及更多),但这里不会选择**“dna is awesome”或“dnamutation”**的标题。 **注意:**在大多数情况下,属性选择器中不需要引号,但是我使用它们,因为我相信它可以提高清代码的可读性,并确保边界用例能够正常工作。 如果你想选择 title 包含 dna的元素,如 “my beautiful dna” 或者 “mutating dna is fun!” ,可以使用波浪号(~)。

div[title~=”dna”]

如果你想匹配以 dna 结尾的 title,如  “dontblamemeblamemydna” 或 “his-stupidity-is-from-upbringing-not-dna” ,刚可以使用$标志符:

[title$=”dna”]

如果你想匹配以 dna 开头的 title,如  “dnamutants” 或 “dna-splicing-for-all” ,刚可以使用^标志符:

[title^=”dna”]

虽然精确匹配是有帮助的,但它可能选择太紧,并且^符号匹配可能太宽而无法满足你的需要。 例如,可能不想选择 “genealogy” 的标题,但仍然选择“gene”和“gene-data”。 管道特征()就是这样,属性中必须是完整且唯一的单词,或者以-分隔开。

[title=”gene”]

最后,还有一个匹配任何子字符串的模糊搜索属性操作符,属性中做字符串拆分,只要能拆出来dna这个词就行:

[title*=”dna”]

使这些属性选择器更加强大的是,它们是可堆叠的,允许你选择具有多个匹配因子的元素。 如果你需要找到一个a 标签,它有一个 title ,并且有一个以“genes” 结尾的 class,可以使用如下方式:

a[title][class$=”genes”]

你不仅可以选择 HTML 元素的属性,还可以使用伪类型元素来打印出文本:

What’s the first thing a biotech journalist does after finishing the first draft of an article?

.joke:hover:after {
content: “Answer:”attr(title);
display: block;
}

上面的代码在鼠标悬停时将显示一串自定义的字符串。 最后要知道的是,您可以添加一个标志,让属性搜索不区分大小写。 在结束方括号之前添加i:

[title*=”DNA” i]

因此它会匹配dna, DNA, dnA等。 现在我们已经看到了如何使用属性选择器进行选择,让我们看看一些用例。 我将它们分为两类:一般用途和诊断。

一般用途

输入类型样式的设置

你可以对输入类型使用不同的样式,例如电子邮件和电话。

input[type=”email”] {
color: papayawhip;
}
input[type=”tel”] {
color: thistle;
}

显示电话链接

你可以隐藏特定尺寸的电话号码并显示电话链接,以便在手机上轻松拨打电话。

span.phone {
display: none;
}
a[href^=”tel”] {
display: block;
}

内部链接 vs 外部链接,安全链接 vs 非安全链接

你可以区别对待内部和外部链接,并将安全链接设置为与不安全链接不同:

a[href^=”http”] {
color: bisque;
}
a:not([href^=”http”]) {
color: darksalmon;
}

a[href^=”http://“]:after {
content: url(unlock-icon.svg);
}
a[href^=”https://“]:after {
content: url(lock-icon.svg);
}

下载图标

HTML5 给我们的一个属性是“下载”,它告诉浏览器,你猜对了,下载该文件而不是试图打开它。这对于你希望人们访问但不希望它们立即打开的 PDF 和 DOC 非常有用。它还使得连续下载大量文件的工作流程更加容易。下载属性的缺点是没有默认的视觉效果将其与更传统的链接区分开来。通常这是你想要的,但如果不是,你可以做类似下面的事情:

a[download]:after {
content: url(download-arrow.svg);
}

还可以使用不同的图标(如PDF与DOCX与ODF等)来表示文件类型,等等。

a[href$=”pdf”]:after {
content: url(pdf-icon.svg);
}
a[href$=”docx”]:after {
content: url(docx-icon.svg);
}
a[href$=”odf”]:after {
content: url(open-office-icon.svg);
}

你还可以通过叠加属性选择器来确保这些图标只出现在可下载链接上。

a[download][href$=”pdf”]:after {
content: url(pdf-icon.svg);
}

覆盖或重新使用已废弃/弃用的代码

我们都遇到过时代码过时的旧网站,在 HTML5 之前,你可能需要覆盖甚至重新应用作为属性实现的样式。

Old,holey genes

div[bgcolor=”#000000”] {
/*override*/
background-color: #222222 !important;
}
div[color=”#FFFFFF”] {
/*reapply*/
color: #FFFFFF;
}

重写特定的内联样式

有时候你会遇到一些内联样式,这些样式会影响布局,但这些内联样式我们又没修改。那么以下是一种方法。 如果你道要覆盖的确切属性和值,并且希望在它出现的任何地方覆盖它,那么这种方法的效果最好。 对于此示例,元素的边距以像素为单位设置,但需要在 em 中进行扩展和设置,以便在用户更改默认字体大小时可以正确地重新调整元素。

div[style*=”margin: 8px”] {
margin: 1em !important;
}

显示文件类型

默认情况下,文件输入的可接受文件列表是不可见的。 通常,我们使用伪元素来暴露它们:

<input type=”file”accept=”pdf,doc,docx”>

[accept]:after {
content: “Acceptable file types: “attr(accept);
}

html 手风琴菜单

details和summary标签是一种只用HTML做扩展/手风琴菜单的方法,details 包括了summary标签和手风琴打开时要展示的内容。点击summary会展开details标签并添加open属性,我们可以通过open属性轻松地为打开的details标签设置样式:

details[open] {
background-color: hotpink;
}

打印链接

在打印样式中显示URL使我走上了理解属性选择器的道路。 你现在应该知道如何自己构建它, 你只需选择带有href的所有标签,添加伪元素,然后使用attr()和content打印它们。

a[href]:after {
content: “ (“attr(href) “) “;
}

自定义提示

使用属性选择器创建自定义工具提示既有趣又简单:

[title] {
position: relative;
display: block;
}
[title]:hover:after {
content: attr(title);
color: hotpink;
background-color: slateblue;
display: block;
padding: .225em .35em;
position: absolute;
right: -5px;
bottom: -5px;
}

便捷键

web 的一大优点是它提供了许多不同的信息访问选项。一个很少使用的属性是设置accesskey的能力,这样就可以通过键组合和accesskey设置的字母直接访问该项目(确切的键组合取决于浏览器)。但是要想知道网站上设置了哪些键并不是件容易的事下面的代码将显示这些键:focus。我不使用鼠标悬停,因为大多数时候需要accesskey的人是那些使用鼠标有困难的人。你可以将其添加为第二个选项,但要确保它不是惟一的选项。

a[accesskey]:focus:after {
content: “ AccessKey: “attr(accesskey);
}

诊断

这些选项用于帮助我们在构建过程中或在尝试修复问题时在本地识别问题。将这些内容放在我们的生产网站上会使用户产生错误。

没有 controls 属性的 audio

我不经常使用audio标签,但是当我使用它时,我经常忘记包含controls属性。 结果:没有显示任何内容。 如果你在 Firefox,如果隐藏了音频元素,或者语法或其他一些问题阻止它出现(仅适用于Firefox),此代码可以帮助你解决问题:

audio:not([controls]) {
width: 100px;
height: 20px;
background-color: chartreuse;
display: block;
}

没有 alt 文本

没有 alt 文本的图像是可访问性的噩梦。只需查看页面就很难找到它们,但如果添加它们,它们就会弹出来(当页面图片加载失败时,alt文字可以更好的解释图片的作用):

img:not([alt]) {
/* no alt attribute */
outline: 2em solid chartreuse;
}
img[alt=””] {
/* alt attribute is blank */
outline: 2em solid cadetblue;
}

异步 Javascript 文件

网页可以是内容管理系统和插件,框架和代码的集合,确定哪些JavaScript异步加载以及哪些不加载可以帮助你专注于提高页面性能。

script[src]:not([async]) {
display: block;
width: 100%;
height: 1em;
background-color: red;
}
script:after {
content: attr(src);
}

JavaScript 事件元素

你可以突出显示具有JavaScript事件属性的元素,以便将它们重构到JavaScript文件中。这里我主要关注OnMouseOver属性,但是它适用于任何JavaScript事件属性。

[OnMouseOver] {
color: burlywood;
}
[OnMouseOver]:after {
content: “JS: “attr(OnMouseOver);
}

隐藏项

如果需要查看隐藏元素或隐藏输入的位置,可以使用它们来显示

[hidden], [type=”hidden”] {
display: block;
}

  文章来源:https://www.smashingmagazine.com/2018/10/attribute-selectors-splicing-html-dna-css

在项目开发中用到echarts有这样的需求,点击点击柱子改变颜色并且回调柱子的信息,看官网方法很简单,就是官网的例子

myChart.on(‘click’, function (params) {
//params 就是点击柱子的返回信息
});

那么如何改变柱子的颜色呢?请在 如何给ECharts柱状图设置一个高亮色 文中阅读,讲的是如何设置高亮色,其实实现方法是一样的,只不过这里只是添加了点击事件而已,实现方法如下:

var curInt = null;
option = {

series:[{

data:[…],
itemStyle:{
color: function(params){
var key = params.dataIndex;
if(key === curInt){
return ‘#E062AE’
}else{
return ‘#37A2DA’
}
}
},

}],

};

myChart.on(‘click’, function (params) {
curInt = params.dataIndex;
myChart.setOption(option)
});

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

我用的是4.0.0版本,测试了一下4.2版本以上有bug,无法重新绘制颜色

第一步

先安装babel-cli

npm install --save-dev babel-cli

第二步

在sublime text 中安装babel插件 插件如何安装这里就不多解释,不过记住一定是babel

第三步

安装好后来配置一下安装的babel插件


这里的地址要配置好,也就是你安装的babel-cli的目录地址,让插件可以在此寻找 babel或babel-core 命令,否则就会报错 Couldn’t find babel or babel-core

第四步

在当前目录配置好.babelrc文件,定义转码规则,在当前目录放置好依赖包(因为这里找不到全局的)

npm install --save babel-preset-es2015

比如我只用了babel编译es5,就在.babelrc文件里添加

{
  "presets": [
    "es2015"
  ]
}

然后在js文件里编写es6的代码,按下快捷键Ctrl+Shift+P,输入babel,选择 babel transform 执行即可,如果顺利的话,就能看到es6文件已经转换为es5语法,然而有一个很大的缺陷就是只能生成匿名文件,具体如何解决还没找到方法。 其他一些编辑器的安装方法

上篇文章写的是利用CSS3 选择器:target制作一个可伸缩的导航菜单,可以在兼容这个选择器的基础可以使用,但是如果在低版本浏览器就可能使用不了了,这篇文章是通过JavaScript实现可伸缩性导航,我们看下面代码:

CSS代码:

.tabs {
position: relative;
}
.tabs:not(.jsfied) {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.tabs .hidden {
display: none;
}
.tabs a,
.tabs button {
width: 100%;
height: 100%;
display: block;
font-size: 1em;
line-height: 1.2;
text-align: center;
color: #FAF3DD;
background-color: transparent;
}
.tabs .primary {
display: flex;
}
.tabs .primary > li {
flex-grow: 1;
background-color: #5a5a5a;
}
.tabs .primary > li + li {
border-left: 1px solid #414141;
}
.tabs .primary > li > a,
.tabs .primary > li > button {
white-space: nowrap;
padding: 1em 0.6em;
box-shadow: inset 0 -0.2em 0 #414141;
}
.tabs .primary > li > a:hover,
.tabs .primary > li > button:hover {
background-color: #414141;
}
.tabs .primary > li > a:active,
.tabs .primary > li > button:active {
background-color: #4a4a4a;
}
.tabs .primary .more {
background-color: #414141;
}
.tabs .primary .more > button span {
display: inline-block;
transition: -webkit-transform 0.2s;
transition: transform 0.2s;
transition: transform 0.2s, -webkit-transform 0.2s;
}
.tabs.show-secondary .primary .more > button span {
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
}
.tabs .secondary {
max-width: 100%;
min-width: 10em;
display: none;
position: absolute;
top: 100%;
right: 0;
box-shadow: 0 0.3em 0.5em rgba(0, 0, 0, 0.3);
-webkit-animation: nav-secondary 0.2s;
animation: nav-secondary 0.2s;
}
.tabs .secondary li {
border-top: 1px solid #353535;
background-color: #414141;
}
.tabs .secondary a,
.tabs .secondary button {
padding: 0.6em;
}
.tabs .secondary a:hover,
.tabs .secondary button:hover {
background-color: #4a4a4a;
}
.tabs .secondary a:active,
.tabs .secondary button:active {
background-color: #353535;
}
.tabs.show-secondary .secondary {
display: block;
}

@-webkit-keyframes nav-secondary {
0% {
opacity: 0;
-webkit-transform: translateY(-1em);
transform: translateY(-1em);
}
100% {
opacity: 1;
-webkit-transform: translateY(0);
transform: translateY(0);
}
}

@keyframes nav-secondary {
0% {
opacity: 0;
-webkit-transform: translateY(-1em);
transform: translateY(-1em);
}
100% {
opacity: 1;
-webkit-transform: translateY(0);
transform: translateY(0);
}
}

JS:

const container = document.querySelector(‘.tabs’)
const primary = container.querySelector(‘.primary’)
const primaryItems = container.querySelectorAll(‘.primary > li:not(.more)’)
container.classList.add(‘jsfied’)

primary.insertAdjacentHTML(‘beforeend’, `

    • ${primary.innerHTML}
  • \`) const secondary = container.querySelector('.secondary') const secondaryItems = secondary.querySelectorAll('li') const allItems = container.querySelectorAll('li') const moreLi = primary.querySelector('.more') const moreBtn = moreLi.querySelector('button') moreBtn.addEventListener('click', (e) => { e.preventDefault() container.classList.toggle('show-secondary') moreBtn.setAttribute('aria-expanded', container.classList.contains('show-secondary')) })

    // 调整选项卡

    const doAdapt = () => {
    // 显示所有项目
    allItems.forEach((item) => {
    item.classList.remove(‘hidden’)
    })

    // 隐藏已经超过导航的其他项目
    let stopWidth = moreBtn.offsetWidth
    let hiddenItems = []
    const primaryWidth = primary.offsetWidth
    primaryItems.forEach((item, i) => {
    if(primaryWidth >= stopWidth + item.offsetWidth) {
    stopWidth += item.offsetWidth
    } else {
    item.classList.add(‘hidden’)
    hiddenItems.push(i)
    }
    })

    // 切换更多按钮
    if(!hiddenItems.length) {
    moreLi.classList.add(‘hidden’)
    container.classList.remove(‘show-secondary’)
    moreBtn.setAttribute(‘aria-expanded’, false)
    }
    else {
    secondaryItems.forEach((item, i) => {
    if(!hiddenItems.includes(i)) {
    item.classList.add(‘hidden’)
    }
    })
    }
    }

    doAdapt()
    window.addEventListener(‘resize’, doAdapt)

    document.addEventListener(‘click’, (e) => {
    let el = e.target
    while(el) {
    if(el === secondary el === moreBtn) {
    return;
    }
    el = el.parentNode
    }
    container.classList.remove(‘show-secondary’)
    moreBtn.setAttribute(‘aria-expanded’, false)
    })

    当我们在写页面时经常会遇到页面内容少的时候,footer会戳在页面中间或什么?反正就是不在最底部显示,反正就是很难看,下面要讲的布局就是解决如何使元素粘住浏览器底部,

    方法一:footer高度固定+绝对定位

    html

    <div class="dui-container">
      <header>Header</header>
      <main class="content">
        <h1>使用position定位footer布局</h1>
      </main>
      <footer class="footer">
        Footer
      </footer>
    </div>

    CSS

    .dui-container{
      position: relative;
      min-height: 100%;
    }
      header,main, footer{
      padding: 0 calc(50% - 450px);
    }
    main {
      padding-bottom: 100px;
    }
    header, footer{
      font-size: 30px;
      background-color: #333;
      color: white;
      line-height: 100px;
      height: 100px;
    }
    footer{
      width: 100%;
      position: absolute;
      bottom: 0
    }

    方法二:在主体content上的下边距增加一个负值等于底部高度

    html

    <body>
    <header>Header</header>
    <main>Content</main>
    <footer>Footer</footer>
    </body>

    CSS

    html, body {
      height: 100%;
    }
    main {
      min-height: 100%;
      padding-top: 100px;
      padding-bottom: 100px;
      margin-top: -100px;
      margin-bottom: -100px;
    }
    header, footer{
      line-height: 100px;
      height: 100px;
    }

    方法三:将页脚的margin-top设为负数

    html

    <header>Header</header>
    <main class="content">
    <button id="add">添加内容试试</button>
    </main>
    <footer class="footer">
    Footer
    </footer>

    CSS

    main {
      min-height: 100%;
      padding-top: 100px;
      padding-bottom: 100px;
    }
    header, footer{
      font-size: 30px;
      background-color: #333;
      color: white;
      line-height: 100px;
      height: 100px;
    }
    header{
      margin-bottom: -100px;
    }
    footer{
      margin-top: -100px;
    }

     

    方法四: 通过设置flex,将footer的margin-top设置为auto

    html

    <header>Header</header>
    <main>
      <h1>在主体content上的下边距增加一个负值等于底部高度</h1>
    </main>
    <footer>Footer</footer>

    CSS

    body{
      display: flex;
      min-height: 100vh;
      flex-direction: column;
    }
    header,main, footer{
      padding: 0 calc(50% - 450px);
    }
    header,footer{
      font-size: 30px;
      background-color: #333;
      color: white;
      line-height: 100px;
      height: 100px;
    }
    footer{
      margin-top: auto
    }

     

     

    方法五: 通过函数calc()计算内容的高度

    html代码

    <body>
    <header>Header</header>
    <main>Content</main>
    <footer>Footer</footer>
    </body>

    CSS代码

    main{
      min-height: calc(100vh - 200px); /* 这个200px是header和footer的高度 */
    }
    header,footer{
      height: 100px;
      line-height: 100px;
    }

     

     

    方法六: 通过设置flexbox,将主体main设置为flex

    关于flexbox还请查看具体怎么使用

    <body>
    <header>Header</header>
    <main>Content</main>
    <footer>Footer</footer>
    </body>

    CSS代码

    body{
      display: flex;
      min-height: 100vh;
      flex-direction: column;
    }
    main{
      flex: 1
    }

     

     

    方法七: 使用grid布局

    Html代码

    <body>
      <header>Header</header>
      <main>Content</main>
      <footer>Footer</footer>
    </body>

    CSS代码

    html {
      height: 100%;
    }
    body {
      min-height: 100%;
      display: grid;
      grid-template-rows: auto 1fr auto;
    }
    .footer {
      grid-row-start: 3;
      grid-row-end: 4;
    }

     

    方法八: display-*

    html

    <header>Header</header>
    <button id="add">添加内容试试</button>
    <footer class="footer">Footer</footer>

    CSS

    body {
      min-height: 100%;
      display: table;
      width: 100%;
    }
    main {
      display: table-row;
      height: 100%;
    }

    参考:Sticky Footer, Five Ways

    在过去的几年间,有一种网页设计手法逐渐流行起来,我将它称作背景宽度满屏,内容宽度固定。这个设计的一些典型特征如下。 页面中包含多个大区块,每个区块都占据了整个视口的宽度,区块的背景也各不相同。 内容是定宽的,即使在不同分辨率下的宽度不一样,那也只是因为媒体查询改变了这个固定的宽度值而已。在某些情况下,不同区块的内容也可能具有不同的宽度。 有时候,整个网页都是由这种风格的多个区块组成的满屏背景的定宽内容。不过在更多的情况下,页面中只有某个特定区域是以这个风格来设计的,最典型的就是导航或页脚。 要实现这种设计风格,最常见的方法就是为每个区块准备两层元素:外层用来实现满屏的背景,内层用来实现定宽的内容。后者是通过 margin: auto实现水平居中的。举例来说,采用这种设计的页脚通常需要把结构代码写成:

    同时用 CSS 来设置这两层元素的样式:

    footer {
    background: #333;
    }
    .wrapper {
    max-width: 900px;
    margin: 1em auto;
    }

    看起来很眼熟对不对?目前绝大多数的前端工程师都是这样写的。难道为了这个效果就一定要添加一层额外的元素?我们能否在现代 CSS的帮助下彻底抛弃这个累赘? 我们先来想一想,margin: auto 在这个场景下到底发挥了什么作用。这条声明所产生的左右外边距实际上都等于视口宽度的一半减去内容宽度的一半。由于百分比在这里是基于视口宽度来解析的(假设所有祖先元素都没有显式指定宽度),我们可以把这个外边距的值表达为 50% - 450px。幸好CSS3定义了这样一个 calc() 函数,它允许我们在 CSS 中直接以这种简单的算式来指定属性的值。如果用 calc() 取代原先的 auto,这个内层容器的样式就会变成:

    .wrapper {
    max-width: 900px;
    margin: 1em calc(50% - 450px);
    }

    之所以要在页脚内加一层容器元素,唯一的原因就是为了给它的margin 指定神奇的 auto 关键字,从而实现内容的水平居中布局。不过,现在我们已经用 calc() 替代了这个神奇的 auto,而且这个新值实际上可以作为一个通用的 CSS 长度值应用到任何一个接受长度值的属性上。这意味着如果我们愿意,还可以把这个长度值应用到父元素的 padding 上,而整个效果是保持不变的:

    footer {
    max-width: 900px;
    padding: 1em calc(50% - 450px);
    background: #333;
    }
    .wrapper {}

    经过这一番改造之后,我们已经把内层容器上的所有 CSS代码都剥离干净了。也就是说,它其实已经不需要参与布局了,我们可以安全地把它从结构代码中去掉。终于,我们在纯净无冗余的 HTML 结构上实现了想要的设计风格。这个方案还有进一步优化的空间吗?没错。你要相信,追求卓越的道路是永无止境的! 如果把 width 这一行声明注释掉,你会发现其实没有影响。视觉效果是完全一样的,而且不论视口尺寸如何变化都是如此。这是为什么呢?因为当内边距是 50% - 450px 时,只可能给内容留出 900px(2×450px)的可用空间。只有把 width 显式地设置为 900px 之外(或大或小)的其他值,我们才有可能看出区别。由于我们想要得到的内容宽度本来就是 900px,这一行声明其实就是冗余的,我们可以把它去掉,让代码更加简洁。 另一个可以优化的地方在于,我们可以增加一条回退样式来增强向后兼容性。这样即使浏览器不支持 calc(),我们也至少可以得到一个相对合理的内边距:

    footer {
    padding: 1em;
    padding: 1em calc(50% - 450px);
    background: #333;
    }

    终于大功告成了。我们抛弃了冗余的标签,花费了三行 CSS 代码,最终达成了这个完美的结果:样式灵活、代码简练,还具有良好的兼容性! 代码演示一下