• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

preactjs / preact / 8402476508

23 Mar 2024 02:30PM UTC coverage: 99.469% (-0.1%) from 99.601%
8402476508

Pull #4321

github

web-flow
Merge 341c9372f into bc7c55155
Pull Request #4321: Some performance improvements

1247 of 1282 branches covered (97.27%)

5 of 5 new or added lines in 1 file covered. (100.0%)

1 existing line in 1 file now uncovered.

749 of 753 relevant lines covered (99.47%)

891.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.86
/src/diff/props.js
1
import { IS_NON_DIMENSIONAL } from '../constants';
2
import options from '../options';
3

4
function setStyle(style, key, value) {
5
        if (key[0] === '-') {
46✔
6
                style.setProperty(key, value == null ? '' : value);
12✔
7
        } else if (value == null) {
34✔
8
                style[key] = '';
9
        } else if (typeof value != 'number' || IS_NON_DIMENSIONAL.test(key)) {
73✔
10
                style[key] = value;
11
        } else {
12
                style[key] = value + 'px';
13
        }
14
}
15

16
/**
17
 * Set a property value on a DOM node
18
 * @param {PreactElement} dom The DOM node to modify
19
 * @param {string} name The name of the property to set
20
 * @param {*} value The value to set the property to
21
 * @param {*} oldValue The old value the property had
22
 * @param {boolean} isSvg Whether or not this DOM node is an SVG node or not
23
 */
24
export function setProperty(dom, name, value, oldValue, isSvg) {
25
        let useCapture;
26

27
        o: if (name === 'style') {
623✔
28
                if (typeof value == 'string') {
41✔
29
                        dom.style.cssText = value;
6✔
30
                } else {
31
                        if (typeof oldValue == 'string') {
37✔
32
                                dom.style.cssText = oldValue = '';
33
                        }
34

35
                        if (oldValue) {
35✔
36
                                for (name in oldValue) {
20✔
37
                                        if (!(value && name in value)) {
21✔
38
                                                setStyle(dom.style, name, '');
39
                                        }
40
                                }
41
                        }
42

43
                        if (value) {
35✔
44
                                for (name in value) {
34✔
45
                                        if (!oldValue || value[name] !== oldValue[name]) {
49✔
46
                                                setStyle(dom.style, name, value[name]);
47
                                        }
48
                                }
49
                        }
50
                }
51
        }
52
        // Benchmark for comparison: https://esbench.com/bench/574c954bdb965b9a00965ac6
53
        else if (name[0] === 'o' && name[1] === 'n') {
582✔
54
                useCapture =
164✔
55
                        name !== (name = name.replace(/(PointerCapture)$|Capture$/i, '$1'));
56

57
                // Infer correct casing for DOM built-in events:
58
                if (
59
                        name.toLowerCase() in dom ||
379✔
60
                        name === 'onFocusOut' ||
61
                        name === 'onFocusIn'
62
                )
63
                        name = name.toLowerCase().slice(2);
64
                else name = name.slice(2);
65

66
                if (!dom._listeners) dom._listeners = {};
273✔
67
                dom._listeners[name + useCapture] = value;
68

69
                if (value) {
164✔
70
                        if (!oldValue) {
150✔
71
                                value._attached = Date.now();
72
                                dom.addEventListener(
73
                                        name,
74
                                        useCapture ? eventProxyCapture : eventProxy,
118✔
75
                                        useCapture
76
                                );
77
                        } else {
78
                                value._attached = oldValue._attached;
79
                        }
80
                } else {
81
                        dom.removeEventListener(
82
                                name,
83
                                useCapture ? eventProxyCapture : eventProxy,
14!
84
                                useCapture
85
                        );
86
                }
87
        } else {
88
                if (isSvg) {
418✔
89
                        // Normalize incorrect prop usage for SVG:
90
                        // - xlink:href / xlinkHref --> href (xlink:href was removed from SVG and isn't needed)
91
                        // - className --> class
92
                        name = name.replace(/xlink(H|:h)/, 'h').replace(/sName$/, 's');
73✔
93
                } else if (
94
                        name != 'width' &&
345✔
95
                        name != 'height' &&
96
                        name != 'href' &&
97
                        name != 'list' &&
98
                        name != 'form' &&
99
                        // Default value in browsers is `-1` and an empty string is
100
                        // cast to `0` instead
101
                        name != 'tabIndex' &&
102
                        name != 'download' &&
103
                        name != 'rowSpan' &&
104
                        name != 'colSpan' &&
105
                        name != 'role' &&
106
                        name in dom
107
                ) {
108
                        try {
213✔
109
                                dom[name] = value == null ? '' : value;
213✔
110
                                // labelled break is 1b smaller here than a return statement (sorry)
111
                                break o;
209✔
112
                        } catch (e) {}
113
                }
114

115
                // aria- and data- attributes have no boolean representation.
116
                // A `false` value is different from the attribute not being
117
                // present, so we can't remove it. For non-boolean aria
118
                // attributes we could treat false as a removal, but the
119
                // amount of exceptions would cost too many bytes. On top of
120
                // that other frameworks generally stringify `false`.
121

122
                if (typeof value == 'function') {
209✔
123
                        // never serialize functions as attribute values
124
                } else if (value != null && (value !== false || name[4] === '-')) {
611✔
125
                        dom.setAttribute(name, value);
126
                } else {
127
                        dom.removeAttribute(name);
128
                }
129
        }
130
}
131

132
/**
133
 * Proxy an event to hooked event handlers
134
 * @param {PreactEvent} e The event object from the browser
135
 * @private
136
 */
137
function eventProxy(e) {
138
        if (this._listeners) {
62!
139
                const eventHandler = this._listeners[e.type + false];
62✔
140
                /**
141
                 * This trick is inspired by Vue https://github.com/vuejs/core/blob/main/packages/runtime-dom/src/modules/events.ts#L90-L101
142
                 * when the dom performs an event it leaves micro-ticks in between bubbling up which means that an event can trigger on a newly
143
                 * created DOM-node while the event bubbles up, this can cause quirky behavior as seen in https://github.com/preactjs/preact/issues/3927
144
                 */
145
                if (!e._dispatched) {
62!
146
                        // When an event has no _dispatched we know this is the first event-target in the chain
147
                        // so we set the initial dispatched time.
148
                        e._dispatched = Date.now();
62✔
149
                        // When the _dispatched is smaller than the time when the targetted event handler was attached
150
                        // we know we have bubbled up to an element that was added during patching the dom.
UNCOV
151
                } else if (e._dispatched <= eventHandler._attached) {
×
152
                        return;
×
153
                }
154
                return eventHandler(options.event ? options.event(e) : e);
62✔
155
        }
156
}
157

158
/**
159
 * Proxy an event to hooked event handlers
160
 * @param {PreactEvent} e The event object from the browser
161
 * @private
162
 */
163
function eventProxyCapture(e) {
164
        if (this._listeners) {
4!
165
                return this._listeners[e.type + true](options.event ? options.event(e) : e);
4✔
166
        }
167
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc