Suspected JavaScript click event concurrency issue - javascript

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.

Related

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

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).

Slow checkboxes in IE?

http://jsfiddle.net/RAS4P/ [Open in IE-- it's just a checkbox-- I'm using IE9, not sure about others yet.]
`code`
If you start clicking really fast, just clicking away, the checkbox can't keep up. Some of the clicks don't check/uncheck the checkbox, they just get ignored--- EXCEPT that the function attached to the checkbox (not shown in fiddle) is firing anyway, I console.logged it and found it was catching every click...but the checkbox element itself isn't changing.
Why would that happen?
This only happens in Internet Explorer, just realized.
This is, in fact, by design. IE ignores double-clicks on checkboxes because it's safeguarding its users from themselves, in case they naively try to carry over the Windows double-click-to-open methodology into the browser.
You can disable the checkbox's default operation and code it by hand to allow rapid checkbox toggling if necessary, but I found this buggy and hardly worth the trouble just to cater to IE users.
my guess is this is done by design specifically for supporting touch devices. It would help eliminate any fluctuations as the finger goes on/off the check box. This is purely a theory and I have no facts to back this up just seems to make sense to me.

Detecting a change in radio button/checkbox state

I need to reliably detect the state change of radio buttons/checkboxes on my page in order to watch if the form was modified or not. Now, this is a completely separate script, I cannot modify anything that controls the form.
Right now, I can see only two ways of doing this:
onchange event handler, which helps with textboxes, textareas and selects, but is not fired for checkboxes/radiobuttons
onclick event handler, which is not reliable, because users often use hotkeys to change the values of these elements.
What am I missing here? Is there a way to reliably detect that checkbox was checked/unchecked?
UPDATE: As you guys pointed out, change event is really fired on checkboxes/radiobuttons, despite the fact that w3schools says it is only for text inputs
However, my problem turned out to be that the values of checkboxes/radiobuttons are set via setAttribute in scripts and in that case the event is not fired.
Is there anything I can do in this case?
See: http://www.quirksmode.org/dom/events/change.html.
It says that all major browsers support change event but the IE's implementation is buggy.
IE fires the event when the checkbox or radio is blurred, and not when it is activated. This is a serious bug that requires the user to take another action and prevents a consistent cross-browser interface based on the change event on checkboxes and radios.
I think you can overcome IE's bug with this trick. blur() elements when they focued! (Use something like $('input[type=radio]').focus(function(){$(this).blur();}); in jQuery or use pure javascript)
Ok, after some digging, here is what I found out. Note, this is applicable to Firefox, and, probably to Firefox only. Since in this case I was dealing with internal application, this was enough for me.
So, basically, in order to reliably detect changes in checkbox/radiobutton state in Firefox, you need to do two things:
Set up custom Firefox's event handlers CheckboxStateChange and RadioStateChange for checkbox and radiobutton respectively. These events will be fired when the user changes the inputs or when it is modified via script, using setAttribute, however, these events are not fired, when the state is changed in the script, using checked or selected properties of these elements, this is why we need ...
Watch the changes of the checked property using Object.watch
Standard onchange event is no good, since it only fired when user changes the value directly.
Damn, this thing is broken...
If people get interested, I'll post some code.

Multiple checkboxes check as in Gmail

I'm trying to copy the behavior of Gmail with the checkboxes, selecting a whole range click on one and then shift-clicking another, the checkboxes in between these will change.
However, I'm having a compatibility issue between Firefox and Chrome as clicking the checkboxes works just fine, but clicking the labels somehow it's handled very differently, as Firefox will apparently not trigger the change when shift-clicking.
You can check and test my code here.
Obviously there's a trouble with Firefox and the label, I've tried triggering the checkbox's change(), but it works backwards the behavior of the checkbox, I've tried 'resetting' the label events with preventDefault() and then triggering the change() event and the issue seems to be the same, but now Chrome has this bug (which I think it's somehow the correct way, first homologizing).
The easy way is detecting browsers, but every web developer guru tells us that it's better to identify the problem rather than the browser, so what would be a good fix for this? Also, it doesn't work in IE because it doesn't support indexOf().
Thanks!
For some reason the tag is working differently when highlighting text in Firefox. I think the reason it is failing is because when you hold down shift and click on the label, it doesn't check the box and thus doesn't fire the labeled event.
Check out this: http://jsfiddle.net/xerf/Prxdn/10/
This works both in webkit and FireFox. I changed the labels to span tags. With a bit of CSS, you can fix the padding.

ANDROID WEBKIT: Select elements getting focus events, but not opening!

I have a series of select elements in a form on a mobile site. These select elements are inside a scrolling pane handled through JS and CSS3 transforms, so getting a touch/click/whatever event to register on the selects was enough of a pain in the first place. However, I'm now finding, on android only, that even though the selects are getting clicked, and are getting focus- they simply refuse to open. I'm 100% sure that the selects are getting their focus event (through debug), so honestly, I am completely stumped. Without the debug, there are no other focus/blur events on the selects. It works fine on iPhone... any ideas?
I've been banging my head against the wall with this same issue. It seems to be isolated to Android 2.1/2.2 (and maybe 2.0?). The selects work fine in Android 1.5/1.6. I even created a simple page that just changes the select's display style from none to block and the select still doesn't open consistently. Oddly, sometimes after page refresh it might work, then after another refresh it might be broken again. As you stated, focus and click/touch events do fire from the element, so I'm at a loss as to what the issue is.
Sometimes if I zoom the page I can get the select to open, but even then the value selected isn't represented in the select element on the page.
I submitted a bug report to the Android dev team, but even if it's fixed in future builds the problem will still exist in 2.1/2.2.
Anyone find a workaround for this yet?
//---- Update ------
If you use a webkit-transition to show/hide the element, attaching the following event to the element appears to fix the select inside of it:
.addEventListener("webkitTransitionEnd",function(e){
this.innerHTML = this.innerHTML;
},false);
I'm not entirely sure why this works, but re-writing the element to the DOM seems to help for some reason. Tested in Android 2.1/2.2 simulator, EVO4G and MyTouch.
I found the solution in this answer by a.meservy. Here is the answer, copied for everyone's convenience.
In this case the problem was actually caused by jQTouch. To fix it, just comment out these 4 lines in jqtouch.css
Under "body"
/*-webkit-perspective: 800;*/
/*-webkit-transform-style: preserve-3d;*/
Under "body > * "
/*-webkit-backface-visibility: hidden;*/
/*-webkit-transform: translate3d(0,0,0) rotate(0) scale(1);*/

Categories

Resources