@@ -10,7 +10,7 @@ import { getAttributesToRecord } from './util/getAttributesToRecord';
10
10
11
11
export interface DomHandlerData {
12
12
name : string ;
13
- event : Node | { target : Node } ;
13
+ event : Node | { target : EventTarget } ;
14
14
}
15
15
16
16
export const handleDomListener : ( replay : ReplayContainer ) => ( handlerData : DomHandlerData ) => void =
@@ -29,39 +29,21 @@ export const handleDomListener: (replay: ReplayContainer) => (handlerData: DomHa
29
29
addBreadcrumbEvent ( replay , result ) ;
30
30
} ;
31
31
32
- /**
33
- * An event handler to react to DOM events.
34
- * Exported for tests only.
35
- */
36
- export function handleDom ( handlerData : DomHandlerData ) : Breadcrumb | null {
37
- let target ;
38
- let targetNode : Node | INode | undefined ;
39
-
40
- const isClick = handlerData . name === 'click' ;
41
-
42
- // Accessing event.target can throw (see getsentry/raven-js#838, #768)
43
- try {
44
- targetNode = isClick ? getClickTargetNode ( handlerData . event ) : getTargetNode ( handlerData . event ) ;
45
- target = htmlTreeAsString ( targetNode , { maxStringLength : 200 } ) ;
46
- } catch ( e ) {
47
- target = '<unknown>' ;
48
- }
49
-
32
+ /** Get the base DOM breadcrumb. */
33
+ export function getBaseDomBreadcrumb ( target : Node | INode | null , message : string ) : Breadcrumb {
50
34
// `__sn` property is the serialized node created by rrweb
51
- const serializedNode =
52
- targetNode && '__sn' in targetNode && targetNode . __sn . type === NodeType . Element ? targetNode . __sn : null ;
35
+ const serializedNode = target && isRrwebNode ( target ) && target . __sn . type === NodeType . Element ? target . __sn : null ;
53
36
54
- return createBreadcrumb ( {
55
- category : `ui.${ handlerData . name } ` ,
56
- message : target ,
37
+ return {
38
+ message,
57
39
data : serializedNode
58
40
? {
59
41
nodeId : serializedNode . id ,
60
42
node : {
61
43
id : serializedNode . id ,
62
44
tagName : serializedNode . tagName ,
63
- textContent : targetNode
64
- ? Array . from ( targetNode . childNodes )
45
+ textContent : target
46
+ ? Array . from ( target . childNodes )
65
47
. map (
66
48
( node : Node | INode ) => '__sn' in node && node . __sn . type === NodeType . Text && node . __sn . textContent ,
67
49
)
@@ -73,12 +55,46 @@ export function handleDom(handlerData: DomHandlerData): Breadcrumb | null {
73
55
} ,
74
56
}
75
57
: { } ,
58
+ } ;
59
+ }
60
+
61
+ /**
62
+ * An event handler to react to DOM events.
63
+ * Exported for tests.
64
+ */
65
+ export function handleDom ( handlerData : DomHandlerData ) : Breadcrumb | null {
66
+ const { target, message } = getDomTarget ( handlerData ) ;
67
+
68
+ return createBreadcrumb ( {
69
+ category : `ui.${ handlerData . name } ` ,
70
+ ...getBaseDomBreadcrumb ( target , message ) ,
76
71
} ) ;
77
72
}
78
73
79
- function getTargetNode ( event : DomHandlerData [ 'event' ] ) : Node {
74
+ function getDomTarget ( handlerData : DomHandlerData ) : { target : Node | INode | null ; message : string } {
75
+ const isClick = handlerData . name === 'click' ;
76
+
77
+ let message : string | undefined ;
78
+ let target : Node | INode | null = null ;
79
+
80
+ // Accessing event.target can throw (see getsentry/raven-js#838, #768)
81
+ try {
82
+ target = isClick ? getClickTargetNode ( handlerData . event ) : getTargetNode ( handlerData . event ) ;
83
+ message = htmlTreeAsString ( target , { maxStringLength : 200 } ) || '<unknown>' ;
84
+ } catch ( e ) {
85
+ message = '<unknown>' ;
86
+ }
87
+
88
+ return { target, message } ;
89
+ }
90
+
91
+ function isRrwebNode ( node : EventTarget ) : node is INode {
92
+ return '__sn' in node ;
93
+ }
94
+
95
+ function getTargetNode ( event : Node | { target : EventTarget | null } ) : Node | INode | null {
80
96
if ( isEventWithTarget ( event ) ) {
81
- return event . target ;
97
+ return event . target as Node | null ;
82
98
}
83
99
84
100
return event ;
@@ -90,7 +106,7 @@ const INTERACTIVE_SELECTOR = 'button,a';
90
106
// If so, we use this as the target instead
91
107
// This is useful because if you click on the image in <button><img></button>,
92
108
// The target will be the image, not the button, which we don't want here
93
- function getClickTargetNode ( event : DomHandlerData [ 'event' ] ) : Node {
109
+ function getClickTargetNode ( event : DomHandlerData [ 'event' ] ) : Node | INode | null {
94
110
const target = getTargetNode ( event ) ;
95
111
96
112
if ( ! target || ! ( target instanceof Element ) ) {
@@ -101,6 +117,6 @@ function getClickTargetNode(event: DomHandlerData['event']): Node {
101
117
return closestInteractive || target ;
102
118
}
103
119
104
- function isEventWithTarget ( event : unknown ) : event is { target : Node } {
105
- return ! ! ( event as { target ?: Node } ) . target ;
120
+ function isEventWithTarget ( event : unknown ) : event is { target : EventTarget | null } {
121
+ return typeof event === 'object' && ! ! event && ' target' in event ;
106
122
}
0 commit comments