Can Firefox use document.execCommand in a textarea? - javascript

With the cursor in a contenteditable div, both Chrome and Firefox can emulate typing "sometext" like this:
document.execCommand('insertText', false, 'sometext');
In Chrome, this works when you're in a textarea as well. In Firefox, I get the error "NS_ERROR_FAILURE:".
Here's a fiddle to demonstrate: http://jsfiddle.net/ukx37/. In Chrome, hit enter and you type "ENTER\n". In Firefox, you type "\n" and get an error NS_ERROR_FAILURE.
Does anybody know if there's a way to get this working in Firefox? Or, if not, is there some way I can test for support without a try-catch statement?
Also, I don't want to manually edit the textarea's value because doing so breaks the edit history.

Figured out that the error is being fired because the focused Node isn't contentEditable. If you make the textarea contentEditable it stops firing errors, but gets all buggy. Firefox will sometimes put the inserted text in the textarea, sometimes put it in the DOM as a child node of the textarea (and never display it), sometimes do nothing. No errors are fired, but it's still unusable. Same thing for making a parent contentEditable and the textarea not.
The answer I'm using for now is feature-detecting and giving up if it doesn't "just work". If somebody gets Firefox to work I'll un-accept this and accept theirs. Until then, here's the code I'm using.
var canEditInput = (function () {
try {
var t = document.createElement('textarea');
document.body.appendChild(t);
t.focus();
document.execCommand('insertText', false, 'x');
document.body.removeChild(t);
return t.value === 'x';
} catch (e) {
return false;
}
})();
Note that we can't give t display:none; because then it can't be focused. But it shouldn't matter, because the JS should finish (and remove t) before the browser starts to draw the next frame.

Related

How to perform programmatic text input that supports both undo and redo?

My current Chrome extension is supposed to programmatically insert text on user input. This works great, however, it mangles the undo/redo behavior of all text editors I've tried it on so far. So, the following routine:
type some text
insert some text programmatically
type more text
press ctrl-z thrice
does not always revert to a blank state. Very often it will get stuck somewhere in the middle. Its behaviour is mostly inconsistent.
Here's an MCVE of the content script:
function init() {
var $input = document.getElementsByTagName("textarea")[0];
if (!$input) {
console.log("No input element found.");
return true;
}
var $btn = document.createElement("button");
$btn.innerHTML = "Click";
$btn.addEventListener("click", function() {
var caretPos = $input.selectionStart;
$input.value = $input.value.substring(0, caretPos) + " test string " + $input.value.substring(caretPos);
});
$input.parentElement.insertBefore($btn, $input);
return true;
}
window.addEventListener("load", init);
<div><textarea></textarea></div>
(I also bundled it as a Chrome extension in case you'd like that.)
I want the undo/redo to function perfectly in both textarea as well as contenteditable nodes. I also tried document.execCommand in both insertText and insertHTML modes without any success. I've looked at the other two related questions but they do not answer my query. (q1, q2)
What else can be a possible solution to this problem?
Thanks to the helpful comments above, my problem was doing execCommand on pre-programmed weird editors like Facebook messenger box, tinymce, etc. They might have their own customizations interfering.
In normal text editors, document.execCommand should work fine, and support undo/redo. Use it like so:
document.execCommand('insertText', false, "text");
document.execCommand('insertHTML', false, "html");

Preventing textarea scroll behaviour in chrome after newline added

Recently my version of chrome has been doing something strange (74.0.3729.131 on ubuntu 18.04) more and more often. I have a small editor script which has a textarea which displays code. The textarea has a fixed size and a vertical scroll bar. Beyond that nothing fancy.
Usually, when I insert a newline (normal behaviour of textarea), the scroll bar doesn't move. Now for some reason about 80% of the times it scrolls the textarea down till the position of the caret is at the top of the textarea. Strangely if I delete and enter the newline in the same position, it usually does not scroll.
I'm not sure if this is some new issue in Chrome. I usen't have this issue with previous versions with the identical editor.
Here is a codepen which demonstrates the issue, scroll to some line, press enter and the textarea should scroll down. Try this a few times to see the unpredictable behaviour (adding the code just to be able to add the link, as you can see it's just a textarea).
https://codepen.io/anon/pen/rgKqMb
<textarea style="width:90%;height:300px"></textarea>
The only solution that occurs to me to avoid this is to stop the normal behaviour of the enter key and add the newline to the text. Any other ideas/insights very much welcome.
It's almost the end of 2020, Chrome version 86 and this issue still exists? What's more, I am surprised I have not found more information (complaints) on this matter (this post is the only thing I've found which speaks of this issue specifically.) I have observed that this behavior occurs not only in typing, but pasting any text containing a newline. I also observed that if I execute an undo action after this occurs, another random scroll happens, taking me even farther up the page, and nowhere near where the caret is.
I experimented and examined this behavior at much length, and was not able to find any repeatable circumstances which might give a clue as to how to predict when this would occur. It truly just seems "random". Nonetheless, I've had to work around this issue for an NWJS editor app I'm creating (NWJS uses Chrome for UI.)
This is what seems to be working for me:
First all, let me start simple in order to introduce the principle. We attach an "input" listener and "scroll" listener to the textarea. This works because, from my observation anyway, the "input"[1] listener gets fired before the random scroll action occurs.
The scroll listener records each scrolling action and saves it in a global prevScrollPos. It also checks for a global flag scrollCorrection.
The "input" listener sets the scrollCorrection flag everytime text is input into the textarea. Remember, this has happened before the random scroll occurs.
So the next scroll to occur, which may be the nuisance random action, the scroll listener will clear scrollCorrection, then scroll the textarea to the previous scroll position, ie, scrolling it back to where it was before the "random" scroll. But the issue is unpredictable, what if there is no random scroll and the next scroll to occur is intentional? That is not a big deal. It just means that if the user scrolls manually, the first scroll event is basically nullified, but then after that (with scrollCorrection cleared) everything will scroll normally. Since during normal scrolling, events are spit out so rapidly, it is unlikely there will be any noticeable effect.
Here is the code:
let textarea;
let prevScrollPos = 0;
let scrollCorrection = false;
function onScroll(evt) {
if (scrollCorrection) {
// Reset this right off so it doesn't get retriggered by the corrction.
scrollCorrection = false;
textarea.scrollTop = prevScrollPos;
}
prevScrollPos = textarea.scrollTop;
}
function onInput(evt) {
scrollCorrection = true;
}
window.addEventListener("load", () => {
textarea = document.getElementById("example_textarea");
textarea.addEventListener("scroll", onScroll);
textarea.addEventListener("input", onInput);
})
Now let's expand on it:
There is another consideration. What if the typing or pasting action puts the end of the typed or pasted text (and thus the caret) outside the view of the textarea viewport? When normal scrolling is in play, most browsers will scroll the page[2] so the caret will remain in view. However now that we've taken over scrolling action, we'll need to implement that ourselves.
In the psuedo-code below, on input to the textarea, besides setting scrollCorrection, we call a function which will:
determine the xy position of caret relative to textarea viewport
determine if it is scrolled out of view
if so:
determine the amount to scroll to bring it in view
determine if the random scroll has already occurred by testing the state of scrollCorrection
if it hasn't, set flag scrollCorrection2 containing the amount to scroll
if it has, explicitly do the additional scrolling to bring it back into view
Finding the xy position of the caret in a textarea is not a trivial matter and is outside the scope of this answer, but there are plenty of methods to be found in searching the web. Most involve replicating the textarea contents in a non-form element, eg div block, with similar font, font-size, text wrapping etc, then using getBoundingClientRect on the resultant containing block and such. In my situation, I was already doing most of this for my editor, so it wasn't much of an additional expense. But I've included some psuedo-code to show how this can be implemented in the scroll correction mechanism. setCaretCorrection basically does steps 1 - 7 above.
let textarea;
let prevScrollPos = 0;
let scrollCorrection = false;
let caretCorrection = 0;
function onScroll(evt) {
if (scrollCorrection) {
// Reset this right off so it doesn't get retriggered by the correction.
scrollCorrection = false;
textarea.scrollTop = prevScrollPos + caretCorrection;
caretCorrection = 0;
}
prevScrollPos = textarea.scrollTop;
}
function onTextareaInput() {
scrollCorrection = true;
setCaretCorrection();
}
function setCaretCorrection(evt) {
let caretPos = textarea.selectionStart;
let scrollingNeeded;
let amountToScroll;
/* ... Some code to determine xy position of caret relative to
textarea viewport, if it is scrolled out of view, and if
so, how much to scroll to bring it in view. ... */
if (scrollingNeeded) {
if (scrollCorrection) {
// scrollCorrection is true meaning random scroll has not occurred yet,
// so flag the scroll listener to add additional correction. This method
// won't cause a flicker which could happen if we scrollBy() explicitly.
caretCorrection = amountToScroll;
} else {
// Random scroll has already occurred and been corrected, so we are
// forced to do the additional "out of viewport" correction explicitly.
// Note, in my situation I never saw this condition happen.
textarea.scrollBy(0, amountToScroll);
}
}
}
One could go further and use the experimental event, "beforeinput"[3], to optimize this a little bit so fewer unnecessary calls to setCaretCorrection are made. If one examines event.data from "beforeinput" event, in certain cases it will report the data to be input. If it does not, then it outputs null. Unfortunately, when a newline is typed, event.data is null. However it will report newlines if they are pasted. So at least one can see if event.data contains a string, and if the string does not contain newlines, skip the whole correction action. (Also, see [1] below.)
[1] I also don't see any reason you couldn't do in the "beforeinput"[3] listener, what what we're doing in the "input" listener. That may also give more insurance that we set scrollCorrection before the random scroll occurs. Although note that "beforeinput" is experimental.
[2] I suspect it is broken implementation of this feature which is causing this issue.
[3] https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/beforeinput_event (Available on Chrome and all major browsers except Firefox according to this link.)
You can try avoiding the events on the textarea with css and js, then force the scroll to it's current position:
css:
textarea {
overflow:auto;
resize:none;
width:90%;
height:300px;
}
js:
You'll need to insert the first answer from this question at A
function preventMoving(e) {
var key = (e.keyCode ? e.keyCode : e.which);
if(key == 13) {
e.preventDefault();
// A
}
}
Then on your HTML:
<textarea onkeyup="preventMoving(event);"></textarea>

Looking for ibooks html input alternative

In IOS5 on the iPad, iPad2 etc. iBooks accepted <input type="color"> as a way to prompt the keyboard to display when you clicked on an input field, to say, type in the answer to a question. I've just recently updated to IOS6, and this workaround no longer seems to be working. I tried using the JavaScript I found here - http://www.linkedin.com/groups/How-Show-iPads-Keyboard-when-3877009.S.84287009
<script type="text/javascript">
function iPadTouchHandler(event) {
var type = "",
button = 0; /*left*/
if (event.touches.length > 1)
return;
switch (event.type) {
case "touchstart":
// OLD: On iPad2 clicking on a text input field did not show the keyboard
// if ($(event.changedTouches[0].target).is("select")) {
// NEW: Now on iPad2 the touchstart-Event on input fields is ignored and everything works fine
// change my by Roland Caspers, Scheer Management
if ($(event.changedTouches[0].target).is("select") || $(event.changedTouches[0].target).is("input")) {
return;
}
iPadTouchStart(event); /*We need to trigger two events here to support one touch drag and drop*/
event.preventDefault();
return false;
break;
</script>
However this code seems to be outdated and relevant to IOS5. I know of a workaround, which is to put the page with the input into an iFrame, in that case you can just use <input type="text">, however I'd prefer to stay away from iFrames as they tend to move the content around depending on where the input box is. Any thoughts as to other possible solutions or workarounds? Tyvm :)
I am also Facing the same issue on iOS6 for , the same is working perfectly on the <iframe> tag. But it omits the images & style and etc.
Review the code "http://www.linkedin.com/groups/How-Show-iPads-Keyboard-when-3877009.S.84287009", I feel some thing has to modify on below condition:
($(event.changedTouches[0].target).is("select") || $(event.changedTouches[0].target).is("input"))
I'd be great if anyone provide the earlier response.
Thanks
I struggled with this same problem in iBooks on iOS 7. The tricky part was, that iBooks probably makes all text input fields disabled by default. We are using prototype.js, so here is my solution written for prototype:
$('my-input-field-id').observe('touchstart', function(event) {
var element = event.element();
if (element.disabled)
element.disabled = false;
element.focus();
event.stop();
});
So just listen for the 'touchstart' event on the input field and enable and focus the field when touched. It works for ordinary text fields (<input type="text">). Simple :-).

Multiple key presses in JavaScript only working in IE

I have the following code:
function handle_paste_keydown(key)
{
if(key.keyCode == 86 && key.ctrlKey) // Ctrl + V
{
alert("Test...");
}
}
This works in IE, but none of the other browsers. My reason for doing this is that I have finished creating a rich-text editor, but I need to handle the onpaste event carefully because formatted text is able to make it in to my editor, which could pose a minor risk to security, but also butchers my layout if malicious <span>s and <div>s make it in.
My current method is to give focus to an off-screen textarea, which means all code will be pasted in to that (which removes formatting); then I immediately grab the textarea.value and insert it at the current caret position in my contentEditable <div>.
So anyway, how do I get the Ctrl+V to work in all browsers and why doesn't it work in its current state?
Thank you.
If it works in IE but nowhere else you did something wrong.
Use the keypress event rather than keydown.
http://jsfiddle.net/Lxvgr/1/
document.getElementById('foo').onkeypress = function(e) {
if(e.charCode == 118 && e.ctrlKey) alert('pasted');
};
#Eric Sites: "use jQuery" isn't the answer to every javascript question. including an entire external framework to solve a simple 4byte issue like this is ridiculous.

javascript fails in firefox but works fine in IE

my code
function hide()
{
var lblclear= document.getElementById("<%=Label1.ClientID%>");
if(lblclear!= null) {
lblclear.value="";
lblclear.innerText="";
lblclear.outerText="";
}
}
on button click i am calling this function
the above function works fine in IE it is clearing my label text value
in firefox browser it is not clearing my label text value
can any one help me out
thank you
Your problem is that innerText and outerText is not supported on Firefox.
http://www.java2s.com/Tutorial/JavaScript/0460__DOM-Node/GetouterTextvalueforatagFirefoxdoesnotsupporttheouterText.htm
In order to hide this you can remove it (as that looks like what you are doing) or, preferably, using css, either the element.style properties or set className, but you can set the visibility or display to a value that does what you want.
innerText will only work in IE, for other browser you should use innerHTML
function hide()
{
var lblclear= document.getElementById("<%=Label1.ClientID%>");
if(lblclear!= null) {
lblclear.value="";
if (document.all) { // check if IE
lblclear.innerText="";
lblclear.outerText="";
}
else{ // other browsers
lblclear.innerHTML="";
lblclear.outerHTML=""; // updated. thanks #cdmckay
}
}
}
please check working example
Add a call to Alert in your function to see if your function is even getting called.

Categories