优惠券活动模版功能开发之填坑小结

踩坑背景简述

最近负责养车M端优惠券活动模版功能的开发,由于这次开发任务本来不是安排给我的,所以这个功能产品在做需求评审宣讲时我没有参加,固很多逻辑和交互细节都不是很清楚,当然也包括M端一些不能解的坑在宣讲时也没有向产品讲清楚,到我这就只能接受得按照原型去实现…迎难而上,开整!

一.fixed定位和输入框弹出键盘问题

遇到的坑

看了原型设计就发现了一个不好解的坑:需求是要把带文本框的登录窗浮层固定在浏览器可视区最底部。

大概就是这个样子(红色区域):

大家想必一看也知道怎么回事了,要实现底部固定定位显示就得用position: fixed;,但是有输入框就会有软键盘弹出,fixed加上弹出键盘产生的各种兼容性问题各种奇奇怪怪的现象各种闪屏卡屏问题,像这种常识性的前端坑应该在需求评审时就打回去或者M端存在的问题和风险应该向产品和项目讲清楚的,好像并没有…

实践折中解决办法

经过和产品耐心的一遍又一遍的讲解这个坑的问题(包括走邮件这种很正式的流程…)后,产品才答应将登录窗底部浮层固定显示改为点击按钮模态弹窗形式显示。有输入框弹出键盘那fixed肯定不能用啊,那只有弹窗position: absolute;了。

1
2
3
4
5
6
7
.modal-dialog {
position: absolute;
top: 38%;
left: 50%;
-webkit-transform: translate3d(-50%, -50%, 0);
transform: translate3d(-50%, -50%, 0);
}

那么这样的话为了不让浮层跟随页面滑动,bodyhtml就得动态添加class

1
2
3
4
.fn-overflow-hide {
position: relative;
overflow: hidden;
}
1
2
3
4
5
6
7
8
9
// 未登录时点击浮层立即领取
$('.btn-receive', $footerBtnGroups).on('click', function(event) {
$('html, body').addClass('fn-overflow-hide');
$loginReceiveModal.removeClass('fn-hide');
$telephoneInput.trigger('click').focus();

event.stopPropagation();
event.preventDefault();
});

看下真机上的实际效果吧。

默认页面长这个样子:

点击立即使用弹出登录窗

呃呃…两个问题:1.内容区被置顶;2.弹窗移出了浏览器可视区

这是不希望看到的,我希望它能记住我当前滑动页面所处可视区的位置,即点击按钮时获取当前bodyscrollTop值,当关闭窗口是将这个值设置回来,并让页面内容区移动scrollTop值个像素位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var bodyScrollTop = 0; // body的scrollTop值

// 未登录时点击浮层立即领取
$('.btn-receive', $footerBtnGroups).on('click', function(event) {
bodyScrollTop = $('body').eq(0).scrollTop();

$couponActivityMain.css({
'-webkit-transform': 'translate3d(0, -' + bodyScrollTop + 'px, 0)',
'transform': 'translate3d(0, -' + bodyScrollTop + 'px, 0)'
});

$('html, body').addClass('fn-overflow-hide');
$loginReceiveModal.removeClass('fn-hide');
$telephoneInput.trigger('click').focus();
$('body').eq(0).scrollTop(0);

event.stopPropagation();
event.preventDefault();
});

看下效果

嗯…不错,页面留在了滑动的当前位置没有置顶,登录弹窗也正确显示。由于使用了position: absolute;,所以不会有乱七八糟的问题。

最后在取消立即领取成功后将body设回之前全局变量纪录的scrollTop值并将内容区Dom的translate移除即可。

1
2
3
4
5
6
7
8
9
10
11
// 隐藏登录领取弹窗
function hideLoginReceiveModal() {
$telephoneInput.val('');
$validateCodeInput.val('');

$('html, body').removeClass('fn-overflow-hide');
$couponActivityMain.removeAttr('style');
$('body').eq(0).scrollTop(bodyScrollTop);

$loginReceiveModal.addClass('fn-hide');
}

So…这个问题算是以彼此(开发和产品)都能接受的方式解决了。

小结

虽然这个坑很常识,但是没能在评审时提出来是前端的失误(?),希望大家今后引以为鉴,能预知到的坑尽量早点说明白别坑自己。

二.Safari浏览器通过Schema协议呼起App客户端问题

遇到的坑

需求要求我的页面如果在App客户端中内嵌则点击立即使用按钮返回App首页,如果不在App中内嵌在M页中,则如果该用户安装了App客户端点击按钮呼起App到首页,如果没有安装App则跳转到我们App的下载页面。

网上查了下大概了解了下解决办法都是创建一个iframe及设置定时器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 创建一个隐藏的iframe
var iframe = $('<iframe style="display: none;" src="usehome://main/show"></iframe>');
var downloadUrl = 'http://yc.m.autohome.com.cn/v2/app/download.html?PreciseAD=' + PreciseAD;
$('body').append(iframe);

var openTime = Date.now(); // 记录唤醒时间
window.setTimeout(function() {
iframe.remove();
if (Date.now() - openTime < 1400) {
var UA = navigator.userAgent;
if (/MicroMessenger/ig.test(UA)) {
window.location.href = downloadUrl;
return false;
} else { // eslint-disable-line
if (by.versions.android) {
window.location.href = downloadUrl;
} else {
window.location.href = 'http://itunes.apple.com/cn/app/qi-che-zhi-jia/id898126444?mt=8';
}
}
}
}, 1000);

先来看看在IOS上的UC和QQ浏览器的唤起表现:

嗯…不错,表现正常,完美唤起。

实践折中解决办法

再来看看我们的Safari浏览器,发现Safari此时并没有唤起App而是直接跳走App Store了。这不是我们想要的,既然Iframe不起作用,那只能通过window.location.href了,因为它通过window.location指向URL Scheme直接打开本地应用,加上看看。

1
2
3
4
5
6
7
8
...
if (by.versions.iPhone || by.versions.webApp) {
window.location.href = 'usehome://main/show';
}
// 创建一个隐藏的iframe
var iframe = $('<iframe style="display: none;" src="usehome://main/show"></iframe>');
$('body').append(iframe);
...

当你打开链接时,Mobile Safari通过window.location指向URL Scheme,直接打开本地应用,否则1s后打开下载页面。如果应用成功打开,生命周期就是激活状态,那么浏览器的状态是进入后台,页面里的所有操作都被注销了,显然timeout会被clear掉,但如果你没有成功打开应用即返回404,那么1s后页面当然会自动跳转了。

哦了,可以唤起App客户端。但是同时发现如果不立即点击打开按钮,页面依旧是会跳下载页的…

这可不好啊,那我第一时间的想法是让它等待的时间稍微再长点,给用户思考确认时间啊,那么代码变成了这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 如果在Safari浏览器中
var millisec; // 等待延时时间
if (by.versions.iPhone || by.versions.webApp) {
millisec = 3000;
window.location.href = 'usehome://main/show';
} else {
millisec = 1000;
}

// 创建一个隐藏的iframe
var iframe = $('<iframe style="display: none;" src="usehome://main/show"></iframe>');
$('body').append(iframe);

var openTime = Date.now(); // 记录唤醒时间
window.setTimeout(function() {
iframe.remove();
if (Date.now() - openTime < (millisec + 400)) {
var UA = navigator.userAgent;
if (/MicroMessenger/ig.test(UA)) {
window.location.href = 'http://yc.m.autohome.com.cn/v2/app/download.html?PreciseAD=' + PreciseAD;
return false;
} else { // eslint-disable-line
if (by.versions.android) {
window.location.href = 'http://yc.m.autohome.com.cn/v2/app/download.html?PreciseAD=' + PreciseAD;
} else {
window.location.href = 'http://itunes.apple.com/cn/app/qi-che-zhi-jia/id898126444?mt=8';
}
}
}
}, millisec);

再看下发现页面不会立即跳转走,定时器等待用户确认,点击确认正常唤起App.

但是时不时的不点击确认还是会直接跳下载页,为了不让其直接通过http协议跳转(不需要用户的确认),我查到了App Store的URL Scheme协议itms-apps://,改成该种方式发现果然好使,等待时页面不会再跳转走了。

1
window.location.href = 'itms-apps://itunes.apple.com/cn/app/qi-che-zhi-jia/id898126444?mt=8';

当用户未安装App时,由于找不到Scheme定义的地址浏览器会先弹出一个提示窗:

这个是系统提示没法避免,只能点击确定,之后3s延时后(确认去关闭这个提示窗的时间刚好定时器在走时间)会弹出是否打开App Store的确认弹窗:

点击确认正常打开App Store。

最后,经过测试同学和产品同学的使用体验都能够接受这样的处理方式。测试同学也没发现其他什么问题。

So…这个坑也算顺利跨过去了!

小结

可能我的解决方法也并不是最优的,也许还有更好的,希望今后亲们遇到这两个坑了可以参考下我的这个填坑小结,如有更好的解决方案欢迎交流学习。

坚持原创技术分享,您的支持将鼓励我继续创作!