-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.html
179 lines (156 loc) · 6.7 KB
/
index.html
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
<!DOCTYPE html>
<title>reparenting</title>
<style>
button {
font-size: 20px;
}
.script-view {
background-color: lightgray;
white-space: pre;
font-family: monospace;
margin: 10px;
display: inline-block;
}
h2 {
margin-top: 30px;
padding-top: 5px;
border-bottom: 0.5px dashed lightgray;
}
h4 {
margin-bottom: 0px;
}
p {
width: 500px;
}
</style>
<h1>Examples of lost state when reparenting elements</h1>
<p>Various components of state can be lost when an element is reparented atomically in javascript. This is a common use case in React, where elements might get shuffled around within one parent element. Discussion in <a href="https://github.com/whatwg/html/issues/5484">this spec issue</a>.</p>
<h2>Reloading iframes</h2>
<p>Clicking the "reparent" button will remove and then immediately re-append the iframe in the same spot. The iframe will reload, which is undesired in this case.</p>
<script id=reparenting-iframes-script>
const iframeContainer = document.createElement('div');
const iframe = document.createElement('iframe');
iframe.src = 'https://en.wikipedia.org';
iframeContainer.appendChild(iframe);
const reparentButton = document.createElement('button');
reparentButton.textContent = 'click to reparent';
reparentButton.onclick = () => {
iframe.remove();
iframeContainer.appendChild(iframe);
};
document.body.appendChild(reparentButton);
document.body.appendChild(iframeContainer);
</script>
<div class=script-view id=reparenting-iframes-script-text></div>
<h4>Workaround</h4>
<p>Some <a href="https://github.com/whatwg/html/issues/5484#issuecomment-620481794">clever use of shadowdom</a> can be used to move an iframe around by changing which slot the iframe is inserted into. However, this is somewhat limited because it requires the part of the dom which the iframe is moving around in to be a shadowroot with slots set up for each spot where the iframe might go.</p>
<script>
document.getElementById('reparenting-iframes-script-text').textContent =
document.getElementById('reparenting-iframes-script').textContent;
</script>
<h2>Loss of selection and focus in <input>s</h2>
<p>Reparenting <input> elements causes them to lose focus and selection. Focus and select some text in the following input element and then press the enter key - the event listener will reparent the element, showing how focus and selection are lost.</p>
<script id=input-script>
const inputContainer = document.createElement('div');
document.body.appendChild(inputContainer);
const input = document.createElement('input');
input.style.width = '300px';
input.value = 'select me and press enter';
input.addEventListener('keydown', event => {
if (event.key === 'Enter') {
event.preventDefault();
input.remove();
inputContainer.appendChild(input);
}
});
inputContainer.appendChild(input);
</script>
<div class=script-view id=input-script-text></div>
<h4>Workaround</h4>
<p>For input types which support the selection API, this can be fixed by manually saving and restoring selection. However, for input types which don't support selection, such as type=number, selection cannot be restored.</p>
<script id=input-workaround-script>
const inputWorkaroundContainer = document.createElement('div');
document.body.appendChild(inputWorkaroundContainer);
const inputWorkaround = document.createElement('input');
inputWorkaround.style.width = '300px';
inputWorkaround.value = 'select me and press enter';
inputWorkaround.addEventListener('keydown', event => {
if (event.key === 'Enter') {
event.preventDefault();
const isFocused = document.activeElement === inputWorkaround;
const selectionStart = inputWorkaround.selectionStart;
const selectionEnd = inputWorkaround.selectionEnd;
inputWorkaround.remove();
inputWorkaroundContainer.appendChild(inputWorkaround);
if (isFocused) {
inputWorkaround.focus();
inputWorkaround.setSelectionRange(selectionStart, selectionEnd);
}
}
});
inputWorkaroundContainer.appendChild(inputWorkaround);
</script>
<div class=script-view id=input-workaround-script-text></div>
<script>
document.getElementById('input-script-text').textContent =
document.getElementById('input-script').textContent;
document.getElementById('input-workaround-script-text').textContent =
document.getElementById('input-workaround-script').textContent;
</script>
<h2>CSS transitions restarting</h2>
<p>Reparenting elements currently undergoing a CSS transition causes the transition to restart. Click the below element to reparent it and see what happens to the CSS transition.</p>
<style id=transitionStyle>
@keyframes slidein {
from { margin-left: 0%; }
to { margin-left: 90%; }
}
.animation {
background-color: blue;
margin: 20px;
border: 5px solid black;
width: 50px;
height: 50px;
border-radius: 50%;
animation: 3s linear infinite alternate slidein;
}
</style>
<script id=transitionScript>
const animationButton = document.createElement('button');
animationButton.textContent = 'click to reparent';
document.body.appendChild(animationButton);
const animationContainer = document.createElement('div');
document.body.appendChild(animationContainer);
const animation = document.createElement('div');
animation.classList.add('animation');
animationButton.onclick = () => {
animation.remove();
animationContainer.appendChild(animation);
}
animationContainer.appendChild(animation);
</script>
<div class=script-view id=transitionScriptText></div>
<h4>Workaround</h4>
<p>Using the new element.getAnimations() API, it is possible to get animation state before reparenting and restore it after reparenting.</p>
<script id=transitionWorkaroundScript>
const animationWorkaroundButton = document.createElement('button');
animationWorkaroundButton.textContent = 'click to reparent';
document.body.appendChild(animationWorkaroundButton);
const animationWorkaroundContainer = document.createElement('div');
document.body.appendChild(animationWorkaroundContainer);
const animationWorkaround = document.createElement('div');
animationWorkaround.classList.add('animation');
animationWorkaroundButton.onclick = () => {
const animationOffset = animationWorkaround.getAnimations()[0].currentTime;
animationWorkaround.remove();
animationWorkaroundContainer.appendChild(animationWorkaround);
animationWorkaround.getAnimations()[0].currentTime = animationOffset;
}
animationWorkaroundContainer.appendChild(animationWorkaround);
</script>
<div class=script-view id=transitionWorkaroundScriptText></div>
<script>
transitionScriptText.textContent =
transitionScript.textContent;
transitionWorkaroundScriptText.textContent =
transitionWorkaroundScript.textContent;
</script>