-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
91 lines (74 loc) · 2.68 KB
/
index.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
customElements.define(
"elastic-textarea",
class extends HTMLElement {
connectedCallback() {
/**
* Find all inner textareas and use their rows attributes as a minimum count
*/
[...this.querySelectorAll("textarea")].forEach((textareaEl) => {
textareaEl.dataset.minRows = textareaEl.rows || 2;
// If textareas are prefilled they may need to be resized
this.update(textareaEl);
});
// Use event delegation to listen for textarea inputs and update the areas
this.addEventListener("input", ({ target }) => {
if (!(target instanceof HTMLTextAreaElement)) return;
this.update(target);
});
}
/**
* Determine if the element is overflowing
*/
isScrolling(textareaEl) {
return textareaEl.scrollHeight > textareaEl.clientHeight;
}
/** Grow until the textarea stops scrolling */
grow(textareaEl) {
// Store initial height of textarea
let previousHeight = textareaEl.clientHeight;
let rows = this.rows(textareaEl);
while (this.isScrolling(textareaEl)) {
rows++;
textareaEl.rows = rows;
// Get height after rows change is made
const newHeight = textareaEl.clientHeight;
// If the height hasn't changed, break the loop
// This is an important safety check in case the height is hard coded
if (newHeight === previousHeight) break;
// Store the updated height for the next comparison and proceed
previousHeight = newHeight;
}
}
/** Shrink until the textarea matches the minimum rows or starts overflowing */
shrink(textareaEl) {
// Store initial height of textarea
let previousHeight = textareaEl.clientHeight;
const minRows = parseInt(textareaEl.dataset.minRows);
let rows = this.rows(textareaEl);
while (!this.isScrolling(textareaEl) && rows > minRows) {
rows--;
textareaEl.rows = Math.max(rows, minRows);
// Get height after rows change is made
const newHeight = textareaEl.clientHeight;
// If the height hasn't changed, break the loop
// This is an important safety check in case the height is hard coded
if (newHeight === previousHeight) break;
// If we shrunk so far that we're overflowing, add a row back on.
if (this.isScrolling(textareaEl)) {
this.grow(textareaEl);
break;
}
}
}
update(textareaEl) {
if (this.isScrolling(textareaEl)) {
this.grow(textareaEl);
} else {
this.shrink(textareaEl);
}
}
rows(textareaEl) {
return textareaEl.rows || parseInt(textareaEl.dataset.minRows);
}
}
);