Solving cross browser issues in dynamically detecting textarea text value changes

Solving cross browser issues in dynamically detecting textarea text value changes

I have developed a webpage with a form that has (1) a textarea into which a user can input text of any length (right now it is 800px wide and 120px high), and (2) an input text box into which a user can input a number of days over which the textarea text should be considered active.  The form has two buttons that are enabled or disabled whenever any changes in either the textarea or input box differ from the original “reset” values.  The two buttons represent submit and reset actions.

 

I wanted to enable and disable the buttons real time depending on the textarea and text box values as the user changes them.  Looking just at techniques for detecting changes in the textarea value, I searched every source and site I could find for approaches to making this happen.  I learned that context menu events are problematic and that IE responds to changes in properties as opposed to values under some circumstances.  Playing around with events and test input, for example, I discovered that IE 9 would not fire context menu cut or delete events if the location of the corresponding link in the context menu happened to overlap the textarea boundaries.  I found many code and plug-in solutions to the problem, all of which seemed to me to be complex and indirect.  Frankly, I could not get some of them to work.

 

To keep the explanation of what I eventually came up with simple, I’ll limit the discussion to one textarea.  Here is its HTML, simplified:

 

<textarea class="ReviewerTextArea ReviewerInputElement ">Any text data.</textarea>

 

When playing around with IE, I noticed that if I sized the textarea small enough so that context menu links, like “delete,” were outside the boundaries, they actually fired the desired event.  This is because I had already created focusin and focusout events (for other reasons) which I believe were triggered when the context menu popped up and the out-of-boundary item was clicked (first focusout, then focusin).  So I came up with the following simple solution which appears to work in all browsers.  They all, including IE 9, trigger at least one event for all text changes I could come up with (keystrokes, key-based cut/paste/delete, menu based cut/paste/delete/undo/redo, drag-and drop’s, etc.).  Responding to the event can then, of course, be done any way one wishes.  I used a trigger of the “change” event.

                               

       // Respond to changes in input fields by type, enable or disable the submit and reset action buttons

       // Restore visibility of the corresponding field in this response -- see note on contextmenu event below

 

                                $(".ReviewerTextArea").change(function()

                                {

                                                // Restore visibility

                                               

                                                $(this).css("visibility", "visible");

                                               

                                                // Set action buttons

                                               

                                                SetRecommendationButtonStatus($(this));

                                });

 

      // Trigger change for text change events in all input fields -- mix should cover all browsers, all versions

                               

                                $(".ReviewerInputElement").keyup(function()

                                {

                                                $(this).trigger("change");

                                });

                               

                                $(".ReviewerInputElement").mouseleave(function()

                                {

                                                $(this).trigger("change");

                                });

                               

                                $(".ReviewerInputElement").focusin(function()

                                {

                                                $(this).trigger("change");

                                });

                               

                                $(".ReviewerInputElement").focusout(function()

                                {

                                                $(this).trigger("change");

                                });

                               

                                $(".ReviewerInputElement").on("textinput input cut delete drop paste redo undo", function()

                                {

                                                $(this).trigger("change");

                                });

                               

       // Disable visibility on contextmenu event so that all possible context menu selections

      // yield a focusout event -- trigger change to insure a subsequent focusin event

                                                               

                                $(".ReviewerInputElement").on("contextmenu", function()

                                {

                                                $(this).css("visibility", "hidden");

                                                $(this).trigger("change");

                                });

 

So, with two lines of code, first hiding the textarea when the contextmenu event is fired, then restoring its visibility in the change event logic, everything works.  What I think is happening is this:

 

When the user invokes the context menu, the contextmenu event is fired.  The browsers freeze the appearance of the screen when that happens.  Hiding the textarea must be occurring behind the scenes, however, even though it is still visible on screen.  When any menu item is clicked, it appears to be outside the textarea’s boundaries and  will therefore update the text before focus returns to the textarea element.  Furthermore, the change always appears to trigger at least one event, either one or more of the events in the early part of the code or the change event as triggered in the contextmenu event logic itself, in both cases after the change in the textarea’s text value has been made by the browser.  The “change” that is triggered in the second line of the contextmenu event appears not to be executed until after the context menu item is clicked and executed (it behaves as if it is queued up).  The last step is to make the textarea visible again in the triggered logic (in my case, “change”) before doing any other processing of the textarea’s value.  This appears to happen instantaneously in all the current browsers because the context menu updates the screen only after the user has clicked a menu item or left the menu without performing any action.

 

A few notes:

(1)  The code to restore visibility can’t occur in the contextmenu event.  It has to be embedded in something that will execute only after the menu item is selected.

(2)  jQuery “show” and “hide” didn’t work universally for some reason (for example, copying from a word document and pasting into the textarea didn’t work).  I had to use CSS.

(3)  The focusin event is required for this technique to work.

(4)  I left in a mouseleave event so that it, along with the focusout event, as a fall back, would fire when the user simply leaves the textarea when using older browsers that may not fire the contextmenu event.

 

I am offering this as an idea that, after a great deal of keyboard and mouse text input testing, seems to work with any and all text changes in all browsers (IE 9 and the latest versions of Firefox, Chrome, Safari, and Opera).  I have not tested with earlier versions of IE or other browsers, though it should work in any version of any browser where the contextmenu event is fired.

 

This seems to be a great solution to the problem.  Does anyone see any issues?  And is my assumption about what is going on correct?

 

Thanks for any comments and input.

 

Jack Herr
 
Update:  Neither switching between CSS (“display”, “none”) and CSS (“display”, “[something else]”), or switching between jQuery “show” and “hide,” work for this technique in all browsers.  In IE 9, one cannot cut or copy from an external document and paste in the textarea.  The context menu “paste” option is disabled (though not so in Firefox and perhaps in other browsers -- didn't check).  Works universally only, as mentioned in the main text, with the “visibility” property.
 
Update Two:  When I access the value (namely the textual content) of the textarea in the change event, and otherwise process a few other elements, all in order to report on the number of characters in the textarea, Internet Explorer deactivates the undo feature of its context menu.  All its other menu items, as well all the menu items in all other browsers, continue to work perfectly.  I am giving up on trying to accommodate Internet Explorer on this issue.
 

Update to Update Two:  It appears that IE9 voids its undo context menu item because I am setting the text values of elements other than the textarea in question.  This EVEN OCCURS IN TWITTER, so I am in good company, and, once again, IE proves to be defective.