Last active
June 16, 2017 21:31
-
-
Save jasoncomes/27af9f74f77b9e82c37a89ad26ac9107 to your computer and use it in GitHub Desktop.
When multiple forms or inputs are placed throughout a web page, the native iOS "next" or "previous" arrows jump from input to input. On pages with our widget, the arrows "jump" from the final widget field to the next input element no matter where it is on the page. This jarring to our users and takes users who have selected widget fields to an u…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
MobileMultForm - 1.5 | |
*/ | |
(function($) { | |
'use strict'; | |
var mobileMultiForm = { | |
/** | |
* | |
* Defaults | |
* | |
*/ | |
excludeList: ':input[type=button], :input[type=submit], :input[type=reset], :button, :input[type=hidden]', | |
get $inputs() | |
{ | |
return $(':input').not( this.excludeList ); | |
}, | |
get mobile() | |
{ | |
return this.isMobile(); | |
}, | |
/** | |
* | |
* Detect if Mobile Browser | |
* | |
*/ | |
isMobile: function() | |
{ | |
return ( /iPhone|iPad|iPod/i.test( navigator.userAgent ) ); | |
}, | |
/** | |
* | |
* Returns active inputs based from the selected input | |
* | |
*/ | |
inputsActive: function( $element ) | |
{ | |
var $parentForm = $element.parents('form'); | |
var $inputsActive = ( $parentForm.length ? $parentForm.find( this.$inputs ) : $element ); | |
return $inputsActive; | |
}, | |
/** | |
* | |
* Any part of form or input is in Viewport | |
* | |
*/ | |
isInView: function( $element ) | |
{ | |
var docViewTop = $(window).scrollTop(); | |
var docViewBottom = docViewTop + $(window).height(); | |
var elemTop = $( $element ).offset().top; | |
var elemBottom = elemTop + $( $element ).height(); | |
return ( ( elemTop <= docViewBottom ) && ( elemBottom >= docViewTop ) ); | |
}, | |
/** | |
* | |
* Enable all disabled form inputs (Especially EDUWIDGET) | |
* | |
*/ | |
removeDisabledInputs: function() | |
{ | |
this.$inputs.removeAttr( 'disabled' ); | |
}, | |
/** | |
* | |
* Resets Inputs | |
* | |
*/ | |
resetInputs: function() | |
{ | |
this.$inputs.filter('[tabindex=-2]').removeAttr( 'tabindex disabled' ); | |
}, | |
/** | |
* | |
* Closes Native Keyboard | |
* | |
*/ | |
closeInputKeyboard: function( $element ) | |
{ | |
if( $element.is('form') ) | |
{ | |
$element.find( this.$inputs ).blur(); | |
} | |
else | |
{ | |
$element.blur(); | |
} | |
}, | |
/** | |
* | |
* Go to Previous Input | |
* | |
*/ | |
goPrevInput: function( $element ) | |
{ | |
var $inputsActive = mobileMultiForm.inputsActive( $element ); | |
var $inputPrevious = $inputsActive.eq( $inputsActive.index( $element ) - 1 ); | |
$( $inputPrevious ).triggerHandler( 'mousedown' ); | |
}, | |
/** | |
* | |
* If Input(selected) is Empty | |
* | |
*/ | |
ifInputEmpty: function( $element ) | |
{ | |
var $options = $element.find('option'); | |
if( $element.is('select') && ( $options.length == 0 || ( $options.length == 1 && $options.filter(':first-child').val() == 0 ) ) ) | |
{ | |
return true; | |
} | |
return false; | |
}, | |
/** | |
* | |
* Listener - Window Scroll | |
* | |
*/ | |
scrollListener: function( $element ) | |
{ | |
var $parForm = $element.parents('form'); | |
var $elContainer = ( $parForm.length ? $parForm : $element ); | |
$(document).on( 'scroll', function( event ) | |
{ | |
if( ! mobileMultiForm.isInView( $elContainer ) ) | |
{ | |
mobileMultiForm.closeInputKeyboard( $element ); | |
$(this).unbind( event ); | |
} | |
}); | |
}, | |
/** | |
* | |
* Listener - Input Focus | |
* | |
*/ | |
focusListener: function() | |
{ | |
mobileMultiForm.$inputs.on( 'focus', function( event ) | |
{ | |
mobileMultiForm.scrollListener( $(this) ); | |
}); | |
}, | |
/** | |
* | |
* Listener - Focus Out Input Timer(500ms Reset) | |
* | |
*/ | |
focusOutListener: function() | |
{ | |
mobileMultiForm.$inputs.on( 'focusout', function() | |
{ | |
setTimeout( function() | |
{ | |
if( ! mobileMultiForm.$inputs.is( ':focus' ) ) | |
{ | |
mobileMultiForm.resetInputs(); | |
} | |
}, 500); | |
}); | |
}, | |
/** | |
* | |
* Listener - Select Input (Prefocus) | |
* | |
*/ | |
mouseDownListener: function() | |
{ | |
this.$inputs.on( 'mousedown', function( event ) { | |
event.preventDefault(); | |
var $inputsActive = mobileMultiForm.inputsActive( $(this) ); | |
var $allOtherInputs = mobileMultiForm.$inputs.not( $inputsActive, '[tabindex="-1"]' ); | |
$inputsActive.not( '[tabindex="-1"]' ).removeAttr( 'tabindex disabled' ); | |
$allOtherInputs.attr( 'tabindex', '-2' ).attr( 'disabled', 'disabled' ); | |
if( mobileMultiForm.ifInputEmpty( $(this) ) ) | |
{ | |
mobileMultiForm.goPrevInput( $(this) ); | |
} | |
else | |
{ | |
$(this).focus(); | |
} | |
}); | |
}, | |
/** | |
* | |
* Init - Mobile Only | |
* | |
*/ | |
init: function() | |
{ | |
if( this.mobile ) | |
{ | |
$(window).load( function() { | |
mobileMultiForm.focusListener(); | |
mobileMultiForm.focusOutListener(); | |
mobileMultiForm.removeDisabledInputs(); | |
mobileMultiForm.mouseDownListener(); | |
}); | |
} | |
} | |
}; | |
mobileMultiForm.init(); | |
})(jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Widget Mobile Fixes
iOS has long contained a web form feature that negatively affects pages with widgets. Here's the problem and our solution:
Mobile Issues
When multiple forms or inputs are placed throughout a web page, the native iOS "next" or "previous" arrows jump from input to input. On pages with our widget, the arrows "jump" from the final widget field to the next input element no matter where it is on the page. This jarring to our users and takes users who have selected widget fields to an unexpected part of the page, preventing them from submitting the widget.
EduWidget by default disables the
Select by Category
andSelect by Subject
select inputs. This removes the native iOS "next" arrow to the Category and Subjects select inputs. Without the "next" arrow, users lose the ability to move quickly through fields and complete the form easily.If EduWidget select inputs are enabled, this creates the possibility of empty select fields (mainly
Select by Category
andSelect by Subject
). But by disabling the input fields causes other issues mentioned above.Manual tab indexes should be avoided: they also create "jumps" that unexpectedly move users throughout fields on the page.
Mobile Solution
Isolate forms & orphaned inputs. This prevents the user from hitting the next button and being displaced to another part of the page. If inputs are in a form tag, you are allowed to jump from child input to child input.
Enable all EduWidget select inputs. Allows the user to quickly complete form inputs while keeping the user locked into the form not being displaced on page.
Disable inputs outside of the current form. If the
select
input field has nooption
s besides the default "Select a {Field Name}", the user will be moved to the most previous input select that has options. Selecting an option in that previous input should correctly populate the option-lessselect
.Reset inputs when leaving current form. The script adjusts tabindex of elements outside the current form, then resets those inputs' tabindex to the default on leaving the form. We'll use
tabindex="-2"
for those elements and not changetabindex="-1"
: devs can still write forms to skip inputs without interference from this script.