2

I'm sitting over a snippet of code, which is superslow on mobile devices.

I have an entry form with about 40 input elements (buttons) in Jquery Mobile. In Jquery Mobile this will mean:

<div class="ui-btn" data-disabled="false">
    <span class="ui-btn-inner">
        <span class="ui-btn-text">Button</span>
    </span>
    <input type="button" value="Button" class="ui-btn-hidden" data-disabled="false">
</div>

Normally, I can just bind to the input, which works ok on all PC browsers, but once I switch to mobile and the number of input elements grows, the buttons go dead.

As a workaround, I'm now binding to the closest ui-btn, like this:

el.jqmData('bound', true )
    .find('input')
    .closest('.ui-btn')
        .on('vclick', function(){
            // do stuff
        });

Which at least makes the element clickable, but still it's superslow.

I have been looking at jsPerf examples comparing closest to native Javascript methods (like here). If I assume a performance difference of 1:10 (like here when switching from desktop to mobile, I'm pretty sure my 'closest' calls are slowing things down.

My question:
Is there any way to replace closest in my chained call with a native Javascript method? If so, how would this look like?

EDIT
I have pulled out the function in question. This is what I'm doing:

// this gets called once for the size entry box 
// sizeEntryBox is the wrapper for $('sizeChart'), which contains the 60 inputs
addSizeHandlers = function( el ){
    el.jqmData('boundStyle', true)
            .find('.sizeChart')
            .on('vclick', '.entry .ui-btn', function(){

                // since I'm listening for .ui-btn I need to get the child input again                
                var qty = $( this ).find('input.qtyInput'),
                    // user can add +1/+2/+4
                    howMany = qty.closest('.ui-collapsible').find('.buttonBar input[name="radio_add"]:checked').val(),
                    qid = qty.attr('id').replace( /qty/, "" );

                // a button may display a preset qty, clicking once replaces this with
                // +1/+2/+4, clicking again adds +1/+2/+4
                if ( qty.jqmData('flag') === false  ){
                    qty.jqmData('flag',true)
                    newVal = parseFloat( howMany );
                    // set new value, add red borders
                    qty.val( howMany ).prev('.ui-btn-inner').addClass("brR").find('.ui-btn-text').html( howMany );
                    $('input[name=menge'+qid+']').val( howMany );
                } else { 
                    newVal = parseFloat( qty.val() ) + parseFloat( howMany );
                    qty.val( newVal ).prev('.ui-btn-inner').addClass("brR").find('.ui-btn-text').html( newVal );
                    $('input[name=menge'+qid+']').val( newVal );
                }
            });
        },

So I'm already binding to the whole chart and delegating to the button. Still this takes forever on mobile devices, so some performance drainers jump to someones eye, please let me know.

Thanks again!

frequent
  • 27,643
  • 59
  • 181
  • 333
  • *"...any way to replace `closest` in my chained call"* Are you saying that you still want it chained? – I Hate Lazy Nov 18 '12 at 19:45
  • That test suite is flawed: `.parent('.end')` and `.closest('.end')` not only traverse parents, but also match each parent against a selector. The native test cases perform no tests on the parents at all, so their performance cannot be compared directly against `.parent('.end')` and `.closest('.end')`. – lanzz Nov 18 '12 at 19:45
  • I'd opt to not chain, if that increase performance. – frequent Nov 18 '12 at 19:46
  • @lanzz: ah. I admint of being not very familiar with jsPerf, but you are right about the selector. – frequent Nov 18 '12 at 19:47
  • Try measuring if it's really `closest` that's causing the problems. How many elements do you have? On my laptop/Chrome I get tens of thousands of operations per second. – Matt Zeunert Nov 18 '12 at 19:49
  • @Matt: The maximum I had was 60 input buttons with the above markup, so that would be 60x closest(). If you look at the 2nd example I linked to which compared `for` vs `each` including iPhone and iPad you see that on these you only get about 1/10 of the operations per second compared to PC/laptop. So if closest is anywhere near, I'd rather replace it with something faster. Also, checking the Jquery Mobile source code, whenever there's something called frequently, they opt not to use closest(). – frequent Nov 18 '12 at 19:54
  • For what number of elements does it still work? Do the problems start when an affected button is clicked or when the page is loaded? Can you see if CPU usage is high in the phone's task manager? – Matt Zeunert Nov 18 '12 at 19:58
  • @Matt, binding directly to the `input`, the buttons don't work at all on iPhone/iPad. Binding to `clsoest(".ui-btn")` will make them work, but if you click on a button, it takes anywhere from 1-2 seconds until I get an alert placed on it. CPU usage... I wish I'd know where to check that, but I'm not getting any `javascript execution exceeded timeout` errors, so I guess I'm within limits. – frequent Nov 18 '12 at 20:05

1 Answers1

3

I'd suggest to use event delegation, and only set one click event for ALL your inputs, rather that one click event per input:

For more details, see delegated events in the jQuery .on() documentation. Your code should look like this:

$(myForm).on("vclick", "input.ui-btn-hidden", function(event){...});

or if you bind to the divs:

$(myForm).on("vclick", "div.ui-btn", function(event){...});
Christophe
  • 27,383
  • 28
  • 97
  • 140
  • @FelixKling that's not what I understand, each input seems to have its own div wrapper. – Christophe Nov 18 '12 at 20:05
  • Ok. Will try that and repost what happens. Thanks so far. – frequent Nov 18 '12 at 20:18
  • @frequent your second snippet is delegating, not the first one. I am a little bit confused... – Christophe Nov 18 '12 at 20:43
  • @Christophe: well we have been drifting off a little bit. My inital question was whether I could replace `closest` with `parentNode`, for which I kept the code (I admit) a little short... the 2nd snippet is my actual code. – frequent Nov 18 '12 at 20:46
  • @frequent right, sometimes the right answer involves getting back to the true objective. In your case you could use parent() instead of closest(), but delegation bypasses this issue. – Christophe Nov 18 '12 at 20:49
  • btw based on your last comment "it takes anywhere from 1-2 seconds until I get an alert placed on it" I am afraid the binding is not the issue. Still better to use delagation anyway. – Christophe Nov 18 '12 at 20:52
  • @Christophe: I have been sitting on these dead buttons for quite some time... at least they are not dead anymore, but still feel zoombie-ishly slow. – frequent Nov 18 '12 at 20:55