手机端按钮包含按钮的触发问题

问题

如果给一个dom添加触摸事件以后,那这个dom里面子元素的触摸事件还可以触发么?这是最近项目中遇到的一个问题。

之后我做了个测试。

 

测试

测试环境:iphone4&ios6,lumia920&wp8,某安卓机

测试的点击方式:默认的click、MBP、Fastclick、Zepto&tap。

测试演示:http://gaofeiyu.com/demo/click/click.html

演示的基本逻辑是黄色块点击后会隐藏自己,黑色的底色块和红色块点击后会改变色块内的html并记录点击次数。

演示里面可以看出click和Fastclick的子元素和父元素都会触发,而MBP和tap事件只有当前dom会触发。

 

总结

在移动项目中还是Zepto和Fastclick混用的方案比较合适,无论点透还是这种逻辑混乱的交互都搞得通。

建议在项目中尽量用一种触摸事件触发方式,不然碰到问题改代码会很难看。我这边最开始用的是tap,之后发现各种问题搞得不得不做很多多余的操作,在优化后还是统一用了Fastclick,父子元素的问题,也只能换个方向去解决,毕竟这个问题本身的用户体验就不是很好。

 

 

我还顺便写了个连点的小样例在演示里面,有心的人可以分析一下。

 

 

《Terraria 泰拉瑞亚》太好玩了!

好久没玩过这么好玩的单机了~这游戏除了这么久我现在才玩到真是相见恨晚~

发文时间游戏版本是1.2.3.1,游戏的生存和建造够复杂,够多元,戳中沉迷点。

维基百科快速到达->

现在搞的steam的英文正版,玩了一段中文后,玩英文也不违和,还可以学习英文,So,easy!

 

 

Q:如何引入蘑菇人NPC?

A:森林女神那里有卖夜光蘑菇种子的,随便在地上找个地方用淤泥(Mud block)搭个空间(以长出蘑菇后bgm和bg变成蘑菇为准),把蘑菇种子种上去,在旁边建个能住人的房子,蘑菇长出来他就来了。

 

Q:如何获取无极的光明和黑暗碎片?

A:要杀光明木乃伊和黑暗木乃伊。

关于光明木乃伊和黑暗木乃伊,如果世界里面没有自然形成的神圣沙漠和腐败沙漠要自己做一个。

去挖好多沙子、珍珠石和黑炭石。

神圣沙漠就是铺一层珍珠石,距离最好是长一点,最好是悬空,距离别的生态环境远一些(以上都是为了方便刷怪),然后在上面铺沙子,我是铺到站在上面看不到珍珠石为止(需要好多沙子~有沙枪应该好一些),慢慢等待沙子神圣化就好了~然后就等着出怪刷碎片了。

腐败同理。

 

Q:机械骷髅打法。

A:其实没有特别的打法,骷髅基本伤害不高,我是有了翅膀以后才打的,打了几次最大的感触就是,需要操作比较多,最重要的就是,不要把手都打掉!我的武器都是aoe的,打骷髅只打头好辛苦啊。

 

Q:世纪花的打法。

A:穿神圣套打发现太吃力了,非要这么打的话,开好空间搭好台子,像打骷髅那么打也还是能打的,就是太累了。简单打只要一点地方做个传送装置两边传,就好弄多了。如果有雨云法杖,放两个乌云,把花引到乌云上,然后就一直传送就好了…

 

Q:Golem的打法。

A:以前这个家伙什么东西都不穿墙,据说造个台子骗他上去,随便什么可以穿过地形的东西都傻掉了。

现在1.2.3.1他除了身体都可以穿墙了。激光速度太快了,跑来跑去打到头分离,又要躲脑袋,装备不好还是新手的我感觉好难。

之前在神庙被滚球伤的好深,这回我也做个机关来坑他~

 

 

 

关于移动设备“点透”的问题

“点透”是什么

你可能碰到过在列表页面上创建一个弹出层,弹出层有个关闭的按钮,你点了这个按钮关闭弹出层后后,这个按钮正下方的内容也会执行点击事件(或打开链接)。我暂时定义为这是一个“点透”现象。

如何产生的

一般来说我们用原生的touch事件,还有jqueryMobile和zepto的tap可能会产生上面的问题。
相关的内容可以参考http://blog.youyo.name/archives/zepto-tap-click-through-research.html

注:当你点击链接页面刷新太快也会出现点透状况,这个是要在测试中特别注意的。

来说点转载文章中没说到的

规避这个点透现象除了上面参考文章说的方法,有更简单的方法。
推荐方法是引入Fastclick.js插件,该插件重写了默认的click事件,你可以通过
FastClick.attach(document.body);

来对整个文档操作,也可以对某节点单独操作。
那么我之前写的《触屏网页防止误点击(GHOSTCLICK)》中的 MBP 也可以避免这个问题,选择你喜欢的方法去解决。

 

 

触屏网页防止误点击(ghostClick)

如果在触屏的网页中,你为一个dom元素绑定’touchstart’,’touchend’事件来实现点击互动,那么如果你拖动屏幕时触摸到这个dom也会触发这两个事件,因为move的过程中也会执行start和end,这违背了我们的交互需要。
显然,我们要想办法规避这个问题。
如果你用jqueryMobil或者zepto,只要用里面针对触屏的事件,是不会碰到这个问题的。
基本思路就是重写了触发的的方法,根据条件来判断当前事件状态,在’touchend’的时候来决定当前用户是用了点击还是用了移动。
如果不想引入类库要怎么做。

我们增加一个函数MBP(全称为Mobile Boilerplate,该点击事件函数是该项目中的一部分)。
整个函数参考《HTML5移动Web开发实践》四章9节的代码内容

但是书里的内容有些问题,我对里面的内容做了一些修改,我们看下面代码。

函数部分

(function(document){//防止误点击
window.MBP = window.MBP || {};
MBP.fastButton = function (element, handler) {//函数入口
this.element = element;
this.handler = handler;
if (element.addEventListener) {//这里对选用的触发事件做了区分,保证触屏和非触屏用的是不同的事件绑定。
if (document.hasOwnProperty("ontouchstart")) {
element.addEventListener('touchstart', this, false);
}else{
element.addEventListener('click', this, false);
}
}
};
MBP.fastButton.prototype.handleEvent = function(event) {//监听触发事件类型
switch (event.type) {
case 'touchstart': this.onTouchStart(event); break;
case 'touchmove': this.onTouchMove(event); break;
case 'touchend': this.onClick(event); break;
case 'click': this.onClick(event); break;
}
};
MBP.fastButton.prototype.onTouchStart = function(event) {
//onTouchStart方法监听touchmove和touchend事件,Stoppropagation函数用来防止事件在监听中传递,阻止冒泡,即只在当前方法体内寻找当前事件的监听器
event.stopPropagation();
this.element.addEventListener('touchend', this, false);
document.body.addEventListener('touchmove', this, false);
this.startX = event.touches[0].clientX;
this.startY = event.touches[0].clientY;
};
MBP.fastButton.prototype.onTouchMove = function(event) {//如果触摸偏移10像素以上判断为move事件
if(Math.abs(event.touches[0].clientX - this.startX) > 10 ||
Math.abs(event.touches[0].clientY - this.startY) > 10 ) {
this.reset();
}
};
MBP.fastButton.prototype.onClick = function(event) {//如果不是其他可能,执行事件
event.stopPropagation();
this.reset();
this.handler(event);
if(event.type == 'touchend') {
MBP.preventGhostClick(this.startX, this.startY);
}
};
MBP.fastButton.prototype.reset = function() {//核心方法,移除move和touched的事件,以保证做正确的事件
this.element.removeEventListener('touchend', this, false);
document.body.removeEventListener('touchmove', this, false);
};
MBP.preventGhostClick = function (x, y) {
MBP.coords.push(x, y);
window.setTimeout(function (){
MBP.coords.splice(0, 2);
}, 2500);
};
MBP.ghostClickHandler = function (event) {//这部分是对不支持touch事件设备的兼容
for(var i = 0, len = MBP.coords.length; i < len; i += 2) {
var x = MBP.coords[i];
var y = MBP.coords[i + 1];
if(Math.abs(event.clientX - x) < 25 && Math.abs(event.clientY - y) < 25) {
event.stopPropagation();
event.preventDefault();
}
}
};
if (document.addEventListener) {//为文档中其他click事件添加防止误点击操作
document.addEventListener('click', MBP.ghostClickHandler, true);
}
MBP.coords = [];
})(document);

如何使用

var btn = document.getElementById('domId');
btnTouch = new MBP.fastButton(btn,function(){alert('点击成功')});

关于触摸事件的延伸——点透问题

在移动设备中用了触摸点击事件会产生一系列的点透问题,可以转到我文章中的《关于移动设备“点透”的问题》查看解决方法。

其他补充

不要在该方法生成的事件中添加alert事件,不然在ios(我的环境是ios6)中触发后会在下一次触摸屏幕的时候触发touchstart事件,执行未被销毁的alert产生问题。

批处理压缩css和js for nodejs

同事推荐的压缩css和js的方法,是运行在nodejs的基础上的,所以你要先安装一个nodejs。

在有nodejs的基础上,进入命令行模式分别运行

npm install clean-css -g

npm install uglifyjs -g

clean-css是压缩css的,uglifyjs是压缩js的。
然后新建两个批处理文件。
@echo off
:: 设置压缩css文件的根目录,脚本会自动按树层次查找和压缩所有的css
SET OUTFOLDER= 压缩后的目标目录
echo 正在深度复制文件
xcopy 准备压缩的目录*.* "%OUTFOLDER%" /s /e && echo 复制成功 || echo 复制失败,请检查文件是否存在!
echo 复制完成
:: 设置压缩的路径
chdir /d %OUTFOLDER%
echo 正在查找CSS 文件
for /r . %%b in (*.css) do (
@echo 正在压缩 %%~b ...
cleancss -o %%~fb %%~fb
)
echo CSS压缩完成
echo 完成!

@echo off
:: 设置压缩JS文件的根目录,脚本会自动按树层次查找和压缩所有的JS
SET OUTFOLDER = 压缩后的目标目录
echo 正在深度复制文件
xcopy 准备压缩的目录*.* "%OUTFOLDER%" /s /e && echo 复制成功 || echo 复制失败,请检查文件是否存在!
echo 复制完成
:: 设置压缩的路径
chdir /d %OUTFOLDER%
echo 正在查找JS文件
for /r . %%a in (*.js) do (
@echo 正在压缩 %%~a ...
uglifyjs %%~fa -o %%~fa
)
echo JS压缩完成
echo 完成!

目录的格式应该是 C:\abc\
只要你写的路径是绝对路径批处理文件可以放在任意你喜欢的位置。
如果有需要,你也可以写到一起。

白菜开花了

老爹老妈以前就爱这么弄,生命力旺盛的白菜,在叶子被吃了以后把根直接丢在水里,就可以重新燃起绿意。
之前买的娃娃菜,留下根放进水里,现在也终于开花了,好友成就感。
门槛好低的成就感。

WP_20140303_004

WP_20140212_002

WP_20140228_002

 

zepto兼容ie10的方法

原先的zepto,通过__proto__赋值,来使dom继承到$.fn方法,

无奈IE11之前的IE10,IE9不支持这种写法,

所以我们只能自己手动把方法添加到dom
// `$.zepto.Z` swaps out the prototype of the given `dom` array
// of nodes with `$.fn` and thus supplying all the Zepto functions
// to the array. Note that `__proto__` is not supported on Internet
// Explorer. This method can be overriden in plugins.
zepto.Z = function(dom, selector) {
dom = dom || []

// 支持ie10,主要是支持wp8
if(navigator.userAgent.indexOf(“MSIE 10″) > -1){
for(var func in $.fn){
dom[func] = $.fn[func];
}
}
else{
dom.__proto__ = $.fn
}

dom.selector = selector || ”
return dom
}

继续阅读“zepto兼容ie10的方法”

关于js中,“与”运算符“&&”和“或”运算符“||”的一些用法

在各类插件里总是能看到这样的赋值。
var a = b || {}
能看出如果b有值就赋值b,如果没有就给一个空对象。
那么下面这段呢
var a = (b==5 && 1) || (b==10 && 2) || (b==12 && 3) || (b==15 && 4) || 0;
就不是那么清晰可见了,下面引用一篇文章,详细说明一下这个问题。

原文地址:http://www.jb51.net/article/21339.htm

首先出个题:

如图:
假设对成长速度显示规定如下:
成长速度为5显示1个箭头;
成长速度为10显示2个箭头;
成长速度为12显示3个箭头;
成长速度为15显示4个箭头;
其他都显示都显示0各箭头。
用代码怎么实现?

差一点的if,else:
Js代码
var add_level = 0;
if(add_step == 5){
add_level = 1;
}
else if(add_step == 10){
add_level = 2;
}
else if(add_step == 12){
add_level = 3;
}
else if(add_step == 15){
add_level = 4;
}
else {
add_level = 0;
}

稍好些的switch:
Js代码
var add_level = 0;
switch(add_step){
case 5 : add_level = 1;
break;
case 10 : add_level = 2;
break;
case 12 : add_level = 3;
break;
case 15 : add_level = 4;
break;
default : add_level = 0;
break;

}如果需求改成:
成长速度为>12显示4个箭头;
成长速度为>10显示3个箭头;
成长速度为>5显示2个箭头;
成长速度为>0显示1个箭头;
成长速度为<=0显示0个箭头。

那么用switch实现起来也很麻烦了。

那么你有没有想过用一行就代码实现呢?
ok,让我们来看看js强大的表现力吧:
Js代码
var add_level = (add_step==5 && 1) || (add_step==10 && 2) || (add_step==12 && 3) || (add_step==15 && 4) || 0;
更强大的,也更优的:

Js代码
var add_level={'5':1,'10':2,'12':3,'15':4}[add_step] || 0;
第二个需求:
Js代码
var add_level = (add_step>12 && 4) || (add_step>10 && 3) || (add_step>5 && 2) || (add_step>0 && 1) || 0;
首先我们来梳理一下一个概念,请你一定要记住:在js逻辑运算中,0、””、null、false、undefined、NaN都会判为false,其他都为true(好像没有遗漏了吧,请各位确认下)。这个一定要记住,不然应用||和&&就会出现问题。
这里顺便提下:经常有人问我,看到很多代码if(!!attr),为什么不直接写if(attr);
其实这是一种更严谨的写法:
请测试 typeof 5和typeof !!5的区别。!!的作用是把一个其他类型的变量转成的bool类型。
下面主要讨论下逻辑运算符&&和||。
几乎所有语言中||和&&都遵循“短路”原理,如&&中第一个表达式为假就不会去处理第二个表达式,而||正好相反。
js也遵循上述原则。但是比较有意思的是它们返回的值。
代码:var attr = true && 4 && “aaa”;
那么运行的结果attr就不是简单的true或这false,而是”aaa”
再来看看||:
代码:var attr = attr || “”;这个运算经常用来判断一个变量是否已定义,如果没有定义就给他一个初始值,这在给函数的参数定义一个默认值的时候比较有用。因为js不像php可以直接在型参数上定义func($attr=5)。再次提醒你记住上面的原则:如果实参需要是0、””、null、false、undefined、NaN的时候也会当false来处理。

if(a >=5){
alert(“你好”);
}
可以写成:
a >= 5 && alert(“你好”);
这样只需一行代码就搞定。但是需要注意的一点就是:js中||和&&的特性帮我们精简了代码的同时,也带来了代码可读性的降低。这就需要我们自己来权衡了。
一方面精简js代码,能实质性的减少网络流量,尤其是大量应用的js公用库。个人比较推荐的做法是:如果是相对复杂的应用,请适当地写一些注释。这个和正在表达式一样,能够精简代码,但是可读性会降低,对读代码的人要求会高些,最好的办法就是写注释。

我们可以不使用这些技巧,但是我们一定要能看懂,因为这些技巧已经广泛应用,尤其是像JQuery等js框里的代码,不理解这些你就很难看懂别人的代码。
像var Yahoo = Yahoo || {};这种是非常广泛应用的。
ok,最后让我们来看一段jQuery中的代码吧:

Js代码
var wrap =
// option or optgroup
!tags.indexOf("<opt") &&
[ 1, "<select multiple='multiple'>", "</select>" ] ||!tags.indexOf("<leg") &&
[ 1, "<fieldset>", "</fieldset>" ] ||

tags.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
[ 1, "<table>", "</table>" ] ||

!tags.indexOf("<tr") &&
[ 2, "<table><tbody>", "</tbody></table>" ] ||

// <thead> matched above
(!tags.indexOf("<td") || !tags.indexOf("<th")) &&
[ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||

!tags.indexOf("<col") &&
[ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||

// IE can't serialize <link> and <script> tags normally
!jQuery.support.htmlSerialize &&
[ 1, "div<div>", "</div>" ] ||

[ 0, "", "" ];

// Go to html and back, then peel off extra wrappers
div.innerHTML = wrap[1] + elem + wrap[2];

// Move to the right depth
while ( wrap[0]-- )
div = div.lastChild;


这段代码是作者用来处理 $(html) 时,有些标签必须要约束的,如<option>必须在<select></select>之内的。
可能你也发现了作者还有一个很巧的地方就是 !tags.indexOf(“<opt”) ,作者很巧很简单的就实现了startWith的功能了,没有一点多余的代码。jquery源代码中还有很多如此精妙的代码,大家可以去学习学习。