jQuery $.post() inside w2ui .reload() makes TEXTAREA silently lose focus in Chrome - javascript

Note: this question is probably too specialized. The solution (if I ever find one) is unlikely to help anyone but myself. Nonetheless, I believe the workaround described below to apply to several borderline Chrome/jQuery focus loss scenarios.
Scenario:
I have an input TEXTAREA to enter some text.
Meanwhile, a timer makes periodical AJAX calls to the server (one per minute).
What happens:
In Firefox, everything is hunky dory and the user can type away to his heart's content.
On Chrome, when the AJAX request fires, the input focus is lost. It goes... nowhere, apparently. window.activeElement returns nothing, and the cursor indeed disappears from the textarea, until the user clicks it again with the mouse.
What I expected:
Well, for the focus to stay there.
Attempts:
One - I have tried setting an event handler on the textarea's [.focusout()][1], only to discover that the event does not fire. It does fire when the user clicks somewhere else, but that doesn't help.
Two - I have then tried a less elegant - say rather, brutal - approach:
var hasFocus = document.activeElement;
if (hasFocus) {
var focusKeeper = window.setInterval(function(){
hasFocus.focus(); // JUST. STAY. THERE.
$(hasFocus).css("background-color", "red");
}, 10);
}
The field goes red, so the handler is firing at least. Except that the focus does not come back. It's just as if Chrome isn't even trying.
Again, everything works as expected in Firefox. I'll try next on Safari to confirm whether this is a Webkit-related thing.
Research and more attempts:
I've found several posts on how to overcome focus loss, or how to set the focus in the first place, even on newly created fields (which mine isn't), but my case seems different enough that they either offered no clue, or just plain didn't work. The documentation states that
element.focus();
is necessary and sufficient, yet sufficient quite clearly it is not. Someone has suggested setting focus using a zero-delay timeout; I tried, but this did not seem to help.
Could this be related to the fact that Chrome maybe runs XHR requests in a different process, so that the "focus" is going to the hidden XHR window? (Haven't tried with the --process-per-site commandline switch, it just occurred to me - I will now give it a try).
Could this be a bug? There was something like it, but bug 27868 was related to Flash objects, not TEXTAREAs - that's a completely different animal AFAIK.
The strange thing is that this behaviour (or one amazingly similar) was noted on Firefox and the bug reporter says explicitly, focus should remain on the same input control like in other browsers, so he did not observe it on Chrome.
JSFiddle - not exhibiting the behaviour, thus supplying a clue
I made a fiddle, and... it works. So the issue seems to be more with the function called in the timer, which is a w2ui grid.reload(). I still do not understand why the focus doesn't come back using focus(), as it should.
Acceptable workaround
Inspired by amphetamachine's comment, I've tried combining several of the tricks in the posts above. I've come up with a combination that works... sort of.
The elements needed (whichever I remove, the trick stops working) are:
re-set the focus manually where it was
do this inside a setInterval timer
blur the focus before re-setting it
unset and reset the focus inside a short, but not zero, setTimeout delay.
// Save focus.
hasFocus = document.activeElement;
w2ui.myGrid.reload(function() { // Callback, called after reloading.
// If there was no focus, we just return.
if (hasFocus) {
// We DON'T do anything directly, but use setTimeout.
window.setTimeout(
function() {
// And before setting the focus, we truly remove it.
hasFocus.blur();
hasFocus.focus();
}, 5); // A timeout of 0 does not work.
}
});
The "con" of this solution is that the cursor visually "shivers", and any key that was pressed during the second part of the grid.reload(), after the unknown event that loses the Chrome focus, will of course get lost.

Obsolete: just update the libraries.
The strange behaviour disappeared by upgrading w2ui to 1.4.2 on the latest Chrome (actually, I did not try on previous Chromes because I didn't think to keep copies of the previous versions).

Related

WebDriver: Change event not firing

I have an application that is using KnockoutJS and I'm attempting to write some tests that test a form. If you don't know KnockoutJS, the short story for it is that it provides bindings from my view to my data model. This means that when I type a value in the an input field, my underlying object automatically gets updated with that input fields value. This is done through a change event by default.
The problem I am having is that when my WebDriver test is typing into the field, the change event is not firing so my underlying data model does not have the appropriate values. This causes my form validation to fail when it should not.
I've done everything I could find on the internet to make this work. I've:
sent the tab key
clicked away from the form field
send JavaScript code to fire focus and blur events (validation occurs on blur)
clicked the form field before typing
set waits just incase it was a timing issue
changed KnockoutJS to update input field on afterkeydown
None of these have worked for me.
In addition, I have verified that this is not an event bubbling issue as I removed all other events explicitly, leaving just the KnockoutJS change event.
for the solution i'm looking for is one that works for all browser drivers (... at least the main ones e.g. IE, FF, Chrome, Safari) and does not require the use of jQuery.
How do I solve the problem?
Here is the relevant code I'm using to type values into the field:
// find element
WebElement input = this.element.findElement(By.className("controls"))
.findElement(By.tagName("input"));
// to set focus?
input.click();
// erase any existing value (because clear does not send any events
for (int i = 0; i < input.getAttribute("value").length(); i++) {
input.sendKeys(Keys.BACK_SPACE);
}
// type in value
input.sendKeys(text);
// to fire change & blur? (doesnt fire change)
//input.sendKeys(Keys.TAB);
// to fire change & blur? (doesnt fire change)
driver.findElement(By.tagName("body")).click();
So I have found a way to work around this issue for now, but by far do I believe this is the correct solution. This does break my rule about not using jQuery, however I feel this is okay for me as KnockoutJS requires jQuery be loaded. There's probably a plain ol' JavaScript way of doing this. I have tested this with FireFox, Safari, and PhantomJS. I assume it will work just as well in Chrome. I give no promises for Internet Explorer.
I am NOT going to mark this answer as the correct answer. The correct solution should be through WebDriver browsers firing the proper events. Only when I believe this is not possible through WebDriver will I mark this as the correct answer.
// find element in question
WebElement input = this.element.findElement(By.className("controls"))
.findElement(By.tagName("input"));
// click it to fire focus event
input.click();
// erase any existing value
for (int i = 0; i < input.getAttribute("value").length(); i++) {
input.sendKeys(Keys.BACK_SPACE);
}
// enter in desired text
input.sendKeys(text);
// fire on change event (WebDriver SHOULD DO THIS)
((JavascriptExecutor) driver).executeScript(
"$(arguments[0]).change(); return true;"
, input);
// click away to fire blur event
driver.findElement(By.tagName("body")).click();
So I believe I have found out my problem. I will fully admit this was PEBKAC. I had forgotten that WebDriver has issues if the browser window is not in active focus on the machine (which is still weird to me). When I was debugging the problem, I was using my editor to step through the code. By running the code normally and without removing focus from the browser, the events fire as expected using all three solutions (tab, click-away, and javascript).
I have an example project showing all three methods, however I am a major noob with git and github and am having problems delivering the project. Once i figure that out, I will share the project with you all.
EDIT: Got the example code up on GitHub (https://github.com/loesak/knockout.webdriver.change.event.test). You can either start the project as a webapp in a server and run the test manually or you can run the tests via maven (mvn clean install). I didn't put a whole lot of effort into getting this to work robustly so it is assuming you have Firefox installed and port 0808 8080 is open.
EDIT: Fixed stated port number

iOS5 Safari: Keyboard does not hide in a home screen HTML app

I am working on a custom application for the iPad that runs as a homescreen app, but is made in all CSS/HTML/Javascript. (not using the SDK here)
I have run into an issue with a calculator I have built into my page not hiding the keyboard. No matter what I do, the keyboard stays up. I have searched this extensively and tried everything I can think of, but the keyboard stays up no matter what I do.
Explanation of what I have tried to hide the keyboard:
I have tried to blur all input fields to remove focus. I have tried setting focus onto non-text field items.
There were several threads on Stackoverflow from earlier this year/last year that suggested both of those options, but they do not appear to be working anymore.
To test further, I put a blank a href="#" on an img that was above the calculator, so that I could set focus on a non-entry and see if that would auto-minimize the keyboard. When I tap that item above the keyboard the focus changes and I am no longer in input mode, but the keyboard stays up.
Did Apple break this functionality with the latest update? If so, is there a work around?
Here is some example code that doesn't work:
$('input').blur(function(e) {
// Keyboard disappeared
window.scrollTo(0, 1);
});
That code successfully removes focus from the inputs, but the keyboard stays up. I have also attempted the inverse of that by just .focus ing on a non-text element. And additionally, as stated previously, I have straight-up just added a non-text element on the page and that still doesn't hide the keyboard.
Thanks so much for any help, and feel free to link/abuse me if I have mistakenly reposted. :)
you should be able to blur it just by using something like this
$('input').blur();
you should put this inside the function/procedure that happens when you want it to disappear, unless your looking to disable it completely?
document.activeElement.blur() inside a try catch block works for me. (Possibly you also need a setTimeout? I didn't need a timeout, and it is important to avoid timeouts wherever possible because they can easily cause nasty heisen-bugs!)
Also double check that you are not calling focus() somewhere within a mousedown or click event (which causes the keyboard to show). You can use a console.log(document.activeElement.tagName); or similar to help find what has current focus.
However if you don't find a solution then I am very interested in seeing how you get the keyboard to stay up... I have a use for that :-)

Suspected JavaScript click event concurrency issue

I'm using this piece of jQuery to add radio button behavior to checkboxes, i.e. allowing only one from each group to be checked.
$("input:checkbox").click(function()
{
$("input:checkbox:checked[name='" + this.name + "']")
.not(this).removeAttr("checked");
});
It works great, or at least I thought so until I discovered that in Opera I can still check multiple checkboxes from the same group if I'm insistent. By rapidly toggling between some checkboxes (clicking like a maniac) I eventually end up with two (or more) checked.
Normally thinking of JavaScript as single-threaded I was kind of surprised by this. However, I know JavaScript is not always as single-threaded as it seems (see this answer) and I assume that's somehow causing this behavior. How can it otherwise happen?
If my assumption is right, can anyone explain what happens? In the answer I link to you can read:
Similarly calling click() on an element that provides it calls the onclick handler immediately in all browsers (at least this is consistent!).
That seems relevant, except that this only happens with Opera (I've tried with IE(8), FF, Chrome and Safari).
Any input is much appreciated!
If you click really fast here,
http://jsfiddle.net/RxbW6/1/
You will see that when 2 checkboxes end up being checked at the same time (by spamming them) the click event does not fire for the second checkbox. So it's not a threading issue with the 2 click events interfering, rather opera does not seem to be firing the onclick event at all.
I would recommend just using a radio group instead and styling it, this case is what it is here for.
However,
$("input:checkbox").mouseup(function()
{
$("input:checkbox:checked[name='" + this.name + "']")
.not(this).removeAttr("checked");
});
Will fix your issue.
[Edit] mouseup will probably be better so you can mousedown and then move off and keep the previous check.

IE, textarea value is not changed when pressing buttons

I have a really nasty problem with focus in Internet Explorer.
I have a textarea for inputting text. This textarea is not visible for the user and is only used to provide robust text input for a more advanced view.
As the textarea isn't visible and shouldn't be I use textarea.focus() in the JavaScript to activate text-input. This has worked fine until now where I get really weird results.
For the textinput I basically use this event plus an exact copy for onkeypress.
textarea.onkeyup = function (e) {
//textarea.value contains the full text
//Update the view with this value
};
The problem is that sometimes textarea.value is not updated. I can even see the button in e.keyCode on the keyup event above but the value isn't changed.
This seems to happen after I have clicked somewhere on the page, but this does not trigger a blur-event. I'm logging the onfocus- and onblur-events so I can see when the textarea loses focus but it doesn't. And i still receive the keyup/press/down events.
If I try to refocus the textarea with textarea.focus() the problem remains. A workaround I found though is to focus the window with window.focus() and right after call textarea.focus().
The problem with this is that focus is a really expensive operation in IE (no kidding) and since I cannot detect when this problem will happen I have to keep doing it with certain interval which seriously affects the performance of my application (involves animation etc).
I use the exact same code for other browsers and do not have this problem there.
Change the identifier to something less generic like "textAreaAdv", IE sometimes get confused with this word.

Detecting drop into textarea with Javascript

All I need is to be able to detect when text is dropped into a Textarea. I then take that text and do stuff with it and clear the textarea. There may be many of these textareas and very high UI expectations, so polling is a last resort.
For IE, "onfocus" does the trick, since this event is fired after the user drops stuff into the textarea.
For Firefox, I can't find an event that works. I've tried onmouseup and onchange.. don't know what else to try from there. I'd hate to have to poll the textarea for content. Help much appreciated. Thanks.
Edit: For clarification, "dropped" means the user selects text (usually) from the page, but it doesn't matter where, drags it, and drops it into the textarea. This is not the same as Ctrl+V, or right click pasting (which take two different methods of detection, BTW), or (obviously) typing into the textarea. Specifically, it is the "drop" aspect of drag and drop. I really don't know how else to phrase it.
I feel this question was stated rather accurately. To humor me, assume that I meant any of the other operations on a textarea that you all have chosen to share. If I meant "pasting", don't you think I would have mentioned something about pasting in the title or content of my question? Same goes for typing in a textarea. But, perhaps, you all just don't know me well enough to know that I type what I mean, rather than mistakingly typing things only somewhat related to what I mean.
For Firefox, this works for me:
window.addEventHandler("dragdrop", function(event) {alert("Drop it like it's hot!")}, true)
Does not work in Safari, however. Haven't tried IE.
Polling seems to be the only way to go.
When a Drag+Drop operation is started within the browser, it seems to cancel out everything else that's going on. You can witness this by putting "onmouseover" events on something on the page, then starting a DnD operation and dragging over it. The events will not fire.
It seems only a coincidence of how the browser internally handles "drag and drop" that it causes onfocus() to be fired for the textarea in IE and Safari. I doubt there is much defined in the W3 specification... not that it matters, W3 is crap, and besides nobody adheres to it completely.
In my question I stated the luxury of emptying out the textarea after each use, so I feel that polling for: textarea.value.length>0 is hardly any cost and shouldn't be much of a performance concern.
But, alas, it specifically has to do with dropping content into a textarea.
Down-mod away to sooth your pain.
If you want your textarea to react to text being dropped, pasted or typed, your only solution would be a button. Even a timer polling the content will not help you.
You mention that you want the user to be able to type text in the textarea. And you mention that you want to clear the text whenever the event is fired. There is no possible way for the browser to know that the user has finished typing. You must rely on a specific action.
Now for the drop or paste, you can rely on the mouseup and keyup events in order to detect that.
What do you mean by "dropped"? Are you talking about a paste (^V) operation? If so Aron's answer is indeed correct, you will get keydown/keyup (and the value will be readable in keyup).
A generic callback that will happen on any update to an input is 'onchange' - you could try that. Note that when typing, it doesn't get called until you finish and unfocus the input, so if you need instant updates on both typing and other forms of input, you still need 'onkeyup' as well.
Edit re clarification: IMO 'onchange' should be fired when a drag-and-drop causes content to be added to a text area; I consider it a browser bug that it is not (in Firefox at least). I can only concur in this case that polling is all you have left.

Categories

Resources