浅谈FastClick 填坑及源码解析(6)
接着咱们看看这两行很重要的代码:
this.focus(targetElement); this.sendClick(targetElement, event); //立即触发其click事件,而无须等待300ms
所涉及的两个原型方法分别为:
⑴ this.focus
FastClick.prototype.focus = function(targetElement) {
var length;
// 组件建议通过setSelectionRange(selectionStart, selectionEnd)来设定光标范围(注意这样还没有聚焦
// 要等到后面触发 sendClick 事件才会聚焦)
// 另外 iOS7 下有些input元素(比如 date datetime month) 的 selectionStart 和 selectionEnd 特性是没有整型值的,
// 导致会抛出一个关于 setSelectionRange 的模糊错误,它们需要改用 focus 事件触发
if (deviceIsIOS && targetElement.setSelectionRange && targetElement.type.indexOf('date') !== 0 && targetElement.type !== 'time' && targetElement.type !== 'month') {
length = targetElement.value.length;
targetElement.setSelectionRange(length, length);
} else {
//直接触发其focus事件
targetElement.focus();
}
};
注意,我们点击 textarea 时调用了该方法,它通过 targetElement.setSelectionRange(length, length) 决定了光标的位置在内容的尾部(但注意,这时候还没聚焦!!!)。
⑵ this.sendClick
真正让 textarea 聚焦的是这个方法,它合成了一个 click 方法立刻在textarea元素上触发导致聚焦:
//合成一个click事件并在指定元素上触发
FastClick.prototype.sendClick = function(targetElement, event) {
var clickEvent, touch;
// 在一些安卓机器中,得让页面所存在的 activeElement(聚焦的元素,比如input)失焦,否则合成的click事件将无效
if (document.activeElement && document.activeElement !== targetElement) {
document.activeElement.blur();
}
touch = event.changedTouches[0];
// 合成(Synthesise) 一个 click 事件
// 通过一个额外属性确保它能被追踪(tracked)
clickEvent = document.createEvent('MouseEvents');
clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
clickEvent.forwardedTouchEvent = true; // fastclick的内部变量,用来识别click事件是原生还是合成的
targetElement.dispatchEvent(clickEvent); //立即触发其click事件
};
FastClick.prototype.determineEventType = function(targetElement) {
//安卓设备下 Select 无法通过合成的 click 事件被展开,得改为 mousedown
if (deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select') {
return 'mousedown';
}
return 'click';
};
经过这么一折腾,咱们轻点 textarea 后,光标就自然定位到其内容尾部去了。但是这里有个问题——排在 touchend 后的 focus 事件为啥没被触发呢?
