2

I am trying to figure out whether an effect seen in Mobile Safari, Android Browser, and Firefox for Android ("Fennec") is a bug or expected behavior. Basically, the issue is that an <input> element can receive the keyboard focus in certain scenarios even if the <input> element was not originally tapped by the user.

Here is a reproducible test case:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="viewport" content="width=device-width, user-scalable=no">
<style>
#screenCover {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background-color: rgba(0, 0, 0, 0.3);
    z-index: 1;
}

#newItemInput {
    position: absolute;
    width: 200px;
    height: 20px;
}
</style>
</head>
<body>

<div id="screenCover"></div>
<input id="newItemInput" type="text">

<script type="text/javascript" language="JavaScript">
var screenCoverEl = document.getElementById("screenCover"),
    newItemInputEl = document.getElementById("newItemInput");

newItemInputEl.value = String(navigator.userAgent);

screenCoverEl.addEventListener("touchend", function (event) {
    // Move the <input> element in the way.
    newItemInputEl.style.top = (event.changedTouches[0].clientY - 10) + "px";
    newItemInputEl.style.left = (event.changedTouches[0].clientX - 10) + "px";

    // Hide the screen cover. Note that the screen cover was the original target of the tap.
    screenCoverEl.style.display = "none";
}, false);

newItemInputEl.addEventListener("click", function (event) {
    this.setSelectionRange(0, this.value.length);
}, false);
</script>
</body>
</html>

http://jsfiddle.net/f7TKc/2/

Or directly in a mobile browser:

http://fiddle.jshell.net/f7TKc/2/show/

In this example, a screen cover lies on top of the document, but on touchend, the screen cover is hidden and the <input> element is moved to where the user tapped the screen cover. In Mobile Safari, Android Browser, and Fennec 13.0b1, the <input> element inexplicably receives keyboard focus.

I am not able to test this in a newer version of Fennec because it crashes within the emulator.

Is this a bug or expected behavior?

Daniel Trebbien
  • 38,421
  • 18
  • 121
  • 193

3 Answers3

1

Yes I think this is a bug and this might be on the same lines here : http://code.google.com/p/android/issues/detail?id=6721 and if its so, then the link has a few workarounds others have tried out which you can try

Vrashabh Irde
  • 14,129
  • 6
  • 51
  • 103
  • This issue seems different than [6721](http://code.google.com/p/android/issues/detail?id=6721), but seeing that bug report and the corresponding discussion leads me to believe that this is indeed a bug. Thank you for the link. – Daniel Trebbien Oct 24 '12 at 23:18
0

I imagine that this is expected behavior.

The developers of a mobile web browser would need to make special accommodations for poorly designed web pages on small displays.

If a web page doesn't scale properly, a UI element could easily be covered by another layer.

Allowing the click event to propagate down through the layers would prevent the UI element from being unusable.

I don't have any documentation to support this theory.

------EDIT--------

After playing around with your code, I realized that the sceencover is being removed and the input is moved to the touch location at "touchstart". The input then receives focus at "touchend".

You can verify this by setting a delay before the input is moved. If your finger has been removed before the input is placed, it will not receive focus. If you are still touching the screen when the input is moved it will receive focus.

screenCoverEl.addEventListener("touchstart", function (event) {

screenCoverEl.style.display = "none";
setTimeout(function() {
        newItemInputEl.style.top = (event.touches[0].clientY - 10) + "px";
        newItemInputEl.style.left = (event.touches[0].clientX - 10) + "px";
    },250);


}, false);
Jason Hessley
  • 1,608
  • 10
  • 8
  • For the actual webpage where I am experiencing this issue (in a few different ways), I am using Google's [fast button technique](https://developers.google.com/mobile/articles/fast_buttons) to avoid the ~300ms delay between touchend and click. So, the actual webpage is in fact updating the document on `touchend`, but I made a mistake in preparing the test case. I have updated the question. Setting a timeout on `touchend` after which the changes are made does not appear to fix the issue. – Daniel Trebbien Oct 22 '12 at 12:47
0

Touch events get called in this order: touchstart, touchend, click. You need to know if your touch event's touchstart happened on your input. You can implicitly declare a object variable (this is allowed in javascript) and initialize it to false. In the touchstart event set it to true. Then check for it's value in touchend and/or click events. The following code works exactly as expected.

    <script type="text/javascript" language="JavaScript">
    var screenCoverEl = document.getElementById("screenCover"),
    newItemInputEl = document.getElementById("newItemInput");

newItemInputEl.value = String(navigator.userAgent);


screenCoverEl.addEventListener("touchstart", function (event) {
    // Move the <input> element in the way.

    screenCoverEl.style.display = "none";

    newItemInputEl.style.top = (event.touches[0].clientY - 10) + "px";
    newItemInputEl.style.left = (event.touches[0].clientX - 10) + "px";

    // Hide the screen cover. Note that the screen cover was the original target of the tap.

}, false);

newItemInputEl.touchStartedHere = false; // remember if a touch event started on this input
newItemInputEl.addEventListener("touchstart", function (event) {

    this.touchStartedHere = true; //set true if did..

}, false);
newItemInputEl.addEventListener("click", function (event) {


    if(this.touchStartedHere){ // do what you want if touch started here
        this.setSelectionRange(0, this.value.length);  
    }else{  // prevent from receiving focus if touch didn't start here.
        this.blur();
    }
    this.touchStartedHere = false;  // reset to false to prepare for next touch event.

}, false);
</script>
Jason Hessley
  • 1,608
  • 10
  • 8
  • I modified this idea to save the last `touchstart` event in a global. Whenever `newItemInputEl` receives a `focus` event, it checks whether the target of the last `touchstart` event was the input element. If not, blur() is called to give up keyboard focus: http://jsfiddle.net/f7TKc/3/ This approach works in iOS 5.0, 5.1, and 6.0, but it wasn't working in iOS 4.3.2. It turns out that iOS 4.3.2 Mobile Safari does not dispatch touch events to input elements: http://jsfiddle.net/3zjQW/1/ So I added a `click` capturing listener to reset the global, and it worked: http://jsfiddle.net/f7TKc/4/ – Daniel Trebbien Oct 24 '12 at 23:11