本文仅讨论核心代码的技术实现思路,以及从代码中看出来的一些内容,涉及到的内容基本包含以下四个文件
- rumEventCollection
- actionCollection
- getActionNameFromElement
- listenActionEvents
- trackClickAction
以下内容,方便用户了解,我做了一些整理,包括精华代码部分。
监听
事件获取的前提第一步是监听,有关click事件,比较简单,就不在本文进行讨论:
var listeners = [ addEventListener(),]
复制代码
根据监听的事件,就要考虑监听哪些事件,除了click事件,还有:
- POINTER_DOWN
- SELECTION_CHANGE
- POINTER_UP
- INPUT
其中addEventListener的入参有几项:
- eventTarget,也就是dom元素
- event,也就是事件
- listener
- options
其中代码逻辑如下,我们看到还是回归到了添加监听事件后的处理上,
export function addEventListener(eventTarget, event, listener, options) {
return addEventListeners(eventTarget, [event], listener, options)
}
复制代码
事件处理
其中有关addEventListeners,首先是非空验证isSelectionEmpty(),其次就是事件处理代码,分别是:
- onPointerDown---->processPointerDown
- userActivity.selection = true
- onPointerUp---->startClickAction
- userActivity.input = true
看到这里基本事件处理就结束了。
事件名称
接下来是事件名称的获取,这里也不讨论兼容性等,目前主要的方法是getActionNameFromElement。 这个方法主要有以下两个内容点:
- getActionNameFromElementProgrammatically
- getActionNameFromElementForStrategies
这样分的考虑在于是否能直接从元素中获取actionName:第一个是能直接从元素中获取actionName,第二个是不能直接从元素中获取actionName。
直接获取actionName
因为dom元素和嵌套的方式是千奇百怪的,所以这里需要考虑元素和元素的parent。
getActionNameFromElementProgrammatically(
element,
DEFAULT_PROGRAMMATIC_ACTION_NAME_ATTRIBUTE
) ||
(userProgrammaticAttribute &&
getActionNameFromElementProgrammatically(
element,
userProgrammaticAttribute
)) ||
getActionNameFromElementForStrategies(
element,
userProgrammaticAttribute,
priorityStrategies
) ||
getActionNameFromElementForStrategies(
element,
userProgrammaticAttribute,
fallbackStrategies
) ||
''
)
复制代码
有关元素中哪些属性是直接从element中获取的呢?基本都能数出来,所以这里就不赘述了。
不能直接获取actionName
function getActionNameFromElementForStrategies(
targetElement,
userProgrammaticAttribute,
strategies
) {
var element = targetElement
var recursionCounter = 0
while (
recursionCounter <= MAX_PARENTS_TO_CONSIDER &&
element &&
element.nodeName !== 'BODY' &&
element.nodeName !== 'HTML' &&
element.nodeName !== 'HEAD'
) {
for (var i = 0; i < strategies.length; i++) {
var strategy = strategies[i]
var name = strategy(element, userProgrammaticAttribute)
if (typeof name === 'string') {
var trimmedName = name.trim()
if (trimmedName) {
return truncate(normalizeWhitespace(trimmedName))
}
}
}
// Consider a FORM as a contextual limit to get the action name. This is experimental and may
// be reconsidered in the future.
if (element.nodeName === 'FORM') {
break
}
element = element.parentElement
recursionCounter += 1
}
}
复制代码
其中最后一个参数 策略 ,这里有两点:
- priorityStrategies
- fallbackStrategies
这里只有针对的拿出几个点来说明,非button类,可能获取哪些内容:
- alt
- name
- title
- palceholder 在上述策略中,兜底的是纯文本内容,该如何获取:
function getTextualContent(element, userProgrammaticAttribute) {
if (element.isContentEditable) {
return
}
if ('innerText' in element) {
var text = element.innerText
var removeTextFromElements = function (query) {
var list = element.querySelectorAll(query)
for (var index = 0; index < list.length; index += 1) {
var _element = list[index]
if ('innerText' in _element) {
var textToReplace = _element.innerText
if (textToReplace && textToReplace.trim().length > 0) {
text = text.replace(textToReplace, '')
}
}
}
}
if (!supportsInnerTextScriptAndStyleRemoval()) {
// remove the inner text of SCRIPT and STYLES from the result. This is a bit dirty, but should
// be relatively fast and work in most cases.
removeTextFromElements('script, style')
}
// remove the text of elements with programmatic attribute value
removeTextFromElements(
'[' + DEFAULT_PROGRAMMATIC_ACTION_NAME_ATTRIBUTE + ']'
)
if (userProgrammaticAttribute) {
removeTextFromElements('[' + userProgrammaticAttribute + ']')
}
return text
}
return element.textContent
}
复制代码
到这里基本核心代码都覆盖到了,仅仅有一些小点,比如事件报错、元素判断,因为篇幅,就不在本文介绍,不过相关代码还是可以看看的,比如
判断事件
function isValidPointerEvent(event) {
return (
event.target instanceof Element &&
// Only consider 'primary' pointer events for now. Multi-touch support could be implemented in
// the future.
event.isPrimary !== false
)
}
复制代码
获取事件名称中,循环遍历父亲节点时的终止条件:
while (
recursionCounter <= MAX_PARENTS_TO_CONSIDER &&
element &&
element.nodeName !== 'BODY' &&
element.nodeName !== 'HTML' &&
element.nodeName !== 'HEAD'
)
复制代码
当然,本文没有讲到自定义事件的收集,这部分比较简单,我把代码粘贴出来,就不做讨论了。
addAction: function (name, context) {
addActionStrategy({
name: name,
context: deepClone(context),
startClocks: clocksNow(),
type: ActionType.CUSTOM
})
},
复制代码
本文暂时没有评论,来添加一个吧(●'◡'●)