I have a textarea and would like to return the most recent change to the text for use in avia JavaScript function. That would include:
For example:
I'm still new to this and have no real idea how to call that added/removed text for use in a JavaScript function.
Reading about InputEvent
, InputEvent.inputType
and InputEvent.data
might be a good starting point. In combination with selectionStart
/ selectionEnd
as with e.g. HTMLInputElement
s and/or HTMLTextAreaElement
s one pretty much could cover/solve the OP's task.
A possible approach which detects (text) changes within the input data/value might ...
utilize a WeakMap
instance as textarea and/or input-element based storage for such an element's most recent (text) value.
enable the exact detection of data/value-changes by keeping track of such an element's 'input'
events, based on the most recent and the current (text) values as well as on selectionEnd
and sometimes on the input event's data
value.
In case of having verified an input-event based text-value change a custom event is created and dispatched. Thus one now can directly listen to and handle textarea and/or input-element related 'input:datachange'
events.
On top of the new custom 'input:datachange'
event type, with all the additional information one does neither receive by 'input'
nor by 'change'
events, the OP now will be able to directly access the changed value whether deleted and/or inserted (pasted or typed or restored for the latter).
// the node reference based storage of most recent element values.
const mostRecentValueStorage = new WeakMap;
function getDataChangeFromDeleteOrPaste({ currentTarget, data }) {
const recentValue = mostRecentValueStorage.get(currentTarget);
const { value: currentValue, selectionEnd } = currentTarget;
let deletionStart = selectionEnd;
let leadingValue = currentValue.slice(0, deletionStart);
while ((leadingValue !== '') && !recentValue.startsWith(leadingValue)) {
leadingValue = leadingValue.slice(0, --deletionStart);
}
const deletionLength =
(selectionEnd - deletionStart) + (recentValue.length - currentValue.length);
const deleted = recentValue.slice(deletionStart, (deletionStart + deletionLength));
const insertLength = currentValue.length + deleted.length - recentValue.length;
const insertStart = selectionEnd - insertLength;
const inserted = (insertStart < selectionEnd)
&& currentValue.slice(insertStart, selectionEnd)
|| null;
return {
currentValue,
recentValue,
deleted: (deleted === '') ? null : deleted,
deletionStart: (deleted === '') ? null : deletionStart,
deletionLength: (deleted === '') ? null : deletionLength,
inserted,
insertStart: (inserted === null) ? null : insertStart,
insertLength: (inserted === null) ? null : insertLength,
};
}
function getDataChangeFromInsertText({ currentTarget, data }) {
const recentValue = mostRecentValueStorage.get(currentTarget);
const { value: currentValue, selectionEnd } = currentTarget;
const insertLength = data.length;
const deletionStart = (selectionEnd - insertLength);
const deletionLength = (recentValue.length - currentValue.length + insertLength);
const deleted = recentValue.slice(deletionStart, (deletionStart + deletionLength));
return {
currentValue,
recentValue,
deleted: (deleted === '') ? null : deleted,
deletionStart: (deleted === '') ? null : deletionStart,
deletionLength: (deleted === '') ? null : deletionLength,
inserted: data,
insertStart: (selectionEnd - insertLength),
insertLength,
};
}
function handleCustomInputDataChange(evt) {
const { currentTarget } = evt;
if (currentTarget.value !== mostRecentValueStorage.get(currentTarget)) {
const { data } = evt;
const dataChange = (
(typeof data === 'string') && getDataChangeFromInsertText(evt)
) || (
(data === null) && getDataChangeFromDeleteOrPaste(evt)
) || null;
// put the most recent element value into an object based storage.
mostRecentValueStorage.set(currentTarget, currentTarget.value);
currentTarget
.dispatchEvent(
new CustomEvent('input:datachange', {
bubbles: true,
detail: {
dataChange,
inputEvent: evt,
},
})
);
}
}
function enableCustomInputDataChangeHandling(elmNode) {
// put the initial element value into an object based storage.
mostRecentValueStorage.set(elmNode, elmNode.defaultValue);
elmNode
.addEventListener('input', handleCustomInputDataChange);
}
function initializeCustomInputDataChangeHandling() {
document
// for every element which features a
// `data-handle-input-data-change` attribute ...
.querySelectorAll('[data-handle-input-data-change]')
// ... enable the handling of a custom
// 'input:datachange' event.
.forEach(enableCustomInputDataChangeHandling);
}
function main() {
initializeCustomInputDataChangeHandling();
document
// subscribe to the custom 'input:datachange' event wherever it is needed.
.addEventListener(
'input:datachange',
({ target, detail: { dataChange } }) => console.log({ /*target, */dataChange })
);
}
main();
body { margin: 0; }
.as-console-wrapper { left: auto!important; width: 76%; min-height: 100%!important; }
<textarea
data-handle-input-data-change
cols="16"
rows="12"
>The quick brown fox jumps over the lazy dog ... edit text in whichever way.</textarea>