CSS选择器
CSS 元素选择器
最常见的 CSS 选择器是元素选择器。换句话说,文档的元素就是最基本的选择器。
如果设置 HTML 的样式,选择器通常将是某个 HTML 元素,比如 p、h1、em、a,甚至可以是 html 本身
CSS 类选择器
.class选择指定class属性为class的任意类型的任意多个元素
CSS ID 选择器
只能在文档中使用一次
与类不同,在一个 HTML 文档中,ID 选择器会使用一次,而且仅一次。
CSS 属性选择器
CSS 后代选择器
在后代选择器中,规则左边的选择器一端包括两个或多个用空格分隔的选择器。选择器之间的空格是一种结合符(combinator)。每个空格结合符可以解释为“… 在 … 找到”、“… 作为 … 的一部分”、“… 作为 … 的后代”,但是要求必须从右向左读选择器。有关后代选择器有一个易被忽视的方面,即两个元素之间的层次间隔可以是无限的。
CSS 子元素选择器
如果您不希望选择任意的后代元素,而是希望缩小范围,只选择某个元素的子元素,请使用子元素选择器(Child selector)。
例如,如果您希望选择只作为 h1 元素子元素的 strong 元素,可以这样写:
h1 > strong {color:red;}
这个规则会把第一个 h1 下面的两个 strong 元素变为红色,但是第二个 h1 中的 strong 不受影响:
<h1>This is <strong>very</strong> <strong>very</strong> important.</h1>
<h1>This is <em>really <strong>very</strong></em> important.</h1>
CSS 相邻兄弟选择器
相邻兄弟选择器
相邻兄弟选择器(Adjacent sibling selector)可选择紧接在另一元素后的元素,且二者有相同父元素。
结合其他选择器
相邻兄弟结合符还可以结合其他结合符:
html > body table + ul {margin-top:20px;}
这个选择器解释为:选择紧接在 table 元素后出现的所有兄弟 ul 元素,该 table 元素包含在一个 body 元素中,body 元素本身是 html 元素的子元素。
CSS 伪类
伪类的语法:
selector : pseudo-class {property: value}
CSS 类也可与伪类搭配使用。
selector.class : pseudo-class {property: value}
:first-child 伪类
您可以使用 :first-child 伪类来选择元素的第一个子元素。
p:first-child {font-weight: bold;}
li:first-child {text-transform:uppercase;}
:lang 伪类
:lang 伪类使你有能力为不同的语言定义特殊的规则。在下面的例子中,:lang 类为属性值为 no 的 q 元素定义引号的类型:
属性 | 描述 |
---|---|
:active | 向被激活的元素添加样式。 |
:focus | 向拥有键盘输入焦点的元素添加样式。 |
:hover | 当鼠标悬浮在元素上方时,向元素添加样式。 |
:link | 向未被访问的链接添加样式。 |
:visited | 向已被访问的链接添加样式。 |
:first-child | 向元素的第一个子元素添加样式。 |
:lang | 向带有指定 lang 属性的元素添加样式。 |
CSS 伪元素
:first-line 伪元素
“first-line” 伪元素用于向文本的首行设置特殊样式。
在下面的例子中,浏览器会根据 “first-line” 伪元素中的样式对 p 元素的第一行文本进行格式化:
实例
p:first-line
{
color:#ff0000;
font-variant:small-caps;
}
:first-letter 伪元素
“first-letter” 伪元素用于向文本的首字母设置特殊样式:
伪元素可以与 CSS 类配合使用:
p.article:first-letter
{
color: #FF0000;
}
<p class="article">This is a paragraph in an article。</p>
上面的例子会使所有 class 为 article 的段落的首字母变为红色。
:before 伪元素
“:before” 伪元素可以在元素的内容前面插入新内容。
下面的例子在每个 <h1>
元素前面插入一幅图片:
h1:before
{
content:url(logo.gif);
}
:after 伪元素
“:after” 伪元素可以在元素的内容之后插入新内容。
下面的例子在每个 <h1>
元素后面插入一幅图片:
h1:after
{
content:url(logo.gif);
}
浮动的理解
浮动的框可以向左或向右移动,直到它的外边缘碰到包含框或另一个浮动框的边框为止。
由于浮动框不在文档的普通流中,所以文档的普通流中的块框表现得就像浮动框不存在一样。
再请看下图,当框 1 向左浮动时,它脱离文档流并且向左移动,直到它的左边缘碰到包含框的左边缘。因为它不再处于文档流中,所以它不占据空间,实际上覆盖住了框 2,使框 2 从视图中消失。
如果把所有三个框都向左移动,那么框 1 向左浮动直到碰到包含框,另外两个框向左浮动直到碰到前一个浮动框。
如下图所示,如果包含框太窄,无法容纳水平排列的三个浮动元素,那么其它浮动块向下移动,直到有足够的空间。如果浮动元素的高度不同,那么当它们向下移动时可能被其它浮动元素“卡住”:
浮动影响
- 背景不能显示
由于浮动产生,如果对父级设置了(CSS background背景)CSS背景颜色或CSS背景图片,而父级不能被撑开,所以导致CSS背景不能显示。 - 边框不能撑开
如上图中,如果父级设置了CSS边框属性(css border),由于子级里使用了float属性,产生浮动,父级不能被撑开,导致边框不能随内容而被撑开。 - margin padding设置值不能正确显示
由于浮动导致父级子级之间设置了css padding、css margin属性的值不能正确表达。特别是上下边的padding和margin不能正确显示。
解决方法:
1、clear: both清除产生浮动
2、父级div定义 overflow:hidden
3、.clearFix清除浮动
.clearFix:after {
content: "."; /*内容为“.”就是一个英文的句号而已。也可以不写。*/
display: block; /*加入的这个元素转换为块级元素。*/
clear: both; /*清除左右两边浮动。*/
visibility: hidden; /*可见度设为隐藏。注意它和display:none;是有区别的。visibility:hidden;仍然占据空间,只是看不到而已;*/
line-height: 0; /*行高为0;*/
height: 0; /*高度为0;*/
font-size: 0; /*字体大小为0;*/
}
.clearFix {
*zoom: 1;
}
4、父级div定义 overflow:auto
5、父级也一起浮动
6、父级div定义 display:table
8.结尾处加 br标签 clear:both
弹性盒模型
要使用弹性盒模型需要设置display: flex;
设置了flex-direction的一个方向为主轴则另一个方向就为侧轴,这个需要分清
1、flex-direction
(1)flex-direction: row;默认从左到右
(2)flex-direction: row-reverse;
(3) flex-direction: column;垂直排列
(4)flex-direction: column-reverse;反向垂直排列
2、 justify-content主轴
(1) justify-content: flex-start;
(2)justify-content: flex-end;
(3)justify-content: center;
3、 align-items侧轴
(1)align-items: center;
(2)align-items: flex-end;
(3)align-items: flex-start;
4、对于子元素
排序
.box .item:nth-of-type(1){
order: 3;
}
.box .item:nth-of-type(2){
order: 3;
}
.box .item:nth-of-type(3){
order: 0;
}
.box .item:nth-of-type(4){
order: 2;
}
.box .item:nth-of-type(5){
order: -1;
}
伸缩性
.box .item:nth-of-type(1){
flex: 1;
}
.box .item:nth-of-type(2){
flex: 2;
}
.box .item:nth-of-type(3){
flex: 3;
}
.box .item:nth-of-type(4){
flex: 4;
}
.box .item:nth-of-type(5){
flex: 5;
}
css与预处理器:(less、sass、stylus)用一种专门的编程语言,为CSS增加了一些编程的特性,将CSS作为目标生成文件,然后开发者就只要使用这种语言进行编码工作,让你的CSS更加简洁、适应性更强、可读性更佳,更易于代码的维护等诸多好处
sass常用的功能:
1:变量赋值`
2:嵌套:(1.选择器嵌套 2.属性嵌套)
3:混合:@mixin center-block {
margin-left:auto;
margin-right:auto;
}
.demo{
@include center-block;
}
4:继承:@extend
h1{
border: 4px solid #ff9aa9;
}
.speaker{
@extend h1;
border-width: 2px;
}
精灵图
1、精灵图(雪碧图)技术产生的目的:很多大型网页在首次加载的时候都需要加载很多的小图片,而考虑到在同一时间,服务器拥堵的情况下,为了解决这一问题,采用了精灵图这一技术来缓解加载时间过长从而影响用户体验的这个问题。
2、精灵图技术的本质:所谓精灵图就是把很多的小图片合并到一张较大的图片里,所以在首次加载页面的时候,就不用加载过多的小图片,只需要加载出来将小图片合并起来的那一张大图片也就是精灵图即可,这样在一定程度上减少了页面的加载速度,也一定程度上缓解了服务器的压力。
css选择器的匹配过程
4个等级的定义如下:
第一等:代表内联样式,如: style=””,权值为1000。
第二等:代表ID选择器,如:#content,权值为100。
第三等:代表类,伪类和属性选择器,如.content,权值为10。
第四等:代表类型选择器和伪元素选择器,如div p,权值为1。
盒模型
当然, 还有用来改变盒模型width范围的一个css3的属性, box-sizing:
当设置为’border-box’时, width = border + padding + content;
当设置为’content-box’时, width = content。
说完盒模型的padding和border, 那么再来吐槽下margin, 盒模型的margin的折叠(margin collapsing)问题, 有些也叫外边距合并。
通常我们说的折叠, 都是垂直方向上的折叠, 水平方向是不存在的。标准模式下, 上下两个兄弟的块级元素, margin是会重叠的, 并且以最大的那个间距为准(都为正数)。
闭包及用途
闭包本质上说就是一个函数,有权限访问其他函数内部的函数。
来看一段代码:
function f1(){
var n = 0;
function f2(){
n++;
console.log(n);
}
return f2;
}
var temp = f1();
temp();//控制台输出1
temp();//控制台输出2
在以上代码中,f2被定义在f1函数的内部,所以f1定义的局部变量n,f2是可以访问到的,但是如果在f2中定义的变量,f1是访问不到的。
众所周知js的函数就是一个个的小黑屋,也就是可以从外面获取信息,但是外界却不能直接获得里面的内容。
将变量n放在f1中,没有其他的方法可以接触到变量n,只能通过renturn返回函数标志f2(注意是f2,不是f2())。
函数名只是一个标识(指向函数的指针),而()才是执行函数。所以后面执行的实际上是var temp = f2;f2();f2();
在外部是不能直接调用f2()的,只能通过return f2与外部联系,这就是Javascript语言特有的”链式作用域”结构。
- 读取其他函数内部的变量
- 让变量的值始终保存在内存中
JavaScript中闭包的应用都有关键词return,引用JavaScript 秘密花园中的一段话就是因为:
闭包是 JavaScript 一个非常重要的特性,这意味着当前作用域总是能够访问外部作用域中的变量。 因为 函数 是 JavaScript
中唯一拥有自身作用域的结构,因此闭包的创建依赖于函数。
闭包是一个引用其他函数的变量的函数,引用的变量不会被回收,因此可以用来封装一个私有变量,但是不必要的闭包会让内存消耗很大。
this
function foo() {
console.log(this.a}
var a = 1
foo()
const obj = {
a: 2,foo: foo
}
obj.foo()
const c = new foo
- 对于直接调⽤ foo 来说,不管 foo 函数被放在了什么地
⽅,this ⼀定是 window - 对于 obj.foo() 来说,我们只需要记住,谁调⽤了函数,谁
就是 this,所以在这个场景下 foo 函数中的 this 就是 obj
对象 - 对于 new 的⽅式来说,this 被永远绑定在了 c 上⾯,不会被
任何⽅式改变 this
箭头函数它会直接绑定到它父级的执行上下文里的this
var name='window';
var obj = {
name:'obj',
nameprintf:()=>{
console.log(this.name)
}
}
obj.nameprintf();//window,因为此时父级是obj,obj的上下文是window。
不管我们给函数 bind ⼏次,fn 中的this 永远由第⼀次 bind 决定,所以结果永远是 window
原型和原型链
- 对于这个问题,可以从下⾯这⼏个要点来理解和回答,下⾯⼏条必须记住并且理解所有的引⽤类型(数组、对象、函数),都-具有对象特性,即可⾃由扩展属性( null 除外)
- 所有的引⽤类型(数组、对象、函数),都有⼀个 proto 属性,属性值是⼀个普通的对象
- 所有的函数,都有⼀个 prototype 属性,属性值也是⼀个普通的对象
- 所有的引⽤类型(数组、对象、函数), proto 属性值指向它的构造函数的 prototype属性值
通过代码解释⼀下,⼤家可⾃⾏运⾏以下代码,看结果
// 要点⼀:⾃由扩展属性
var obj = {}; obj.a = 100;
var arr = []; arr.a = 100;
function fn () {}
fn.a = 100;
// 要点⼆:__proto__
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);
// 要点三:函数有 prototype
console.log(fn.prototype)
// 要点四:引⽤类型的 __proto__ 属性值指向它的构造函数的 prototype 属性值
console.log(obj.__proto__ === Object.prototyp
原型
// 构造函数
function Foo(name, age) {
this.name = name
}
Foo.prototype.alertName = function () {
alert(this.name)
}
// 创建示例
var f = new Foo('zhangsan')
f.printName = function () {
console.log(this.name)
}
// 测试
f.printName()
f.alertName()
执⾏ printName 时很好理解,但是执⾏ alertName 时发⽣了什么?这⾥再记住⼀个重点 当试图得到⼀个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的 proto (即它的构造函数的 prototype )中寻找,因此 f.alertName 就会找到 Foo.prototype.alertName 。
那么如何判断这个属性是不是对象本身的属性呢?使⽤ hasOwnProperty ,常⽤的地⽅是遍历⼀个对象的时候。
var item
for (item in f) {
// ⾼级浏览器已经在 for in 中屏蔽了来⾃原型的属性,但是这⾥建议⼤家还是加上这个判断,保证程序的健壮性
if (f.hasOwnProperty(item)) {
console.log(item)
}
}
原型链
还是接着上⾯的示例,如果执⾏ f.toString() 时,⼜发⽣了什么?
// 省略 N ⾏
// 测试
f.printName()
f.alertName()
f.toString
因为 f 本身没有 toString() ,并且 f.proto (即 Foo.prototype )中也没有 toString 。这个问题还是得拿出刚才那句话——当试图得到⼀个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的 proto (即它的构造函数的 prototype )中寻找。如果在 f.proto 中没有找到 toString ,那么就继续去 f.proto.proto 中寻找,因为 f.proto 就是⼀个普通的对象⽽已嘛!
- f.proto 即 Foo.prototype ,没有找到 toString ,继续往上找
- f.proto.proto 即 Foo.prototype.proto 。 Foo.prototype 就是⼀个普通
的对象,因此 Foo.prototype.proto 就是 Object.prototype ,在这⾥可以找到 toString - 因此 f.toString 最终对应到了 Object.prototype.toString
这样⼀直往上找,你会发现是⼀个链式的结构,所以叫做“原型链”。如果⼀直找到最上层都没有找到,那么就宣告失败,返回 undefined 。最上层是什么 —— Object.prototype.proto ===null
原型链中的 this
所有从原型或更⾼级原型中得到、执⾏的⽅法,其中的 this 在执⾏时,就指向了当前这个触发事件执⾏的对象。因此 printName 和 alertName 中的 this 都是 f 。
怎么做继承
①原型继承
组合继承
组合继承是最常⽤的继承⽅式
function Parent(value) {
this.val = value
}
Parent.prototype.getValue = function() {
console.log(this.val)
}
function Child(value) {
Parent.call(this, value)
}
Child.prototype = new Parent()
const child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
以上继承的⽅式核⼼是在⼦类的构造函数中通过Parent.call(this) 继承⽗类的属性,然后改变⼦类的原型为new Parent() 来继承⽗类的函数。这种继承⽅式优点在于构造函数可以传参,不会与⽗类引⽤属性共享,可以复⽤⽗类的函数,但是也存在⼀个缺点就是在继承⽗类函数的时候调⽤了⽗类构造函数,导致⼦类的原型上多了不需要的⽗类属性,存在内存上的浪费。
寄⽣组合继承
这种继承⽅式对组合继承进⾏了优化,组合继承缺点在于继承⽗类函数时调⽤了构造函数,我们只需要优化掉这点就⾏了。
function Parent(value) {
this.val = value
}
Parent.prototype.getValue = function() {
console.log(this.val)
}
function Child(value) {
Parent.call(this, value)
}
Child.prototype = Object.create(Parent.prototype,
{
constructor: {
value: Child,
enumerable: false,
writable: true,
configurable: true
}
})
const child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
以上继承实现的核⼼就是将⽗类的原型赋值给了⼦类,并且将构造函数设置为⼦类,这样既解决了⽆⽤的⽗类属性问题,还能正确的找到⼦类的构造函数。
②class继承
class Parent {
constructor(value) {
this.val = value
}
getValue() {
console.log(this.val)
}
}
class Child extends Parent {
constructor(value) {
super(value)
this.val = value
}
}
let child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
class 实现继承的核⼼在于使⽤ extends 表明继承⾃哪个⽗类,并且在⼦类构造函数中必须调⽤ super,因为这段代码可以看成
Parent.call(this, value)。当然了,之前也说了在 JS 中并不存在类,class 的本质就是函数。
call bind apply, apply的用途
call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向。
接收参数不一样:
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])
- apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
- apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
- apply 、 call 、bind 三者都可以利用后续参数传参;
- bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用
常用用法:
1.数组之间的追加
var array1 = [12 , "foo" , {name "Joe"} , -2458];
var array2 = ["Doe" , 555 , 100];
Array.prototype.push.apply(array1, array2);
/* array1 值为 [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */
2.获取数组中最大值和最小值,number 本身没有 max 方法,但是 Math 有,我们就可以借助 call 或者 apply 使用其方法。
var numbers = [5, 458 , 120 , -215 ];
var maxInNumbers = Math.max.apply(Math, numbers), //458
maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458
3.验证是否是数组(前提是toString()方法没有被重写过)
functionisArray(obj){
return Object.prototype.toString.call(obj) === '[object Array]' ;
}
4.类(伪)数组使用数组方法
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));
Javascript中存在一种名为伪数组的对象结构。比较特别的是 arguments 对象,还有像调用 getElementsByTagName , document.childNodes 之类的,它们返回NodeList对象都属于伪数组。不能应用 Array下的 push , pop 等方法。但是我们能通过 Array.prototype.slice.call 转换为真正的数组的带有 length 属性的对象,这样 domNodes 就可以应用 Array 下的所有方法了。
未完。。。
ps:笔者最近开通了微信公众号,大家可以关注一下哦,谢谢大家的支持!