那些年,在前端路上踩过的坑

大千世界,齐路万千。一不小心踩坑,深陷囵圄。几番周折,才得以逃脱。。。痛,来自爱。因爱生痛,痛的彻底,痛的刻骨。大爱前端,所以逢坑必填。试想,多年之后回首,万千平路,皆吾填之。故,坑,必填也。

目录

一、input框设置width:100%,溢出父类容器

2014,已去!为迎接新的一年公司会在除夕之时搞一场大型活动。而我,有幸参与此次活动的研发。是的,坑就是在这中情况下来的。

我们知道,浏览器是一种很神奇的东西(此处省略一万字)。各种奇形怪状,杂七杂八的BUG随处可见。这不,我就在input标签中踩着了。。。

情景: 项目引入了标准的normalize.css, 整个页面设在一个<div class="container"></div>下,由她来控制整个文档流整体的在页面中的显示(如居中,与两边的宽度等)。之后在其内部有一个input输入框,为了让其跨浏览器显示一般我们会将元素的宽度设置成百分比,即设置input的宽度为“100%”,那么问题来了:在PC浏览器上显示正常,但在移动端偏偏input越界了。这令我很是费解,尝试了N中方法就是不行,差点就选择用绝对长度来定位了。如下图:

input-width-100%25-wrong.png

解决方法:没事找我,有事找谷歌。overstack是个好东西,几乎你遇到的问题别人都遇到过了。结果增加了以下代码就ok了:

1
2
3
4
5
6
input {   
 width: 100%;     
 box-sizing: border-box;   
 -webkit-box-sizing:border-box;   
 -moz-box-sizing: border-box;   
}  

效果如下:

input-wdth-100%25-ok.png

那么这究竟是为什么呢?为此特意去study了一下…

我们知道每一个元素都有一个盒模型,而世界上存在两种计算元素宽度的方式: 一种是W3C的标准(宽度及为元素的宽度,不包括padding和border),另一种是传统的盒模型(宽度包括了了元素自身的宽度再加上padding和border)。

浏览器一般都是默认按照W3C标准盒模型来计算元素宽度的(除了IE的“Quirks Mode”).

比如:两种模式下相同样式不同结果

1
2
3
4
5
6
.demo {
  width: 250px;
  height: 100px;
  border: 5px solid #6374AB;
  padding: 10px;
}

第一个使用传统的盒模型,第二个使用W3C的盒模型,效果如下:

box.gif

区别很明显,传统的盒模型中元素的宽度就是content + padding + border,而 W3C的盒模型中元素的宽度是content。这就解释了为什么input输入框设置了100%会超出父类宽度了。因为当时环境中是width=100%,但padding和border不包括在这100%的宽度中,故渲染出来的宽度大于100%。

显然,按照传统的盒模型比较好控制。css3中出现了box-sizing属性,允许你切换盒模型:

1
2
box-sizing: border-box;
box-sizing: content-box;

box-sizing: border-box表示元素的大小是包括border以内的所有宽度,即传统盒模型,而box-sizing: content-box表示元素的宽度即content的宽度,不包括padding、border

Mozilla支持padding-box,即表示元素的大小是padding以内的宽度,不包括border

更精确的测试如下:

1
2
3
4
5
6
7
8
 div.test {
  width: 300px;
  padding: 10px;
  border: 5px solid #000000;
  margin-left: 10%;
  margin-bottom: 10px;
  margin-top: 10px;
 }

效果如下:

box-model-test.png

经此一役,恍然大悟。于是在normalize.css加上一下代码,确保万无一失:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*清除浮动*/
.clearfix:before, .clearfix:after {
  content: '';
  display: table;
}
.clearfix:after {
  clear: both;
  overflow: hidden;
}
.clearfix {
  zoom: 1;
}
/*设置所有元素为传统盒模型*/
*,
*:before,
*:after {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}

二、safari下input输入框有内描边及webkit css 整理

webkit内核浏览器默认会给input输入框加上内描边,这对专业的设计师来说是不能容忍的。而前端叉子们就得想尽办法出去她~ 多悲伤啊,人家浏览器也是为了你好,至于么???? 代码如下:

1
2
3
input {
   -webkit-appearance: none; // Safari 去掉内阴影
}

移动端中,使用click会出现绑定点击区域有高亮背景,修改背景颜色方法:

1
2
3
  input{
  -webkit-tap-highlight-color: rgba(0,0,0,0); // 设置点击区域的颜色
  }

ios中滑动效果:

1
2
3
body{
  -webkit-overflow-scrolling: touch;
}

好吧,webkit属性太多,整体把握才是王道,于是整理如下:

1. “box model”相关,包括content、padding、margin等
1
2
3
4
5
6
7
8
9
10
-webkit-border-bottom-left-radius: radius;
-webkit-border-top-left-radius: horizontal_radius vertical_radius;
-webkit-border-radius: radius;      //容器圆角
-webkit-box-sizing: sizing_model; 边框常量值:border-box/content-box
-webkit-box-shadow: hoff voff blur color; //容器阴影(参数分别为:水平X 方向偏移量;垂直Y 方向偏移量;高斯模糊半径值;阴影颜色值)
-webkit-margin-bottom-collapse: collapse_behavior; 常量值:collapse/discard/separate
-webkit-margin-start: width;
-webkit-padding-start: width;
-webkit-border-image: url(borderimg.gif) 25 25 25 25 round/stretch round/stretch;
-webkit-appearance: push-button;   //内置的CSS 表现,暂时只支持push-button
2. 视觉格式化模型”描述性质,确定了位置和大小的块元素
1
2
3
4
5
6
7
8
9
10
11
12
13
clip: rect(10px, 5px, 10px, 5px)
resize: auto; 常量:auto/both/horizontal/none/vertical
visibility: visible; 常量: collapse/hidden/visible
-webkit-transition: opacity 1s linear; 动画效果 ease/linear/ease-in/ease-out/ease-in-out
-webkit-backface-visibility: visibler; 常量:visible(默认值)/hidden
-webkit-box-reflect: right 1px; //镜向反转
-webkit-box-reflect: below 4px -webkit-gradient(linear, left top, left bottom,from(transparent), color-stop(0.5, transparent), to(white));
-webkit-mask-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0,0,0,1)), to(rgba(0,0,0,0)));;   //CSS 遮罩/蒙板效果
-webkit-mask-attachment: fixed; 常量:fixed/scroll
-webkit-perspective: value; 常量:none(默认)
-webkit-perspective-origin: left top;
-webkit-transform: rotate(5deg);
-webkit-transform-style: preserve-3d; 常量:flat/preserve-3d; (2D 与3D)
3.“颜色和背景”描述属性控制背景下的块级元素和颜色的文本内容的组成部分
1
2
3
-webkit-background-clip: content; 常量:border/content/padding/text
-webkit-background-origin: padding; 常量:border/content/padding/text
-webkit-background-size: 55px; 常量:length/length_x/length_y
4. “文本”描述属性的特定文字样式,间距和自动滚屏
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 text-shadow: #00FFFC 10px 10px 5px;
 text-transform: capitalize; 常量:capitalize/lowercase/none/uppercase
 word-wrap: break-word; 常量:break-word/normal
-webkit-marquee: right large infinite normal 10s; 常量:direction(方向) increment(迭代次数) repetition(重复) style(样式) speed(速度);
-webkit-marquee-direction: ahead/auto/backwards/down/forwards/left/reverse/right/up
-webkit-marquee-incrementt: 1-n/infinite(无穷次)
-webkit-marquee-speed: fast/normal/slow
-webkit-marquee-style: alternate/none/scroll/slide
-webkit-text-fill-color: #ff6600; 常量:capitalize, lowercase, none, uppercase  
-webkit-text-security: circle; 常量:circle/disc/none/square  // 如密码输入框使用该属性
-webkit-text-size-adjust: none; 常量:auto/none; //阻止屏幕旋转时字体自动调整
-webkit-text-stroke: 15px #fff;
-webkit-line-break: after-white-space; 常量:normal/after-white-space
-webkit-appearance: caps-lock-indicator;
-webkit-nbsp-mode: space; 常量: normal/space
-webkit-rtl-ordering: logical; 常量:visual/logical
-webkit-user-drag: element; 常量:element/auto/none
-webkit-user-modify: read-only; 常量:read-write-plaintext-only/read-write/read-only
-webkit-user-select: text; 常量:text/auto/none  // 是否允许用户选中文本
-webkit-touch-callout:none  //禁止 iOS 弹出各种操作窗口
input::-webkit-input-speech-button {display: none} // Andriod 上去掉语音输入按钮
5. “表格”描述的布局和设计性能表的具体内容
1
2
3
4
5
6
7
8
-webkit-border-horizontal-spacing: 2px;
-webkit-border-vertical-spacing: 2px;
-webkit-column-break-after: right; 常量:always/auto/avoid/left/right
-webkit-column-break-before: right; 常量:always/auto/avoid/left/right
–webkit-column-break-inside: logical; 常量:avoid/auto
-webkit-column-count: 3; //分栏
-webkit-column-rule: 1px solid #fff;
style:dashed,dotted,double,groove,hidden,inset,none,outset,ridge,solid
6. “用户界面”描述属性,涉及到用户界面元素在浏览器中,如滚动文字区,滚动条,等等
1
2
3
4
5
6
7
8
-webkit-box-align: baseline,center,end,start,stretch 常量:baseline/center/end/start/stretch
-webkit-box-direction: normal;常量:normal/reverse
-webkit-box-flex: flex_valuet
-webkit-box-flex-group: group_number
-webkit-box-lines: multiple; 常量:multiple/single
-webkit-box-ordinal-group: group_number
-webkit-box-orient: block-axis; 常量:block-axis/horizontal/inline-axis/vertical/orientation
–webkit-box-pack: alignment; 常量:center/end/justify/start
7. 动画过渡
1
2
3
4
5
6
7
8
-webkit-animation: title infinite ease-in-out 3s;
animation 有这几个属性:
-webkit-animation-name: //属性名,就是我们定义的keyframes
-webkit-animation-duration:3s //持续时间
-webkit-animation-timing-function: //过渡类型:ease/ linear(线性) /ease-in(慢到快)/ease-out(快到慢) /ease-in-out(慢到快再到慢) /cubic-bezier
-webkit-animation-delay:10ms //动画延迟(默认0)
-webkit-animation-iteration-count: //循环次数(默认1),infinite 为无限
-webkit-animation-direction: //动画方式:normal(默认 正向播放); alternate(交替方向,第偶数次正向播放,第奇数次反向播放)

三、移动端最佳使用单位

移动端使用什么单位是开发者最迫切需要知道的,px、%、pt、em 还是rem? 当然是rem。rem是非常好用的一个属性,可以根据html来设定基准值,而且兼容性也很不错。不过有的时候还是需要对一些莫名其妙的浏览器优雅降级。可以用以下的代码片段保证在低端浏览器下也不会出问题:

1
2
3
html { font-size: 62.5%; }
body { font-size: 14px; font-size: 1.4rem; } /* =14px */
h1   { font-size: 24px; font-size: 2.4rem; } /* =24px */

四、模拟:hover伪类

由于移动端没有鼠标指针,所以没有hover事件,所以css:hover伪类就没用了。但是移动端有touch事件,onTouchStart 类似 onMouseOver,onTouchEnd 类似 onMouseOut。所以我们可以用它来模拟hover。使用Javascript:

1
2
3
4
5
var myLinks = document.getElementsByTagName('a');
for(var i = 0, len = myLinks.length ; i < len; i++){
  myLinks[i].addEventListener(’touchstart’, function(){this.className = “hover”;}, false);
  myLinks[i].addEventListener(’touchend’, function(){this.className = “”;}, false);
}

然后用css增加hover效果:

1
a:hover, a:hover {/* 你要的效果*/}

这样设计一个链接,感觉可以更像按钮。并且,这个模拟可以用在任何元素上。

五、input类型为date情况下不支持placeholder

由于浏览器会针对date类型的input增加datepicker模块,所以有些系统不支持placeholder:

桌面端(Mac) – Safari 不支持 datepicker,placeholder 正常显示。 – Firefox 不支持 datepicker,placeholder 正常显示。 – Chrome 支持 datepicker,显示 年、月、日 格式,忽略placeholder。

移动端 – iPhone5 iOS7 有 datepicker 功能,但是不显示 placeholder。 – Andorid 4.0.4 无 datepicker 功能,不显示 placeholder

解决方法:

1
<input placeholder="Date" class="textbox-n" type="text" onfocus="(this.type='date')"  id="date">

因为text是支持placeholder的。因此当用户focus的时候自动把type类型改变为date,这样既有placeholder也有datepicker了。

六、active的兼容

要让a链接的CSS active伪类生效,只需要给这个a链接的touch系列的任意事件touchstart/touchend绑定一个空的匿名方法即可hack成功:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<style>
  a {
  color: #000;
  }
  a:active {
  color: #fff;
  }
</style>
<a herf=”asdasd”>asdasd</a>
<script>
  var a=document.getElementsByTagName(‘a’);
  for(var i=0;i<a.length;i++){
  a[i].addEventListener(‘touchstart’,function(){},false);
  }
</script>

七、测试是否支持svg

用以下代码:

1
document.implementation.hasFeature("http:// www.w3.org/TR/SVG11/feature#Image", "1.1");

八、ios下的隐私模式

这个“隐私模式”是最容易被忽视的。ios的safari提供一种“隐私模式”,如果你的webapp没有考虑这个兼容模式,那么在使用html5的本地存储的“localstorage”时,可能因为“隐私模式”下没有权限读写localstorage而使代码抛出错误,导致后续的js代码无法运行~

所以在使用localstorage的时候,首先应该判断是否支持localstorage。但是问题又来了:测试发现,即使在safari的“隐私模式”下,’localStorage’ in window的返回值依然为true,也就是不能用“’localStorage’ in window”来判断。接下来只能相当使用try catch了,虽然这是一个不太推荐被使用的方法,使用try catch捕获错误,使后续的js代码可以继续运行,代码如下:

1
2
3
4
5
6
7
8
9
try{
    if('localStorage' in window){
         //需要使用localStorage的代码写在这
    }else{
         //不支持的提示和向下兼容代码
    }
}catch(e){
    // 隐私模式相关提示代码和不支持的提示和向下兼容代码
}

所以,在需要兼容ios的safari的“隐私模式”的情况下,本地存储相关的代码需要使用try catch包裹并降级兼容。如果不知道的话,呵呵,那就惨了!!!!!

九、中文输入法输入英文,字母之间可能会出现一个六分之一空格

关于 iOS 系统中,中文输入法输入英文时,字母之间可能会出现一个六分之一空格,可以使用正则表达式过滤:

1
this.value = this.value.replace(/\u2006/g, '');

十、关于音频跟视频

代码:

1
2
3
<audio autoplay>
  <source  src="audio/alarm1.mp3" type="audio/mpeg">
</audio>

如上代码,系统默认情况下audio的autoplay属性是无法生效的,这也是手机为节省用户流量做的考虑。如果必须要自动播放,有两种方式可以解决。

1.捕捉一次用户输入后,让音频加载,下次即可播放
1
2
3
4
5
//play and pause it once
document.addEventListener('touchstart', function () {
    document.getElementsByTagName('audio')[0].play();
    document.getElementsByTagName('audio')[0].pause();
});

这种方法需要捕获一次用户的点击事件来促使音频跟视频加载。当加载后,你就可以用javascript控制音频的播放了,如调用audio.play().

2. 利用iframe加载资源
1
2
3
4
5
6
7
var ifr=document.createElement("iframe");
ifr.setAttribute('src', "http://mysite.com/myvideo.mp4");
ifr.setAttribute('width', '1px');
ifr.setAttribute('height', '1px');
ifr.setAttribute('scrolling', 'no');
ifr.style.border="0px";
document.body.appendChild(ifr);

这种方式其实跟第一种原理是一样的。当资源加载了你就可以控制播放了,但是这里使用iframe来加载,相当于直接触发资源加载。 注意,使用创建audio标签并让其加载的方式是不可行的。 慎用这种方法,会对用户造成很糟糕的影响。

>> 持续踩坑中…

文章评论

返回顶部