Skip to content

Commit 883199a

Browse files
authored
Create index.js
1 parent 8038dcd commit 883199a

File tree

1 file changed

+115
-0
lines changed

1 file changed

+115
-0
lines changed

index.js

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
let browserSupportsTextareaTextNodes;
2+
3+
/**
4+
* @param {HTMLElement} input
5+
* @return {boolean}
6+
*/
7+
function canManipulateViaTextNodes(input) {
8+
if (input.nodeName !== "TEXTAREA") {
9+
return false;
10+
}
11+
if (typeof browserSupportsTextareaTextNodes === "undefined") {
12+
const textarea = document.createElement("textarea");
13+
textarea.value = 1;
14+
browserSupportsTextareaTextNodes = !!textarea.firstChild;
15+
}
16+
return browserSupportsTextareaTextNodes;
17+
}
18+
19+
/**
20+
* @param {HTMLTextAreaElement|HTMLInputElement} input
21+
* @param {string} text
22+
* @returns {void}
23+
*/
24+
export default function(input, text) {
25+
// Most of the used APIs only work with the field selected
26+
input.focus();
27+
28+
// IE 8-10
29+
if (document.selection) {
30+
const ieRange = document.selection.createRange();
31+
ieRange.text = text;
32+
33+
// Move cursor after the inserted text
34+
ieRange.collapse(false /* to the end */);
35+
ieRange.select();
36+
37+
return;
38+
}
39+
40+
// Webkit + Edge
41+
const isSuccess = document.execCommand("insertText", false, text);
42+
if (!isSuccess) {
43+
const start = input.selectionStart;
44+
const end = input.selectionEnd;
45+
// Firefox (non-standard method)
46+
if (typeof input.setRangeText === "function") {
47+
input.setRangeText(text);
48+
} else {
49+
// To make a change we just need a Range, not a Selection
50+
const range = document.createRange();
51+
const textNode = document.createTextNode(text);
52+
53+
if (canManipulateViaTextNodes(input)) {
54+
let node = input.firstChild;
55+
56+
// If textarea is empty, just insert the text
57+
if (!node) {
58+
input.appendChild(textNode);
59+
} else {
60+
// Otherwise we need to find a nodes for start and end
61+
let offset = 0;
62+
let startNode = null;
63+
let endNode = null;
64+
65+
while (node && (startNode === null || endNode === null)) {
66+
const nodeLength = node.nodeValue.length;
67+
68+
// if start of the selection falls into current node
69+
if (start >= offset && start <= offset + nodeLength) {
70+
range.setStart((startNode = node), start - offset);
71+
}
72+
73+
// if end of the selection falls into current node
74+
if (end >= offset && end <= offset + nodeLength) {
75+
range.setEnd((endNode = node), end - offset);
76+
}
77+
78+
offset += nodeLength;
79+
node = node.nextSibling;
80+
}
81+
82+
// If there is some text selected, remove it as we should replace it
83+
if (start !== end) {
84+
range.deleteContents();
85+
}
86+
}
87+
}
88+
89+
// If the node is a textarea and the range doesn't span outside the element
90+
//
91+
// Get the commonAncestorContainer of the selected range and test its type
92+
// If the node is of type `#text` it means that we're still working with text nodes within our textarea element
93+
// otherwise, if it's of type `#document` for example it means our selection spans outside the textarea.
94+
if (
95+
canManipulateViaTextNodes(input) &&
96+
range.commonAncestorContainer.nodeName === "#text"
97+
) {
98+
// Finally insert a new node. The browser will automatically split start and end nodes into two if necessary
99+
range.insertNode(textNode);
100+
} else {
101+
// If the node is not a textarea or the range spans outside a textarea the only way is to replace the whole value
102+
const value = input.value;
103+
input.value = value.slice(0, start) + text + value.slice(end);
104+
}
105+
}
106+
107+
// Correct the cursor position to be at the end of the insertion
108+
input.setSelectionRange(start + text.length, start + text.length);
109+
110+
// Notify any possible listeners of the change
111+
const e = document.createEvent("UIEvent");
112+
e.initEvent("input", true, false);
113+
input.dispatchEvent(e);
114+
}
115+
}

0 commit comments

Comments
 (0)