/
props.js
126 lines (114 loc) · 3.49 KB
/
props.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import { IS_NON_DIMENSIONAL } from '../constants';
import options from '../options';
/**
* Diff the old and new properties of a VNode and apply changes to the DOM node
* @param {import('../internal').PreactElement} dom The DOM node to apply
* changes to
* @param {object} newProps The new props
* @param {object} oldProps The old props
* @param {boolean} isSvg Whether or not this node is an SVG node
* @param {boolean} hydrate Whether or not we are in hydration mode
*/
export function diffProps(dom, newProps, oldProps, isSvg, hydrate) {
let i;
for (i in oldProps) {
if (!(i in newProps)) {
setProperty(dom, i, null, oldProps[i], isSvg);
}
}
for (i in newProps) {
if ((!hydrate || typeof newProps[i]=='function') && i!=='value' && i!=='checked' && oldProps[i]!==newProps[i]) {
setProperty(dom, i, newProps[i], oldProps[i], isSvg);
}
}
}
function setStyle(style, key, value) {
if (key[0] === '-') {
style.setProperty(key, value);
}
else {
style[key] = typeof value==='number' && IS_NON_DIMENSIONAL.test(key)===false ? value+'px' : value==null ? '' : value;
}
}
/**
* Set a property value on a DOM node
* @param {import('../internal').PreactElement} dom The DOM node to modify
* @param {string} name The name of the property to set
* @param {*} value The value to set the property to
* @param {*} oldValue The old value the property had
* @param {boolean} isSvg Whether or not this DOM node is an SVG node or not
*/
function setProperty(dom, name, value, oldValue, isSvg) {
name = isSvg ? (name==='className' ? 'class' : name) : (name==='class' ? 'className' : name);
if (name==='key' || name === 'children') {}
else if (name==='style') {
const s = dom.style;
if (typeof value==='string') {
s.cssText = value;
}
else {
if (typeof oldValue==='string') {
s.cssText = '';
oldValue = null;
}
if (oldValue) for (let i in oldValue) {
if (!(value && i in value)) {
setStyle(s, i, '');
}
}
if (value) for (let i in value) {
if (!oldValue || value[i] !== oldValue[i]) {
setStyle(s, i, value[i]);
}
}
}
}
// Benchmark for comparison: https://esbench.com/bench/574c954bdb965b9a00965ac6
else if (name[0]==='o' && name[1]==='n') {
let useCapture = name !== (name=name.replace(/Capture$/, ''));
let nameLower = name.toLowerCase();
name = (nameLower in dom ? nameLower : name).slice(2);
if (value) {
if (!oldValue) dom.addEventListener(name, eventProxy, useCapture);
(dom._listeners || (dom._listeners = {}))[name] = value;
}
else {
dom.removeEventListener(name, eventProxy, useCapture);
}
}
else if (
name!=='list'
&& name!=='tagName'
// HTMLButtonElement.form and HTMLInputElement.form are read-only but can be set using
// setAttribute
&& name!=='form'
&& !isSvg
&& (name in dom)
) {
dom[name] = value==null ? '' : value;
}
else if (typeof value!=='function' && name!=='dangerouslySetInnerHTML') {
if (name!==(name = name.replace(/^xlink:?/, ''))) {
if (value==null || value===false) {
dom.removeAttributeNS('http://www.w3.org/1999/xlink', name.toLowerCase());
}
else {
dom.setAttributeNS('http://www.w3.org/1999/xlink', name.toLowerCase(), value);
}
}
else if (value==null || value===false) {
dom.removeAttribute(name);
}
else {
dom.setAttribute(name, value);
}
}
}
/**
* Proxy an event to hooked event handlers
* @param {Event} e The event object from the browser
* @private
*/
function eventProxy(e) {
return this._listeners[e.type](options.event ? options.event(e) : e);
}