先祝大家新年快乐!作为2016年的开篇,再祝各位单身狗早日托单,已托单的早日生猴子!

前年接触到个移动端项目,需要把相关门店信息以类似IOS(iPhone)联系人列表的形式呈现。左侧是城市列表,右侧是滑动首字母快速查找。
先是去网上搜了下,发现相关资料少之又少,无法满足项目需求。以我性格,改一个差距甚远的代码,不如在条件范围内动手写一个,也利于后期调试、扩展。

可在项目结束之后,感觉这形式的交互效果今后试用范围和需求会不少,便拎出来改为一个独立的插件。

首先看下页面结构:

<div class="contacts">
	<div class="contacts-bd">
		<div class="contacts-header"></div>
		<ul class="contacts-content">
			<li id="a" class="contacts-group">
				<div class="contacts-group-hd">A</div>
				<div class="contacts-group-bd">
					<a href="#">Aalto</a>
					<a href="#">Aaron</a>
				</div>
			</li>
			<li id="b" class="contacts-group">
				<div class="contacts-group-hd">B</div>
				<div class="contacts-group-bd">
					<a href="#">Baade</a>
					<a href="#">Baal</a>
				</div>
			</li>
			...
			<li id="z" class="contacts-group">
				<div class="contacts-group-hd">z</div>
				<div class="contacts-group-bd">
					<a href="#">Zachariah</a>
					<a href="#">Zachary</a>
				</div>
			</li>
		</ul>
		<ul class="contacts-nav" id="contacts-nav">
			<li alt="a">a</li>
			<li alt="b">b</li>
			...
			<li alt="z">z</li>
		</ul>
	</div>
</div>

在此把我遇到的几个主要问题和解决方法给提及一下:

1. 浏览器弹性滚动

这是实际测试中遇到的最大麻烦,弹性滚动也被大家称之为橡皮筋效果/拖动反弹,这是IOS的一个特定,并不单指微信浏览器。

即:滚动区域已经到达顶部/底部时,用户继续向上/向下滚动,便会触发IOS WebView的弹性滚动。

通常在做H5页面时,会添加这样一段代码,阻止浏览器的默认行为:

document.addEventListener('touchmove',function(e){
	e.preventDefault();
}, false);

但这样写带来的问题是页面所有touchmove事件被禁止,元素自身的滚动效果也会受到影响。对此,并没有一个很完美的解决办法,但好在知道了触发原理。于是采取一个规避的方法当我们滚动到容器顶部或底部时,向上或向下移动1个像素,阻止滚动区域到达最顶部,就能有效的禁止浏览器默认事件同时,防止影响内部滚动。

// 禁止浏览器默认事件的同时,防止影响内部滚动
var overScroll = function(el) {
	el.addEventListener('touchstart', function() {
		var top = el.scrollTop,
			totalScroll = el.scrollHeight,
			currentScroll = top + el.offsetHeight
		// 当我们滚动到容器顶部或底部时,向上或向下移动1个像素,阻止整体页面滚动。
		if (top === 0) {
			el.scrollTop = 1;
		} else if (currentScroll === totalScroll) {
			el.scrollTop = top - 1;
		}
	});
	el.addEventListener('touchmove', function(e) {
		// 内容溢出时,才可以允许滚动
		if (el.offsetHeight < el.scrollHeight);
			e._isScroller = true;
	});
};
overScroll( document.querySelector(opts.contactsContent) );
// 禁止IOS页面弹性滚动
document.addEventListener('touchmove', function(e) {
	if (!e._isScroller) {
		e.preventDefault();
	};
});

2. touchmove时判断是否在某个元素的位置上

可以使用Document.elementFromPoint()方法,来返回当前坐标下的元素节点。

document.querySelector(opts.groupNav).addEventListener('touchmove', function(e) {
	var touch 	= e.changedTouches[0],
		elem 	= document.elementFromPoint(touch.pageX, touch.pageY);
	if ( elem && elem.nodeName == "LI" ) {
		currentLocation(touch.clientX, touch.clientY);
	}
});

API文档:Document.elementFromPoint() - Web API 接口 | MDN

3. -webkit-overflow-scrolling:touch 所带来的影响

滚动区域设置-webkit-overflow-scrolling:touch时,虽然能使滚动速度变的更为流畅顺滑,但由于弹性滚动的存在,我做了个测试。当滚动区域回到顶部或底部后,继续快速拖动会导致部分.contacts-group-hd元素定位错误的情况发生。具体什么原因,目前还不清楚,所以没有使用该属性。

Live DemoGithub

参考资料:
prevent-overscroll