I have run into an infuriating bug on Safari for iPad that I cannot fix.
Architecture:
- backbone 0.9.9
- jquery 1.7.2
- jquery mobile 1.3.1
User agent:
- iOS 5.1.1 (iPad)
- Safari 5.1 mobile
- full user agent string: Mozilla/5.0 (iPad; CUP OS 5_1_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B206 Safari/7534.48.3
I have a 10 instances of the same View, each of which has a nested View that contains a textarea element. For some reason when you tap the textarea, it randomly does not focus. I've read that Safari mobile is wonky when you attempt to trigger focus events that don't come from a tap/click event, but this is a direct tap and it still does not focus reliably. Here's the stripped-down code for the views:
var ParentView = Backbone.View.extend({
render: function() {
this.$el.html("<div class='textarea-container'></div>");
this.textareaView = new TextareaView({
el: this.$el.find('.textarea-container')
});
this.textareaView.render();
}
};
var TextareaView = Backbone.View.extend({
events: {
'tap .my-textarea': 'handleTextareaTap'
},
render: function() {
this.$el.html('<textarea rows="4" cols="80" class='my-textarea'></textarea>');
},
handleTextareaTap: function(event) {
console.log('TAPPED');
}
};
var i = 0;
while ( i < 10 ) {
var view = new ParentView();
view.render();
$(body).append(view.$el);
i++;
}
The tap event handler fires 100% of the time. The console correctly displays "TAPPED" each time. But most of the time, the user agent fails focus in the textarea. I added the following line into the TextareaView to see exactly which events Safari is firing and in what order:
var TextareaView = Backbone.View.extend({
render: function() {
this.$el.html('<textarea rows="4" cols="80" class='my-textarea'></textarea>');
this.$el.find('.my-textarea').on('blur change click contextmenu copy cut dblclick focus focusin focusout hashchange keydown keypress keyup load mousedown mouseenter mouseleave mousemove mouseout mouseover mouseup mousewheel paste reset scroll select submit textinput unload wheel tap touch scrollstart scrollstop swipe swipeleft swiperight vclick vmousecancel vmousedown vmousemove vmouseout vmouseover vmouseup touchstart touchend touchmove touchcancel', function(event) {
console.log(event.type);
}
},
};
Here's the event order I get when the textarea focuses correctly: touchstart, vmouseover, vmousedown, touchend, vmouseup, vclick, tap, vmouseout, mousemove, mousedown, focusin, focus, mouseup, click, focusout, blur
Here's the event order I get when the textarea fails to focus: touchstart, vmouseover, vmousedown, touchend, vmouseup, vclick, tap, vmouseout, mousemove
For some reason the events after mousemove fail to fire. I've tried manually triggering these events as well, but the textarea element still does not focus nor does the keyboard pop up, e.g.:
var TextareaView = Backbone.View.extend({
handleTextareaTap: function(event) {
// This still doesn't work:
this.$el.find('.my-textarea').trigger('focus');
// Neither does waiting for the synthesized WebKit events to fire:
var _this = this;
setTimeout(function(){
_this.$el.find('.my-textarea').trigger('focus');
}, 1000);
}
};
I've poured over Apple's event handler documentation to no avail, and I can't find any bug reports relating to this in any of the repos on github.
Two other weird behaviors that I do not understand:
- the first instance of the textarea always works correctly
- the textarea focuses despite a blur event being called
Any insight would be appreciated.
Cheers,