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

preactjs / preact / 8402438009

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

Pull #4321

github

web-flow
Merge 1c83404f1 into bc7c55155
Pull Request #4321: Some performance improvements/byte-saving

1238 of 1273 branches covered (97.25%)

4 of 4 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%)

892.59 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
const reserved = [
17
        'width',
18
        'height',
19
        'href',
20
        'list',
21
        'form',
22
        // Default value in browsers is `-1` and an empty string is
23
        // cast to `0` instead
24
        'tabIndex',
25
        'download',
26
        'rowSpan',
27
        'colSpan',
28
        'role'
29
];
30

31
/**
32
 * Set a property value on a DOM node
33
 * @param {PreactElement} dom The DOM node to modify
34
 * @param {string} name The name of the property to set
35
 * @param {*} value The value to set the property to
36
 * @param {*} oldValue The old value the property had
37
 * @param {boolean} isSvg Whether or not this DOM node is an SVG node or not
38
 */
39
export function setProperty(dom, name, value, oldValue, isSvg) {
40
        let useCapture;
41

42
        o: if (name === 'style') {
623✔
43
                if (typeof value == 'string') {
41✔
44
                        dom.style.cssText = value;
6✔
45
                } else {
46
                        if (typeof oldValue == 'string') {
37✔
47
                                dom.style.cssText = oldValue = '';
48
                        }
49

50
                        if (oldValue) {
35✔
51
                                for (name in oldValue) {
20✔
52
                                        if (!(value && name in value)) {
21✔
53
                                                setStyle(dom.style, name, '');
54
                                        }
55
                                }
56
                        }
57

58
                        if (value) {
35✔
59
                                for (name in value) {
34✔
60
                                        if (!oldValue || value[name] !== oldValue[name]) {
49✔
61
                                                setStyle(dom.style, name, value[name]);
62
                                        }
63
                                }
64
                        }
65
                }
66
        }
67
        // Benchmark for comparison: https://esbench.com/bench/574c954bdb965b9a00965ac6
68
        else if (name[0] === 'o' && name[1] === 'n') {
582✔
69
                useCapture =
164✔
70
                        name !== (name = name.replace(/(PointerCapture)$|Capture$/i, '$1'));
71

72
                // Infer correct casing for DOM built-in events:
73
                if (
74
                        name.toLowerCase() in dom ||
379✔
75
                        name === 'onFocusOut' ||
76
                        name === 'onFocusIn'
77
                )
78
                        name = name.toLowerCase().slice(2);
79
                else name = name.slice(2);
80

81
                if (!dom._listeners) dom._listeners = {};
273✔
82
                dom._listeners[name + useCapture] = value;
83

84
                if (value) {
164✔
85
                        if (!oldValue) {
150✔
86
                                value._attached = Date.now();
87
                                dom.addEventListener(
88
                                        name,
89
                                        useCapture ? eventProxyCapture : eventProxy,
118✔
90
                                        useCapture
91
                                );
92
                        } else {
93
                                value._attached = oldValue._attached;
94
                        }
95
                } else {
96
                        dom.removeEventListener(
97
                                name,
98
                                useCapture ? eventProxyCapture : eventProxy,
14!
99
                                useCapture
100
                        );
101
                }
102
        } else {
103
                if (isSvg) {
418✔
104
                        // Normalize incorrect prop usage for SVG:
105
                        // - xlink:href / xlinkHref --> href (xlink:href was removed from SVG and isn't needed)
106
                        // - className --> class
107
                        name = name.replace(/xlink(H|:h)/, 'h').replace(/sName$/, 's');
73✔
108
                } else if (!reserved.includes(name) && name in dom) {
345✔
109
                        try {
213✔
110
                                dom[name] = value == null ? '' : value;
213✔
111
                                // labelled break is 1b smaller here than a return statement (sorry)
112
                                break o;
209✔
113
                        } catch (e) {}
114
                }
115

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

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

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

159
/**
160
 * Proxy an event to hooked event handlers
161
 * @param {PreactEvent} e The event object from the browser
162
 * @private
163
 */
164
function eventProxyCapture(e) {
165
        if (this._listeners) {
4!
166
                return this._listeners[e.type + true](options.event ? options.event(e) : e);
4✔
167
        }
168
}
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

© 2025 Coveralls, Inc