程序员的知识教程库

网站首页 > 教程分享 正文

可观测性系统中对用户行为如何记录的一些简单介绍

henian88 2024-09-06 18:41:19 教程分享 73 ℃ 0 评论

本文仅讨论核心代码的技术实现思路,以及从代码中看出来的一些内容,涉及到的内容基本包含以下四个文件

  • 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
      })
    },
复制代码

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表