iScroll 4 not working with form <select> element iPhone Safari and Android browser

て烟熏妆下的殇ゞ 提交于 2019-11-26 20:04:05
sym3tri

The problem is that iScroll cancels the default behavior of your select tag (Not a very good implementation if you ask me).

This occurs in the _start() function on line 195:

e.preventDefault();

If you comment that out you'll notice the select tag works again.

But commenting it out means you've hacked the library which might break other desirable iScroll functionality. So here's a better workaround:

var selectField = document.getElementById('Field10');
selectField.addEventListener('touchstart' /*'mousedown'*/, function(e) {
    e.stopPropagation();
}, false);

That code will allow the default behavior to occur, without propagating the event to iScroll where it screws everything up.

Since your JS is not inside any jQuery-like onReady() event, you'll have to make sure to you put this code AFTER the HTML where your select elements are defined.

Note that for mobile devices the event is touchstart, but for your PC browser it will be mousedown

I had the same issue on the iScroll 4.1.9 on android, I just replaced the line 95 (on my version) :

onBeforeScrollStart: function (e) { e.preventDefault(); },

by :

onBeforeScrollStart: function (e) {var nodeType = e.explicitOriginalTarget ? e.explicitOriginalTarget.nodeName.toLowerCase():(e.target ? e.target.nodeName.toLowerCase():''); if(nodeType !='select' && nodeType !='option' && nodeType !='input' && nodeType!='textarea') e.preventDefault(); },           

that enable focus on input, select and textarea tags

Finally fixed this for Android. Ended up modifying a couple of lines in iscroll.js

Here's how we initialize iScroll.

// code from https://github.com/cubiq/iscroll/blob/master/examples/form-fields/index.html
// don't preventDefault for form controls
_menuScroll = new iScroll('menu_wrapper',{
    useTransform: false,
    onBeforeScrollStart: function (e) {
        var target = e.target;
        while (target.nodeType != 1) target = target.parentNode;

        if (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA')
        e.preventDefault();
    }
});

The onBeforeScrollStart is what allows the controls' default behaviors to take place. Android browser seems to have problem with useTransform, so turn to false.

Finally, some additional iscroll code needed to be excluded when useTransform is false:

// iscroll.js v4.1.9
// line 216:
if (that.options.useTransform) bar.style.cssText += ';pointer-events:none;-' + vendor + '-transition-property:-' + vendor + '-transform;-' + vendor + '-transition-timing-function:cubic-bezier(0.33,0.66,0.66,1);-' + vendor + '-transition-duration:0;-' + vendor + '-transform:' + trnOpen + '0,0' + trnClose;

// line 295:
if (that.options.useTransform) that[dir + 'ScrollbarIndicator'].style[vendor + 'Transform'] = trnOpen + (dir == 'h' ? pos + 'px,0' : '0,' + pos + 'px') + trnClose;

Tried several other methods before realizing that it had to do with the css that iscroll adds.

I Know am late,but it might be helpful for some one,

write the following code in pageshow event,but be sure that div ids not same.

it is because, iscroller prevents the default behaviors of elements

 $('#yourpage').bind('pageshow',function (event, ui) {

       var myScroll;

         if (this.id in myScroll) {
         myScroll[this.id].refresh();

         }else{ 

          myScroll[this.id] = new iScroll('wrapper', { //wrapper is div id
                   checkDOMChanges: true,
                   onBeforeScrollStart: function (e) {
                         var target = e.target;
                         while (target.nodeType != 1) 
                         target =target.parentNode;

                   if (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA'){  e.preventDefault();  }
                                 }
                               });
                            }
     document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false);

     });

Here is the solution

/* on page add this after all scripts */
    <script type="text/javascript">
            var myScroll;
            function loaded() {
                myScroll = new iScroll('wrapper');
            }
            document.addEventListener('DOMContentLoaded', function(){ setTimeout(loaded,500);}, false);
    </script>

/* attach a script for fix */
        $(document).ready(function(){
            var my_select = document.getElementsByTagName('select');
            for (var i=0; i<my_select.length; i++) {
                my_select[i].addEventListener('touchstart' /*'mousedown'*/, function(e) {
                    myScroll.destroy();
                    setTimeout(function(){myScroll = new iScroll('wrapper');},500);
                }, false);
            }

    /*if you have input problems */

            var input = document.getElementById('input');

            if (input) {
                input.addEventListener('touchstart' /*'mousedown'*/, function(e) {
                    e.stopPropagation();
                }, false);
            }    
        });

another code example for solution. and thanks for previous comments! Using Jquery!

After:

$(document).ready(function() {            
    document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false);

    document.addEventListener('DOMContentLoaded', setTimeout(function () { loaded(); }, 200), false);
});

in loaded function

function loaded() {
    var allSelects = $('select');
    allSelects.each(function(index, item) {
                        item.addEventListener('touchstart' /*'mousedown'*/, function(e) { e.stopPropagation(); }, false);
                    });
}
Sunil P

Replacing line, onBeforeScrollStart: function (e) { e.preventDefault(); },

By

onBeforeScrollStart: function (e) {
    var nodeType = e.explicitOriginalTarget ? e.explicitOriginalTarget.nodeName.toLowerCase():(e.target ? e.target.nodeName.toLowerCase():'');

    if(nodeType !='select' && nodeType !='option' && nodeType !='input' && nodeType!='textarea') {
         e.preventDefault();
    }
},

In iScroll.js Works

Start with this code. This solution worked for me:

<script type="text/javascript">

var myScroll;
function iScrollLoad() {
    myScroll = new iScroll('wrapper');
    enableFormsInIscroll();
}

function enableFormsInIscroll(){
  [].slice.call(document.querySelectorAll('input, select, button, textarea')).forEach(function(el){
    el.addEventListener(('ontouchstart' in window)?'touchstart':'mousedown', function(e){
      e.stopPropagation();
    })
  })
}

document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false);
document.addEventListener('DOMContentLoaded', function () { setTimeout(iScrollLoad, 200); }, false);

</script>

I'm late but I leave you my solution.

If your are using jQuery you can try that.

$('input, textarea, button, a, select').off('touchstart mousedown').on('touchstart mousedown', function(e) {
    e.stopPropagation();
});
  // Setup myScroll
  myScroll = new IScroll('#wrapper', {
    scrollX: horizontalSwipe,
    scrollY: !horizontalSwipe,
    momentum: false,
    snap: document.querySelectorAll('#scroller .slide'),
    snapSpeed: 400,
    bounceEasing: 'back',
    keyBindings: true,
    click: true
  });

For me, I just need to add click: true on the last line... Spent the whole day debugging and implementing all the suggested solutions to no avail...

he anyone.

i know's about all your answer's but i have new way to offer. without java-script or drop iscroll functions.

the big problems with all this solution is when you scroll on input's element you have no scroll on the page. when you make just one or two input's isn't really matter but when the page is a form you have big problem to scroll the page.

the solution i offering is to wrap the input in label tag or make the label tag with for pointer to the input. then make with absolute positioning and z-index the label above the input. when you touch on the label you focus the input.

and example please

HTML

<fieldset>
<label>
    <input type="text" />
</label>
</fieldset>

CSS

fieldset {position:relative;z-index:20;}
label {position:relative;z-index:initial;}
input {position:relative;z-index:-1;}

you can also in this way put the label side of the input and with position absolute of the input put that into the label area

is working in 100%, check it

There is a bug with Android when -webkit-transform:translate3d is applied to a container that has a select box or password box[1]. The boxed area to activate those elements move, and are not where you think they'd be. Additionally, password boxes paint in a different location, so it appears that you have two input elements instead of one.

I work at AppMobi and we have developed a toolkit library that has fixes for these. We've implemented custom select box widgets and a replacement for the password input field. Check it out below.

https://github.com/imaffett/AppMobi.toolkit

[1] I think the author of the comment is talking about this bug https://bugs.webkit.org/show_bug.cgi?id=50552

Krazer

I'm a bit late to the game, but if anyone is still interested, I took @bastien's approach and tweaked it a bit to get it to work on Android. I'm using JQM with my implementation.

Basically what I did was:

function scrollMe(element) {

var contentID = $wrapper.get(0).id;
var scroller = elm.find('[data-iscroll="scroller"]').get(0);
if (scroller) {
    var iscroll = new iScroll(contentID, {
        hScroll        : false,
        vScroll        : true,
        hScrollbar     : false,
        vScrollbar     : true,
        fixedScrollbar : true,
        fadeScrollbar  : false,
        hideScrollbar  : false,
        bounce         : true,
        momentum       : true,
        lockDirection  : true,
        useTransition  : true, 
        //the preceding options and their values do not have any to do with the fix
        //THE FIX:
        onBeforeScrollStart: function(e) {
            var nodeType = e.explicitOriginalTarget ? e.explicitOriginalTarget.nodeName.toLowerCase() : (e.target ? e.target.nodeName.toLowerCase():''); //get element node type
            if(nodeType !='select' && nodeType !='option' && nodeType !='input' && nodeType!='textarea') e.preventDefault(); //be have normally if clicked element is not a select, option, input, or textarea.
            else if (iscroll != null) {   //makes sure there is an instance to destory
                     iscroll.destroy(); 
                     iscroll = null;
            //when the user clicks on a form element, my previously instanced iscroll element is destroyed
            }
        },
        onScrollStart: function(e) { 
            if (iscroll == null) {  //check to see if iscroll instance was previously destoryed 
                var elm = $.mobile.activePage; //gets current active page
                var scrollIt = setTimeout( function(){ scrollMe(elm) }, 0 ); 
            } //recursively call function that re-instances iscroll element, as long as the user doesn't click on a form element
        } 
    });
    elm.data("iscroll-plugin", iscroll);
}

}

Basically all you need to do is destroy your iScroll instance when a form element is selected by the user, but before they actually start scrolling (onBeforeScrollStart) and if the user clicks on anything else within the element with the custom attribute data-iscroll="scroller", they can scroll using iScroll as usual.

<div data-role="page" id="pageNameHere" data-iscroll="enable">
Pete

Here's a really easy fix that worked for me. I noticed in the Android browsers that on initial page load I could not click on a select box but I was able to click on a text input field that I was using for Search. Then I noticed that after I clicked on the text input field, it would recognize me clicking a select box. So all I did was add this to the javascript function I was using to load the Search page...

$('#search').focus();

So when the search page gets loaded, it automatically focuses on the text input field but does not pop up the keyboard, which is exactly what I wanted. Sorry my example is not publicly accessible, but hopefully this can still help somebody.

try this solution if (e.target.nodeName.toLowerCase() == "select" || e.target.tagName.toLowerCase() == 'input' || e.target.tagName.toLowerCase() == 'textarea') {
return; }

What about, that works for me!:

$('input, select').on('touchstart', function(e) {
    e.stopPropagation();
});

Even if you've excluded form elements in onBeforeScrollStart(), there is another bug in android 2.2/2.3 browser/webview:

https://www.html5dev-software.intel.com/viewtopic.php?f=26&t=1278 https://github.com/01org/appframework/issues/104

You can't input chinese characters in input elements in the div with "-webkit-transform" css style. The iscroll 4 applied the "-webkit-transform" with the scroller div.

The solution is to keep form fields in a absolute div out of the scroller.

Android browser bug is result of very old version of WebKit inside Android, even inside Android 4.3. The root cause of bug - wrong processing of the click event that iScroll sends back to the browser (removing preventDefault just stops sending this click event) The Android Chrome browser is free from this bug because it has upgraded WebKit library inside.

Waiting for Android WebKit upgrade from Google.

Check this. This fixed my issue

https://github.com/cubiq/iscroll/issues/576

In option I have selected

click:false, preventDefaultException:{tagName:/.*/}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!