From f682a106798c4b5b791fb2c8635d310f75cf1fda Mon Sep 17 00:00:00 2001 From: Dmytro Stanchiev Date: Sun, 26 Jan 2025 06:24:43 -0500 Subject: [PATCH 1/2] fix: properly handle synthetic input events --- src/content_script/input_tools.js | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/content_script/input_tools.js b/src/content_script/input_tools.js index 6febff7..7194f1d 100644 --- a/src/content_script/input_tools.js +++ b/src/content_script/input_tools.js @@ -164,6 +164,32 @@ if (!window._hasExecutedSlExtension) { updatePosition(); }, + setInputValue(input, value) { + // Accessing the original setter in the input element's prototype \ + // to update the value, thus bypassing React's getters/setters. + const prototype = Object.getPrototypeOf(input); + const descriptor = Object.getOwnPropertyDescriptor( + prototype, + "value" + ); + const valueSetter = descriptor ? descriptor.set : null; + + if (valueSetter) { + valueSetter.call(input, value); + } else { + // Fallback for environments where the setter is not found + input.value = value; + } + + // Dispatch a bubbling 'input' event to trigger React's event system. + // Also can help other frameworks, or even Vanilla JS to register + // other event handlers, such as 'onchange' and/or 'oninput'. + // They might help to get an intended behaviour out of an input, such + // as input validation, etc. + const event = new Event("input", { bubbles: true }); + input.dispatchEvent(event); + }, + async handleOnClickSLButton(inputElem, slButton) { if (InputTools.isLoading) { return; @@ -182,7 +208,7 @@ if (!window._hasExecutedSlExtension) { InputTools.isLoading = false; slButton.classList.remove("loading"); - inputElem.value = res.alias; + InputTools.setInputValue(inputElem, res.alias); }, sumPixel(dimensions) { From 71e535cc9ce46e34416bac25272f36ee02a65323 Mon Sep 17 00:00:00 2001 From: Dmytro Stanchiev Date: Mon, 27 Jan 2025 02:56:00 -0500 Subject: [PATCH 2/2] emulate user behavior with other events on input change more on why is in the inline comments. tldr; for intended UX, i.e. input validation --- src/content_script/input_tools.js | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/content_script/input_tools.js b/src/content_script/input_tools.js index 7194f1d..5cd7b87 100644 --- a/src/content_script/input_tools.js +++ b/src/content_script/input_tools.js @@ -174,6 +174,10 @@ if (!window._hasExecutedSlExtension) { ); const valueSetter = descriptor ? descriptor.set : null; + // More on why we're dispatching an event at the end of the function definition. + const focusEvent = new Event("focus", { bubbles: true }); + input.dispatchEvent(focusEvent); + if (valueSetter) { valueSetter.call(input, value); } else { @@ -181,13 +185,19 @@ if (!window._hasExecutedSlExtension) { input.value = value; } - // Dispatch a bubbling 'input' event to trigger React's event system. - // Also can help other frameworks, or even Vanilla JS to register - // other event handlers, such as 'onchange' and/or 'oninput'. - // They might help to get an intended behaviour out of an input, such - // as input validation, etc. - const event = new Event("input", { bubbles: true }); - input.dispatchEvent(event); + // Define and dispatch a bubbling 'input' event to trigger React's event system. + const inputEvent = new Event("input", { bubbles: true }); + input.dispatchEvent(inputEvent); + // 'input' and 'change' events are interchangable to React's event system when \ + // it comes to updating the value. Since we're already dispatching an event, we \ + // might as well trigger 'change' for any other case when events are handled by \ + // a standard DOM (i.e Vanilla JS/other frameworks) and have different functionality. + const changeEvent = new Event("change", { bubbles: true }); + input.dispatchEvent(changeEvent); + // We can also dispatch 'blur' event to emulate natural user behaviour and intended UX. + // Simply changing the input.value doesn't trigget any events. + const blurEvent = new Event("blur", { bubbles: true }); + input.dispatchEvent(blurEvent); }, async handleOnClickSLButton(inputElem, slButton) {