如何重设 MySQL 的 root 密码

作者:王赛

MySQL下创建新用户、新数据库、设定访问权限控制都需要用到root密码。万一把root密码忘了,该怎么办?

幸运地是,重设密码很容易。

注意:MySQL的root用户和服务器操作系统的root用户是两个不同的用户,不要搞混了。

安全模式重置法

基本的思路是,以安全模式启动mysql,这样不需要密码可以直接以root身份登录,然后重设密码。

首先,我们停掉MySQL服务:

sudo service mysql stop  

以上命令适用于Ubuntu和Debian。CentOS、Fedora和RHEL下使用mysqld替换mysql。

以安全模式启动MySQL:

sudo mysqld_safe --skip-grant-tables --skip-networking &  

注意我们加了--skip-networking,避免远程无密码登录 MySQL。

这样我们就可以直接用root登录,无需密码:

mysql -u root  

接着重设密码:

mysql> use mysql;  
mysql> update user set password=PASSWORD("mynewpassword") where User='root';  
mysql> flush privileges;  

注意,命令后需要加分号。

重设完毕后,我们退出,然后启动 MySQL 服务:

mysql > quit  

quit不需要分号。

重启服务:

sudo service mysql restart  

同样,以上命令适用于Ubuntu和Debian,Centos、Fedora和RHEL需要用mysqld替换mysql

现在可以尝试用新密码登录了:

mysql -u root -pmynewpassword  

注意,-p 和密码间不能有空格。

其他方案

以上是通用方案,在Ubuntu和Debian系统上,有一个debian-sys-maint用户,Debian类系统下一些系统脚本对mysql的操作是通过这个用户完成的。所以我们可以通过这个用户来修改 root 密码。该用户的密码可以在/etc/mysql/debian.cnf下找到:

# Automatically generated for Debian scripts. DO NOT TOUCH!
[client]
host     = localhost  
user     = debian-sys-maint  
password = PASSWORD  
socket   = /var/run/mysqld/mysqld.sock  
[mysql_upgrade]
host     = localhost  
user     = debian-sys-maint  
password = PASSWORD  
socket   = /var/run/mysqld/mysqld.sock  
basedir  = /usr  

用该用户登录 MySQL 后,也可以修改密码(具体修改过程见上):

sudo mysql -u debian-sys-maint -p  

百度网盘爬虫(如何爬取百度网盘)

因为要做去转盘网分类模式点我),所以一定要爬取网盘资源,本来想自己写一个爬虫挺不容易的,不想分享出来,但最后还是决定了拿给大家一起看吧,毕竟有交流才有进步,有兴趣的朋友也可以看看我写的其他日志或者关注我,会发现去转盘网的大部分技术现在可以说是公开状态,如有对你有帮助还是认真读读吧,下面是爬虫代码,我立马公开:

ps:不会python的孩子先去学学python,代码是python写的

我附上点资料:点我下载点我下载2

其实还有个磁力站,不过暂时技术不想公开出来,之后也想公开,喜欢的看看:ok搜搜

#coding: utf8

“””

author:haoning

create time: 2015-8-15

“””

 

import re #正则表达式模块

import urllib2 #获取URLs的组件

import time

from Queue import Queue

import threading, errno, datetime

import json

import requests #Requests is an Apache2 Licensed HTTP library

import MySQLdb as mdb

 

DB_HOST = ‘127.0.0.1’

DB_USER = ‘root’

DB_PASS = ”

 

 

#以下是正则匹配规则

re_start = re.compile(r’start=(\d+)’) #\d 表示0-9 任意一个数字 后面有+号 说明这个0-9单个数位出现一到多次 比如21312314

re_uid = re.compile(r’query_uk=(\d+)’) #查询编号

re_urlid = re.compile(r’&urlid=(\d+)’) #url编号

 

ONEPAGE = 20 #一页数据量

ONESHAREPAGE = 20 #一页分享连接量

 

#缺少专辑列表

URL_SHARE = ‘http://yun.baidu.com/pcloud/feed/getsharelist?auth_type=1&start={start}&limit=20&query_uk={uk}&urlid={id}’ #获得分享列表

“””

{“feed_type”:”share”,”category”:6,”public”:”1″,”shareid”:”1541924625″,”data_id”:”2418757107690953697″,”title”:”\u5723\u8bde\u58c1\u7eb8\u5927\u6d3e\u9001″,”third”:0,”clienttype”:0,”filecount”:1,”uk”:1798788396,”username”:”SONYcity03″,”feed_time”:1418986714000,”desc”:””,”avatar_url”:”http:\/\/himg.bdimg.com\/sys\/portrait\/item\/1b6bf333.jpg”,”dir_cnt”:1,”filelist”:[{“server_filename”:”\u5723\u8bde\u58c1\u7eb8\u5927\u6d3e\u9001″,”category”:6,”isdir”:1,”size”:1024,”fs_id”:870907642649299,”path”:”%2F%E5%9C%A3%E8%AF%9E%E5%A3%81%E7%BA%B8%E5%A4%A7%E6%B4%BE%E9%80%81″,”md5″:”0″,”sign”:”1221d7d56438970225926ad552423ff6a5d3dd33″,”time_stamp”:1439542024}],”source_uid”:”871590683″,”source_id”:”1541924625″,”shorturl”:”1dDndV6T”,”vCnt”:34296,”dCnt”:7527,”tCnt”:5056,”like_status”:0,”like_count”:60,”comment_count”:19},

public:公开分享

title:文件名称

uk:用户编号

“””

URL_FOLLOW = ‘http://yun.baidu.com/pcloud/friend/getfollowlist?query_uk={uk}&limit=20&start={start}&urlid={id}’ #获得订阅列表

“””

{“type”:-1,”follow_uname”:”\u597d\u55e8\u597d\u55e8\u554a”,”avatar_url”:”http:\/\/himg.bdimg.com\/sys\/portrait\/item\/979b832f.jpg”,”intro”:”\u9700\u8981\u597d\u8d44\u6599\u52a0994798392″,”user_type”:0,”is_vip”:0,”follow_count”:2,”fans_count”:2276,”follow_time”:1415614418,”pubshare_count”:36,”follow_uk”:2603342172,”album_count”:0},

follow_uname:订阅名称

fans_count:粉丝数

“””

URL_FANS = ‘http://yun.baidu.com/pcloud/friend/getfanslist?query_uk={uk}&limit=20&start={start}&urlid={id}’ # 获取关注列表

“””

{“type”:-1,”fans_uname”:”\u62e8\u52a8\u795e\u7684\u5fc3\u7eea”,”avatar_url”:”http:\/\/himg.bdimg.com\/sys\/portrait\/item\/d5119a2b.jpg”,”intro”:””,”user_type”:0,”is_vip”:0,”follow_count”:8,”fans_count”:39,”follow_time”:1439541512,”pubshare_count”:15,”fans_uk”:288332613,”album_count”:0}

avatar_url:头像

fans_uname:用户名

“””

 

QNUM = 1000

hc_q = Queue(20) #请求队列

hc_r = Queue(QNUM) #接收队列

success = 0

failed = 0

 

def req_worker(inx): #请求

s = requests.Session() #请求对象

while True:

req_item = hc_q.get() #获得请求项

 

req_type = req_item[0] #请求类型,分享?订阅?粉丝?

url = req_item[1] #url

r = s.get(url) #通过url获得数据

hc_r.put((r.text, url)) #将获得数据文本和url放入接收队列

print “req_worker#”, inx, url #inx 线程编号; url 分析了的 url

 

def response_worker(): #处理工作

dbconn = mdb.connect(DB_HOST, DB_USER, DB_PASS, ‘baiduyun’, charset=’utf8′)

dbcurr = dbconn.cursor()

dbcurr.execute(‘SET NAMES utf8’)

dbcurr.execute(‘set global wait_timeout=60000’) #以上皆是数据库操作

while True:

“””

#正则备注

match() 决定 RE 是否在字符串刚开始的位置匹配

search() 扫描字符串,找到这个 RE 匹配的位置

findall() 找到 RE 匹配的所有子串,并把它们作为一个列表返回

finditer() 找到 RE 匹配的所有子串,并把它们作为一个迭代器返回

百度页面链接:http://pan.baidu.com/share/link?shareid=3685432306&uk=1798788396&from=hotrec

uk 其实用户id值

“””

metadata, effective_url = hc_r.get() #获得metadata(也就是前面的r.text)和有效的url

#print “response_worker:”, effective_url

try:

tnow = int(time.time()) #获得当前时间

id = re_urlid.findall(effective_url)[0] #获得re_urlid用户编号

start = re_start.findall(effective_url)[0] #获得start用户编号

if True:

if ‘getfollowlist’ in effective_url: #type = 1,也就是订阅类

follows = json.loads(metadata) #以将文本数据转化成json数据格式返回

uid = re_uid.findall(effective_url)[0] #获得re_uid,查询编号

if “total_count” in follows.keys() and follows[“total_count”]>0 and str(start) == “0”: #获得订阅数量

for i in range((follows[“total_count”]-1)/ONEPAGE): #开始一页一页获取有用信息

try:

dbcurr.execute(‘INSERT INTO urlids(uk, start, limited, type, status) VALUES(%s, %s, %s, 1, 0)’ % (uid, str(ONEPAGE*(i+1)), str(ONEPAGE)))

#存储url编号,订阅中有用户编号,start表示从多少条数据开始获取,初始status=0为未分析状态

except Exception as ex:

print “E1”, str(ex)

pass

 

if “follow_list” in follows.keys(): #如果订阅者也订阅了,即拥有follow_list

for item in follows[“follow_list”]:

try:

dbcurr.execute(‘INSERT INTO user(userid, username, files, status, downloaded, lastaccess) VALUES(%s, “%s”, 0, 0, 0, %s)’ % (item[‘follow_uk’], item[‘follow_uname’], str(tnow)))

#存储订阅这的用户编号,用户名,入库时间

except Exception as ex:

print “E13”, str(ex)

pass

else:

print “delete 1”, uid, start

dbcurr.execute(‘delete from urlids where uk=%s and type=1 and start>%s’ % (uid, start))

elif ‘getfanslist’ in effective_url: #type = 2,也就是粉丝列表

fans = json.loads(metadata)

uid = re_uid.findall(effective_url)[0]

if “total_count” in fans.keys() and fans[“total_count”]>0 and str(start) == “0”:

for i in range((fans[“total_count”]-1)/ONEPAGE):

try:

dbcurr.execute(‘INSERT INTO urlids(uk, start, limited, type, status) VALUES(%s, %s, %s, 2, 0)’ % (uid, str(ONEPAGE*(i+1)), str(ONEPAGE)))

except Exception as ex:

print “E2”, str(ex)

pass

 

if “fans_list” in fans.keys():

for item in fans[“fans_list”]:

try:

dbcurr.execute(‘INSERT INTO user(userid, username, files, status, downloaded, lastaccess) VALUES(%s, “%s”, 0, 0, 0, %s)’ % (item[‘fans_uk’], item[‘fans_uname’], str(tnow)))

except Exception as ex:

print “E23”, str(ex)

pass

else:

print “delete 2”, uid, start

dbcurr.execute(‘delete from urlids where uk=%s and type=2 and start>%s’ % (uid, start))

else: #type=0,也即是分享列表

shares = json.loads(metadata)

uid = re_uid.findall(effective_url)[0]

if “total_count” in shares.keys() and shares[“total_count”]>0 and str(start) == “0”:

for i in range((shares[“total_count”]-1)/ONESHAREPAGE):

try:

dbcurr.execute(‘INSERT INTO urlids(uk, start, limited, type, status) VALUES(%s, %s, %s, 0, 0)’ % (uid, str(ONESHAREPAGE*(i+1)), str(ONESHAREPAGE)))

except Exception as ex:

print “E3”, str(ex)

pass

if “records” in shares.keys():

for item in shares[“records”]:

try:

dbcurr.execute(‘INSERT INTO share(userid, filename, shareid, status) VALUES(%s, “%s”, %s, 0)’ % (uid, item[‘title’], item[‘shareid’])) #item[‘title’]恰好是文件名称

#返回的json信息:

except Exception as ex:

#print “E33”, str(ex), item

pass

else:

print “delete 0”, uid, start

dbcurr.execute(‘delete from urlids where uk=%s and type=0 and start>%s’ % (uid, str(start)))

dbcurr.execute(‘delete from urlids where id=%s’ % (id, ))

dbconn.commit()

except Exception as ex:

print “E5”, str(ex), id

dbcurr.close()

dbconn.close() #关闭数据库

 

def worker():

global success, failed

dbconn = mdb.connect(DB_HOST, DB_USER, DB_PASS, ‘baiduyun’, charset=’utf8′)

dbcurr = dbconn.cursor()

dbcurr.execute(‘SET NAMES utf8’)

dbcurr.execute(‘set global wait_timeout=60000’)

#以上是数据库相关设置

while True:

 

#dbcurr.execute(‘select * from urlids where status=0 order by type limit 1’)

dbcurr.execute(‘select * from urlids where status=0 and type>0 limit 1’) #type>0,为非分享列表

d = dbcurr.fetchall()

#每次取出一条数据出来

#print d

if d: #如果数据存在

id = d[0][0] #请求url编号

uk = d[0][1] #用户编号

start = d[0][2]

limit = d[0][3]

type = d[0][4] #哪种类型

dbcurr.execute(‘update urlids set status=1 where id=%s’ % (str(id),)) #状态更新为1,已经访问过了

url = “”

if type == 0: #分享

url = URL_SHARE.format(uk=uk, start=start, id=id).encode(‘utf-8’) #分享列表格式化

#query_uk uk 查询编号

#start

#urlid id url编号

elif  type == 1: #订阅

url = URL_FOLLOW.format(uk=uk, start=start, id=id).encode(‘utf-8’) #订阅列表格式化

elif type == 2: #粉丝

url = URL_FANS.format(uk=uk, start=start, id=id).encode(‘utf-8’) #关注列表格式化

if url:

hc_q.put((type, url)) #如果url存在,则放入请求队列,type表示从哪里获得数据

#通过以上的url就可以获得相应情况下的数据的json数据格式,如分享信息的,订阅信息的,粉丝信息的

 

#print “processed”, url

else: #否则从订阅者或者粉丝的引出人中获得信息来存储,这个过程是爬虫树的下一层扩展

dbcurr.execute(‘select * from user where status=0 limit 1000’)

d = dbcurr.fetchall()

if d:

for item in d:

try:

dbcurr.execute(‘insert into urlids(uk, start, limited, type, status) values(“%s”, 0, %s, 0, 0)’ % (item[1], str(ONESHAREPAGE)))

#uk 查询号,其实是用户编号

#start 从第1条数据出发获取信息

#

dbcurr.execute(‘insert into urlids(uk, start, limited, type, status) values(“%s”, 0, %s, 1, 0)’ % (item[1], str(ONEPAGE)))

dbcurr.execute(‘insert into urlids(uk, start, limited, type, status) values(“%s”, 0, %s, 2, 0)’ % (item[1], str(ONEPAGE)))

dbcurr.execute(‘update user set status=1 where userid=%s’ % (item[1],)) #做个标志,该条数据已经访问过了

#跟新了分享,订阅,粉丝三部分数据

except Exception as ex:

print “E6”, str(ex)

else:

time.sleep(1)

 

dbconn.commit()

dbcurr.close()

dbconn.close()

 

def main():

print ‘starting at:’,now()

for item in range(16):

t = threading.Thread(target = req_worker, args = (item,))

t.setDaemon(True)

t.start() #请求线程开启,共开启16个线程

s = threading.Thread(target = worker, args = ())

s.setDaemon(True)

s.start() #worker线程开启

response_worker()  #response_worker开始工作

print ‘all Done at:’, now()

史上最全的CSS hack方式一览

做前端多年,虽然不是经常需要hack,但是我们经常会遇到各浏览器表现不一致的情况。基于此,某些情况我们会极不情愿的使用这个不太友好的方式来达到大家要求的页面表现。我个人是不太推荐使用hack的,要知道一名好的前端,要尽可能不使用hack的情况下实现需求,做到较好的用户体验。可是啊,现实太残酷,浏览器厂商之间历史遗留的问题让我们在目标需求下不得不向hack妥协,虽然这只是个别情况。今天,结合自己的经验和理解,做了几个demo把IE6~IE10和其他标准浏览器的CSS hack做一个总结,也许本文应该是目前最全面的hack总结了吧。

什么是CSS hack

由于不同厂商的流览器或某浏览器的不同版本(如IE6-IE11,Firefox/Safari/Opera/Chrome等),对CSS的支持、解析不一样,导致在不同浏览器的环境中呈现出不一致的页面展现效果。这时,我们为了获得统一的页面效果,就需要针对不同的浏览器或不同版本写特定的CSS样式,我们把这个针对不同的浏览器/不同版本写相应的CSS code的过程,叫做CSS hack!

CSS hack的原理

由于不同的浏览器和浏览器各版本对CSS的支持及解析结果不一样,以及CSS优先级对浏览器展现效果的影响,我们可以据此针对不同的浏览器情景来应用不同的CSS。

CSS hack分类

CSS Hack大致有3种表现形式,CSS属性前缀法、选择器前缀法以及IE条件注释法(即HTML头部引用if IE)Hack,实际项目中CSS Hack大部分是针对IE浏览器不同版本之间的表现差异而引入的。

  • 属性前缀法(即类内部Hack):例如 IE6能识别下划线”_”和星号” * “,IE7能识别星号” * “,但不能识别下划线”_”,IE6~IE10都认识”\9″,但firefox前述三个都不能认识。
  • 选择器前缀法(即选择器Hack):例如 IE6能识别*html .class{},IE7能识别*+html .class{}或者*:first-child+html .class{}。
  • IE条件注释法(即HTML条件注释Hack):针对所有IE(注:IE10+已经不再支持条件注释): <!–[if IE]>IE浏览器显示的内容 <![endif]–>,针对IE6及以下版本: <!–[if lt IE 6]>只在IE6-显示的内容 <![endif]–>。这类Hack不仅对CSS生效,对写在判断语句里面的所有代码都会生效。

CSS hack书写顺序,一般是将适用范围广、被识别能力强的CSS定义在前面。

CSS hack方式一:条件注释法

这种方式是IE浏览器专有的Hack方式,微软官方推荐使用的hack方式。举例如下

	只在IE下生效
	<!--[if IE]>
	这段文字只在IE浏览器显示
	<![endif]-->
	
	只在IE6下生效
	<!--[if IE 6]>
	这段文字只在IE6浏览器显示
	<![endif]-->
	
	只在IE6以上版本生效
	<!--[if gte IE 6]>
	这段文字只在IE6以上(包括)版本IE浏览器显示
	<![endif]-->
	
	只在IE8上不生效
	<!--[if ! IE 8]>
	这段文字在非IE8浏览器显示
	<![endif]-->
	
	非IE浏览器生效
	<!--[if !IE]>
	这段文字只在非IE浏览器显示
	<![endif]-->

CSS hack方式二:类内属性前缀法

属性前缀法是在CSS样式属性名前加上一些只有特定浏览器才能识别的hack前缀,以达到预期的页面展现效果。

IE浏览器各版本 CSS hack 对照表

hack 写法 实例 IE6(S) IE6(Q) IE7(S) IE7(Q) IE8(S) IE8(Q) IE9(S) IE9(Q) IE10(S) IE10(Q)
* *color 青色 Y Y Y Y N Y N Y N Y
+ +color 绿色 Y Y Y Y N Y N Y N Y
-color 黄色 Y Y N N N N N N N N
_ _color 蓝色 Y Y N Y N Y N Y N N
# #color 紫色 Y Y Y Y N Y N Y N Y
\0 color:red\0 红色 N N N N Y N Y N Y N
\9\0 color:red\9\0 粉色 N N N N N N Y N Y N
!important color:blue !important;color:green; 棕色 N N Y N Y N Y N Y Y

说明:在标准模式中

  • “-″减号是IE6专有的hack
  • “\9″ IE6/IE7/IE8/IE9/IE10都生效
  • “\0″ IE8/IE9/IE10都生效,是IE8/9/10的hack
  • “\9\0″ 只对IE9/IE10生效,是IE9/10的hack

demo如下

  1. <script type=“text/javascript”>
  2.     //alert(document.compatMode);
  3. </script>
  4. <style type=“text/css”>
  5. body:nth-of-type(1) .iehack{
  6.     color#F00;/* 对Windows IE9/Firefox 7+/Opera 10+/所有Chrome/Safari的CSS hack ,选择器也适用几乎全部Mobile/Linux/Mac browser*/
  7. }
  8. .demo1,.demo2,.demo3,.demo4{
  9.     width:100px;
  10.     height:100px;
  11. }
  12. .hack{
  13. /*demo1 */
  14. /*demo1 注意顺序,否则IE6/7下可能无法正确显示,导致结果显示为白色背景*/
  15.     background-color:red/* All browsers */
  16.     background-color:blue !important;/* All browsers but IE6 */
  17.     *background-color:black/* IE6, IE7 */
  18.     +background-color:yellow;/* IE6, IE7*/
  19.     background-color:gray\9/* IE6, IE7, IE8, IE9, IE10 */
  20.     background-color:purple\0/* IE8, IE9, IE10 */
  21.     background-color:orange\9\0;/*IE9, IE10*/
  22.     _background-color:green/* Only works in IE6 */
  23.     *+background-color:pink; /*  WARNING: Only works in IE7 ? Is it right? */
  24. }
  25. /*可以通过javascript检测IE10,然后给IE10的<html>标签加上class=”ie10″ 这个类 */
  26. .ie10 #hack{
  27.     color:red/* Only works in IE10 */
  28. }
  29. /*demo2*/
  30. .iehack{
  31. /*该demo实例是用于区分标准模式下ie6~ie9和Firefox/Chrome的hack,注意顺序
  32. IE6显示为:绿色,
  33. IE7显示为:黑色,
  34. IE8显示为:红色,
  35. IE9显示为:蓝色,
  36. Firefox/Chrome显示为:橘色,
  37. (本例IE10效果同IE9,Opera最新版效果同IE8)
  38. */
  39.     background-color:orange;  /* all – for Firefox/Chrome */
  40.     background-color:red\0;  /* ie 8/9/10/Opera – for ie8/ie10/Opera */
  41.     background-color:blue\9\0;  /* ie 9/10 – for ie9/10 */
  42.     *background-color:black;  /* ie 6/7 – for ie7 */
  43.     _background-color:green;  /* ie 6 – for ie6 */
  44. }
  45. /*demo3
  46. 实例是用于区分标准模式下ie6~ie9和Firefox/Chrome的hack,注意顺序
  47. IE6显示为:红色,
  48. IE7显示为:蓝色,
  49. IE8显示为:绿色,
  50. IE9显示为:粉色,
  51. Firefox/Chrome显示为:橘色,
  52. (本例IE10效果同IE9,Opera最新版效果也同IE9为粉色)
  53. */
  54. .element {
  55.     background-color:orange;    /* all IE/FF/CH/OP*/
  56. }
  57. .element {
  58.     *background-colorblue;    /* IE6+7, doesn’t work in IE8/9 as IE7 */
  59. }
  60. .element {
  61.     _background-colorred;     /* IE6 */
  62. }
  63. .element {
  64.     background-colorgreen\0/* IE8+9+10  */
  65. }
  66. :root .element { background-color:pink\0; }  /* IE9+10 */
  67. /*demo4*/
  68. /*
  69. 该实例是用于区分标准模式下ie6~ie10和Opera/Firefox/Chrome的hack,本例特别要注意顺序
  70. IE6显示为:橘色,
  71. IE7显示为:粉色,
  72. IE8显示为:黄色,
  73. IE9显示为:紫色,
  74. IE10显示为:绿色,
  75. Firefox显示为:蓝色,
  76. Opera显示为:黑色,
  77. Safari/Chrome显示为:灰色,
  78. */
  79. .hacktest{
  80.     background-color:blue;      /* 都识别,此处针对firefox */
  81.     background-color:red\9;      /*all ie*/
  82.     background-color:yellow\0;    /*for IE8/IE9/10 最新版opera也认识*/
  83.     +background-color:pink;        /*for ie6/7*/
  84.     _background-color:orange;       /*for ie6*/
  85. }
  86. @media screen and (min-width:0){
  87.     .hacktest {background-color:black\0;}  /*opera*/
  88. }
  89. @media screen and (min-width:0) {
  90.     .hacktest { background-color:purple\9; }/*  for IE9/IE10  PS:国外有些习惯常写作\0,根本没考虑Opera也认识\0的实际 */
  91. }
  92. @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
  93.    .hacktest { background-color:green; } /* for IE10+ 此写法可以适配到高对比度和默认模式,故可覆盖所有ie10的模式 */
  94. }
  95. @media screen and (-webkit-min-device-pixel-ratio:0){ .hacktest {background-color:gray;} }  /*for Chrome/Safari*/
  96. /* #963棕色 :root is for IE9/IE10, 优先级高于@media, 慎用!如果二者合用,必要时在@media样式加入 !important 才能区分IE9和IE10 */
  97. /*
  98. :root .hacktest { background-color:#963\9; } 
  99. */
  100. </style>

 

demo1是测试不同IE浏览器下hack 的显示效果
IE6显示为:粉色,
IE7显示为:粉色,
IE8显示为:蓝色,
IE9显示为:蓝色,
Firefox/Chrome/Opera显示为:蓝色,
若去掉其中的!important属性定义,则IE6/7仍然是粉色,IE8是紫色,IE9/10为橙色,Firefox/Chrome变为红色,Opera是紫色。是不是有些奇怪:除了IE6以外,其他所有的表现都符合我们的期待。那为何IE6表现的颜色不是_background-color:green;的绿色而是*+background-color:pink的粉色呢?其实是最后一句所谓的IE7私有hack惹的祸?不是说*+是IE7的专有hack吗???错,你可能太粗心了!我们常说的IE7专有*+hack的格式是*+html selector,而不是上面的直接在属性上加*+前缀。如果是为IE7定制特殊样式,应该这样使用:

*+html #ie7test { /* IE7 only*/
	color:green;
}

经过测试,我发现属性前缀*+background-color:pink;只有IE6和IE7认识。而*+html selector只有IE7认识。所以我们在使用时候一定要特别注意。

demo2实例是用于区分标准模式下ie6~ie9和Firefox/Chrome的hack,注意顺序
IE6显示为:绿色,
IE7显示为:黑色,
IE8显示为:红色,
IE9显示为:蓝色,
Firefox/Chrome显示为:橘色,
(本例IE10效果同IE9,Opera最新版效果同IE8)

demo3实例也是用于区分标准模式下ie6~ie9和Firefox/Chrome的hack,注意顺序
IE6显示为:红色,
IE7显示为:蓝色,
IE8显示为:绿色,
IE9显示为:粉色,
Firefox/Chrome显示为:橘色,
(本例IE10效果同IE9,Opera最新版效果也同IE9为粉色)

demo4实例是用于区分标准模式下ie6~ie10和Opera/Firefox/Chrome的hack,本例特别要注意顺序
IE6显示为:橘色,
IE7显示为:粉色,
IE8显示为:黄色,
IE9显示为:紫色,
IE10显示为:绿色,
Firefox显示为:蓝色,
Opera显示为:黑色,
Safari/Chrome显示为:灰色,

CSS hack方式三:选择器前缀法

选择器前缀法是针对一些页面表现不一致或者需要特殊对待的浏览器,在CSS选择器前加上一些只有某些特定浏览器才能识别的前缀进行hack。

目前最常见的是

*html *前缀只对IE6生效
*+html *+前缀只对IE7生效
@media screen\9{...}只对IE6/7生效
@media \0screen {body { background: red; }}只对IE8有效
@media \0screen\,screen\9{body { background: blue; }}只对IE6/7/8有效
@media screen\0 {body { background: green; }} 只对IE8/9/10有效
@media screen and (min-width:0\0) {body { background: gray; }} 只对IE9/10有效
@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {body { background: orange; }} 只对IE10有效
等等

结合CSS3的一些选择器,如html:first-child,body:nth-of-type(1),衍生出更多的hack方式,具体的可以参考下表:

CSS3选择器结合JavaScript的Hack

我们用IE10进行举例:

由于IE10用户代理字符串(UserAgent)为:Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0),所以我们可以使用javascript将此属性添加到文档标签中,再运用CSS3基本选择器匹配。

JavaScript代码:

	var htmlObj = document.documentElement;
	htmlObj.setAttribute('data-useragent',navigator.userAgent);
	htmlObj.setAttribute('data-platform', navigator.platform );

CSS3匹配代码:

html[data-useragent*='MSIE 10.0'] #id {
	color: #F00;
}

CSS hack利弊

一般情况下,我们尽量避免使用CSS hack,但是有些情况为了顾及用户体验实现向下兼容,不得已才使用hack。比如由于IE8及以下版本不支持CSS3,而我们的项目页面使用了大量CSS3新属性在IE9/Firefox/Chrome下正常渲染,这种情况下如果不使用css3pie或htc或条件注释等方法时,可能就得让IE8-的专属hack出马了。使用hack虽然对页面表现的一致性有好处,但过多的滥用会造成html文档混乱不堪,增加管理和维护的负担。相信只要大家一起努力,少用、慎用hack,未来一定会促使浏览器厂商的标准越来越趋于统一,顺利过渡到标准浏览器的主流时代。抛弃那些陈旧的IE hack,必将减轻我们编码的复杂度,少做无用功。

最后补上一张引自国外某大牛总结的CSS hack表,这时一张6年前的旧知识汇总表了,放在这里仅供需要时候方便参考。

说明:本文测试环境为IE6~IE10,Chrome 29.0.1547.66 m,Firefox 20.0.1 ,Opera 12.02等。一边工作,一边总结,总结了几天写下整理好,今天把它分享出来,文中难免有纰漏,如大侠发现请及时告知!

CSS 的优先级机制[总结]

样式的优先级

多重样式(Multiple Styles):如果外部样式、内部样式和内联样式同时应用于同一个元素,就是使多重样式的情况。

一般情况下,优先级如下:

(外部样式)External style sheet <(内部样式)Internal style sheet <(内联样式)Inline style

 

有个例外的情况,就是如果外部样式放在内部样式后面,则外部样式将覆盖内部样式。

示例如下:

<head>
    <style type=”text/css”>
      /* 内部样式 */
      h3{color:green;}
    </style>
    <!– 外部样式 style.css –>
    <link rel=”stylesheet” type=”text/css” href=”style.css”/>
    <!– 设置:h3{color:blue;} –>
</head>
<body>
    <h3>测试!</h3>
</body>

 

选择器的优先权

 

jc6_002

 

解释:

1.  内联样式表的权值最高 1000;

2.  ID 选择器的权值为 100

3.  Class 类选择器的权值为 10

4.  HTML 标签选择器的权值为 1

 

利用选择器的权值进行计算比较,示例如下:

<html>
  <head>
    <style type=”text/css”>
        #redP p {
             /* 权值 = 100+1=101 */
             color:#F00;  /* 红色 */
        }
        #redP .red em {
             /* 权值 = 100+10+1=111 */
             color:#00F; /* 蓝色 */
        }
        #redP p span em {
             /* 权值 = 100+1+1+1=103 */
             color:#FF0;/*黄色*/
        }
    </style>
  </head>
  <body>
     <div id=”redP”>
        <p class=”red”>red
           <span><em>em red</em></span>
        </p>
        <p>red</p>
     </div>
  </body>
</html>

结果:<em> 标签内的数据显示为蓝色。

 

CSS 优先级法则:

A  选择器都有一个权值,权值越大越优先;

B  当权值相等时,后出现的样式表设置要优于先出现的样式表设置;

C  创作者的规则高于浏览者:即网页编写者设置的CSS 样式的优先权高于浏览器所设置的样式;

D  继承的CSS 样式不如后来指定的CSS 样式;

E  在同一组属性设置中标有!important”规则的优先级最大;示例如下:

<html>
  <head>
    <style type=”text/css”>
     #redP p{
        /*两个color属性在同一组*/
        color:#00f !important; /* 优先级最大 */
        color:#f00;
     }
    </style>
  </head>
  <body>
     <div id=”redP”>
       <p>color</p>
       <p>color</p>
     </div>
  </body>
</html>

结果:在Firefox 下显示为蓝色;在IE  6 下显示为红色

 

使用脚本添加样式

当在连接外部样式后,再在其后面使用JavaScript 脚本插入内部样式时(即内部样式使用脚本创建),IE 浏览器就表现出它的另类了。代码如下:

<html>
<head>
  <title> demo </title>
  <meta name=”Author” content=”xugang” />
  <!– 添加外部CSS 样式 –>
  <link rel=”stylesheet” href=”styles.css” type=”text/css” />
  <!– 在外部的styles.css文件中,代码如下:
       h3 {color:blue;}
  –>
  <!– 使用javascript 创建内部CSS 样式 –>
  <script type=”text/javascript”>
  <!–
    (function(){
        var agent = window.navigator.userAgent.toLowerCase();
        var is_op = (agent.indexOf(opera) != -1);
        var is_ie = (agent.indexOf(msie) != -1) && document.all && !is_op;
        var is_ch = (agent.indexOf(chrome) != -1);
        var cssStr=h3 {color:green;};
        var s=document.createElement(style);
        var head=document.getElementsByTagName(head).item(0);
        var link=document.getElementsByTagName(link);
        link=link.item(0);
        if(is_ie)
        {
            if(link)
                head.insertBefore(s,link);
            else
                head.appendChild(s);
            document.styleSheets.item(document.styleSheets.length-1).cssText=cssStr;
        }
        else if(is_ch)
        {
            var t=document.createTextNode();
            t.nodeValue=cssStr;
            s.appendChild(t);
            head.insertBefore(s,link);
        }
        else
        {
            s.innerHTML=cssStr;
            head.insertBefore(s,link);
        }
    })();
  //–>
  </script>
</head>
<body>
  <h3>在IE中我是绿色,非IE浏览器下我是蓝色!</h3>
</body>
</html>

结果在Firefox / Chrome / Safari / Opera 中,文字都是蓝色的。而在IE 浏览器中,文字却是绿色的。

 

附加

在IE 中添加样式内容的JavaScript 代码:

var s=document.createElement(“style”);
var head=document.getElementsByTagName(“head”).item(0);
var link=document.getElementsByTagName(“link”).item(0);
head.insertBefore(s,link);
/* 注意:在IE 中,
   虽然代码是将<style>插入在<link>之前,
   但实际内存中,<style>却在<link>之后。
   这也是“IE中奇怪的应用CSS的BUG”之所在!
*/
var oStyleSheet = document.styleSheets[0];
//这实际是在<link>的外部样式中追加
oStyleSheet.addRule(“h3″,”color:green;”);
alert(oStyleSheet.rules[0].style.cssText);
alert(document.styleSheets[0].rules[0].style.cssText);
//方式2
var cssStr=”h3 {color:green;}”;
document.styleSheets.item(document.styleSheets.length-1).cssText=cssStr;

 

IE 浏览器下载或者渲染的顺序可能如下:

●   IE 下载的顺序是从上到下;

●   JavaScript 函数的执行会阻塞IE 的下载;

●   IE 渲染的顺序也是从上到下;

●   IE 的下载和渲染是同时进行的;

●   在渲染到页面的某一部分时,其上面的所有部分都已经下载完成(但并不是说所有相关联的元素都已经下载完。)

●   在下载过程中,如果遇到某一标签是嵌入文件,并且文件是具有语义解释性的(例如:JS脚本,CSS样式),那么此时IE的下载过程会启用单独连接进行下载。并且在下载后进行解析,如果JS、CSS中如有重定义,后面定义的函数将覆盖前面定义的函数。

●   解析过程中,停止页面所有往下元素的下载。样式表文件比较特殊,在其下载完成后,将和以前下载的所有样式表一起进行解析,解析完成后,将对此前所有元素(含以前已经渲染的)重新进行样式渲染。并以此方式一直渲染下去,直到整个页面渲染完成。

●   Firefox 处理下载和渲染的顺序大体相同,只是在细微之处有些差别,例如:iframe 的渲染。

深入理解Javascript之执行上下文(Execution Context)

在这篇文章中,将比较深入地阐述下执行上下文 – Javascript中最基础也是最重要的一个概念。相信读完这篇文章后,你就会明白javascript引擎内部在执行代码以前到底做了些什么,为什么某些函数以及变量在没有被声明以前就可以被使用,以及它们的最终的值是怎样被定义的。

什么是执行上下文

Javascript中代码的运行环境分为以下三种:

  • 全局级别的代码 – 这个是默认的代码运行环境,一旦代码被载入,引擎最先进入的就是这个环境。
  • 函数级别的代码 – 当执行一个函数时,运行函数体中的代码。
  • Eval的代码 – 在Eval函数内运行的代码。

在网上可以找到很多阐述作用域的资源,为了使该文便于大家理解,我们可以将“执行上下文”看做当前代码的运行环境或者作用域。下面我们来看一个示例,其中包括了全局以及函数级别的执行上下文:

上图中,一共用4个执行上下文。紫色的代表全局的上下文;绿色代表person函数内的上下文;蓝色以及橙色代表person函数内的另外两个函数的上下文。注意,不管什么情况下,只存在一个全局的上下文,该上下文能被任何其它的上下文所访问到。也就是说,我们可以在person的上下文中访问到全局上下文中的sayHello变量,当然在函数firstName或者lastName中同样可以访问到该变量。

至于函数上下文的个数是没有任何限制的,每到调用执行一个函数时,引擎就会自动新建出一个函数上下文,换句话说,就是新建一个局部作用域,可以在该局部作用域中声明私有变量等,在外部的上下文中是无法直接访问到该局部作用域内的元素的。在上述例子的,内部的函数可以访问到外部上下文中的声明的变量,反之则行不通。那么,这到底是什么原因呢?引擎内部是如何处理的呢?

执行上下文堆栈

在浏览器中,javascript引擎的工作方式是单线程的。也就是说,某一时刻只有唯一的一个事件是被激活处理的,其它的事件被放入队列中,等待被处理。下面的示例图描述了这样的一个堆栈:

我们已经知道,当javascript代码文件被浏览器载入后,默认最先进入的是一个全局的执行上下文。当在全局上下文中调用执行一个函数时,程序流就进入该被调用函数内,此时引擎就会为该函数创建一个新的执行上下文,并且将其压入到执行上下文堆栈的顶部。浏览器总是执行当前在堆栈顶部的上下文,一旦执行完毕,该上下文就会从堆栈顶部被弹出,然后,进入其下的上下文执行代码。这样,堆栈中的上下文就会被依次执行并且弹出堆栈,直到回到全局的上下文。请看下面一个例子:

1
2
3
4
5
6
7
8
(function foo(i) {
if (i === 3) {
return;
}
else {
foo(++i);
}
}(0));

上述foo被声明后,通过()运算符强制直接运行了。函数代码就是调用了其自身3次,每次是局部变量i增加1。每次foo函数被自身调用时,就会有一个新的执行上下文被创建。每当一个上下文执行完毕,该上上下文就被弹出堆栈,回到上一个上下文,直到再次回到全局上下文。真个过程抽象如下图:

由此可见 ,对于执行上下文这个抽象的概念,可以归纳为以下几点:

  • 单线程
  • 同步执行
  • 唯一的一个全局上下文
  • 函数的执行上下文的个数没有限制
  • 每次某个函数被调用,就会有个新的执行上下文为其创建,即使是调用的自身函数,也是如此。

执行上下文的建立过程

我们现在已经知道,每当调用一个函数时,一个新的执行上下文就会被创建出来。然而,在javascript引擎内部,这个上下文的创建过程具体分为两个阶段:

  1. 建立阶段(发生在当调用一个函数时,但是在执行函数体内的具体代码以前)
    • 建立变量,函数,arguments对象,参数
    • 建立作用域链
    • 确定this的值
  2. 代码执行阶段:
    • 变量赋值,函数引用,执行其它代码

实际上,可以把执行上下文看做一个对象,其下包含了以上3个属性:

1
2
3
4
5
 executionContextObj = {
variableObject: { /* 函数中的arguments对象, 参数, 内部的变量以及函数声明 */ },
scopeChain: { /* variableObject 以及所有父执行上下文中的variableObject */ },
this: {}
}

建立阶段以及代码执行阶段的详细分析

确切地说,执行上下文对象(上述的executionContextObj)是在函数被调用时,但是在函数体被真正执行以前所创建的。函数被调用时,就是我上述所描述的两个阶段中的第一个阶段 – 建立阶段。这个时刻,引擎会检查函数中的参数,声明的变量以及内部函数,然后基于这些信息建立执行上下文对象(executionContextObj)。在这个阶段,variableObject对象,作用域链,以及this所指向的对象都会被确定。

上述第一个阶段的具体过程如下:

  1. 找到当前上下文中的调用函数的代码
  2. 在执行被调用的函数体中的代码以前,开始创建执行上下文
  3. 进入第一个阶段-建立阶段:
    • 建立variableObject对象:
      1. 建立arguments对象,检查当前上下文中的参数,建立该对象下的属性以及属性值
      2. 检查当前上下文中的函数声明:
        • 每找到一个函数声明,就在variableObject下面用函数名建立一个属性,属性值就是指向该函数在内存中的地址的一个引用
        • 如果上述函数名已经存在于variableObject下,那么对应的属性值会被新的引用所覆盖。
      3. 检查当前上下文中的变量声明:
        • 每找到一个变量的声明,就在variableObject下,用变量名建立一个属性,属性值为undefined。
        • 如果该变量名已经存在于variableObject属性中,直接跳过(防止指向函数的属性的值被变量属性覆盖为undefined),原属性值不会被修改。
    • 初始化作用域链
    • 确定上下文中this的指向对象
  4. 代码执行阶段:
    • 执行函数体中的代码,一行一行地运行代码,给variableObject中的变量属性赋值。

下面来看个具体的代码示例:

1
2
3
4
5
6
7
8
9
10
11
  function foo(i) {
var a = ‘hello’;
var b = function privateB() {

};
function c() {

}
}

foo(22);

在调用foo(22)的时候,建立阶段如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fooExecutionContext = {
variableObject: {
arguments: {
0: 22,
length: 1
},
i: 22,
c: pointer to function c()
a: undefined,
b: undefined
},
scopeChain: {},
this: {}
}

由此可见,在建立阶段,除了arguments,函数的声明,以及参数被赋予了具体的属性值,其它的变量属性默认的都是undefined。一旦上述建立阶段结束,引擎就会进入代码执行阶段,这个阶段完成后,上述执行上下文对象如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fooExecutionContext = {
variableObject: {
arguments: {
0: 22,
length: 1
},
i: 22,
c: pointer to function c()
a: ‘hello’,
b: pointer to function privateB()
},
scopeChain: {},
this: {}
}

我们看到,只有在代码执行阶段,变量属性才会被赋予具体的值。

局部变量作用域提升的缘由

在网上一直看到这样的总结: 在函数中声明的变量以及函数,其作用域提升到函数顶部,换句话说,就是一进入函数体,就可以访问到其中声明的变量以及函数。这是对的,但是知道其中的缘由吗?相信你通过上述的解释应该也有所明白了。不过在这边再分析一下。看下面一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(function() {

console.log(typeof foo); // function pointer
console.log(typeof bar); // undefined

var foo = ‘hello’,
bar = function() {
return ‘world’;
};

function foo() {
return ‘hello’;
}

}());

上述代码定义了一个匿名函数,并且通过()运算符强制理解执行。那么我们知道这个时候就会有个执行上下文被创建,我们看到例子中马上可以访问foo以及bar变量,并且通过typeof输出foo为一个函数引用,bar为undefined。

  • 为什么我们可以在声明foo变量以前就可以访问到foo呢?

    因为在上下文的建立阶段,先是处理arguments, 参数,接着是函数的声明,最后是变量的声明。那么,发现foo函数的声明后,就会在variableObject下面建立一个foo属性,其值是一个指向函数的引用。当处理变量声明的时候,发现有var foo的声明,但是variableObject已经具有了foo属性,所以直接跳过。当进入代码执行阶段的时候,就可以通过访问到foo属性了,因为它已经就存在,并且是一个函数引用。

  • 为什么bar是undefined呢?

    因为bar是变量的声明,在建立阶段的时候,被赋予的默认的值为undefined。由于它只要在代码执行阶段才会被赋予具体的值,所以,当调用typeof(bar)的时候输出的值为undefined。

好了,到此为止,相信你应该对执行上下文有所理解了,这个执行上下文的概念非常重要,务必好好搞懂之!