views:

483

answers:

4

Hi,

I've got the following piece of Jquery:

$("#collapse-menu > li > a").click(function() {
    $(this).toggleClass("expanded").toggleClass("collapsed").find("+ ul").slideToggle("medium");
});

What it basically does is expands or collapses a menu of nested "lists" which contain dropdowns (simplified example):

<ul id="collapse-menu">
   <li><a class="expanded">Basic Details</a>
      <ul>
         <li>
            <select> .... </select>
         </li>
         <li>
            <select> .... </select>
         </li>

The code works absolutely fine EXCEPT when a large value is selected in any of the dropdowns. At that point, when clicked, the menu will still expand/collapse correctly but "flash" quickly while doing so, as if the entire element was being reset somehow. The data itself is fine but it's the flashing that's unwanted.

The strange thing is that if a small value is selected in a dropdown, there's no flashing. When a large value is selected (say, above 30 in an age dropdown of 18-99), the flashing starts happening.

Can anyone tell me why this is happening? Or whether there's something not right about the Jquery that's causing this.

Thanks.

UPDATE:

Adding a bounty to this. Have tried a few similar plugins/solutions out there on the net but they all seem to suffer from this "flashing" problem when large dropdown values are selected by default.

If it helps, here's a typical similar solution: http://www.i-marco.nl/weblog/jquery-accordion-menu/index_collapsed.html# ... but it suffers from the same problem when dropdowns are added inside the accordion.

I hope someone has a clean solution, instead of needing to hack this somehow.

+1  A: 

I personally think autocomplete may really be the way to go. However, I had the idea that hiding the long select by substituting a fake short one would make the slide smoother. Seems to work better on the one browser I tested (Firefox 3.0.18). The code could probably be cleaned up and there is the bug that sometimes the selects won't match sizes by default due to the internal scrollbars but that shouldn't be too difficult to fake.

$("#collapse-menu > li > a").click(function() {
    var was_expanded = $(this).hasClass('expanded');
    var uls = $(this).toggleClass("expanded").toggleClass("collapsed").find('+ ul');
    if (was_expanded) {
        $(this).parent().find('select').each(function() {
            var fake = $('<select class="fake"><option>' + $(this).val() + '</option></select>');
            $(this).parent().append(fake);
            $(this).hide();
        })
        uls.slideUp('medium', function() { });
    } else {
        $('.fake').remove();
        $('select').show();
        uls.slideDown('medium', function() { });
     }
});
Rob Van Dam
Thanks but this solution fails. Unfortunately worse flashing than not adding anything at all. Autocomplete is not a suitable option in this case as I have several selects inside the accordion.
Tom
+1  A: 

I think the problem is that every time the element is resized the dropdown scrolls to the selected element, which causes the flashing.

The answer would be to save each selection in javascript (on change say), then when the animation starts to deselect the value and then restore it when the animation stops. This will give you the same performance as you get when you have a small value selected, while preserving the user's selection.

You might also want to ensure that the form cannot be submitted while it is in a transitional stage, otherwise you'll get bad values coming back.

CurtainDog
Thanks - I suspect you're right. If no better answers crop up, will test this before the deadline and report back. Theoretically, sounds like the best solution so far.
Tom
This should work, but it will require a lot of mess in the aplication and cause complications with every change in number of selects in the future development. (especially compared to a one-liner calling a jquery plugin)
naugtur
+2  A: 

From my observation: The issue seems Operating System dependent. The selects are painted by system, as they're system controls. On my Linux machine I experience no problems with blinking animation in the http://jsbin.com/ixake example. I expect You tested it on Windows (r)

It looks like the select is "scrolled" to the proper value everytime it's repainted. And that happens a lot while animating.

The easiest solution would be to substitute system selects with html-based selects to remove system dependency. There are unobtrusive jquery plugins that will do it for You.

Try this or pick any from here if that first one wasn't too good.

After that Your animation should depend only on JS and styling.

naugtur
Thanks, using some plugin is an option but would produce a lot of hassle for such a simple problem. I have approx 20 dropdowns nested (just has to be), all of which take context-specific values via the PHP form class that generates them, some that load dynamic content via ajax based on other dropdown selections, the form is postable, etc. It becomes too much to start incorporating a plugin when the problem is just an aesthetic annoyance.
Tom
You're not right. You just have to call $('select').whateverThePluginIsCalled() and it's all done. It's jquery. There is no incorporating anything. Just add the script tag and run the function in the ready event. And no difference is visible from the server
naugtur
And You will never get rid of that effect if You keep using system controls.
naugtur
Hmmm... on second look, the basic combobox does look quite simple. Will give it a try before the deadline and report back.
Tom
A working hack (if any exist) for this problem will surely be much more complicated than that. Good luck meeting the deadline.
naugtur
Tom
+1  A: 

What you can do is disable the select lists before animating the ul.

        $("#collapse-menu > li > a").click(function() {
            $(this)
                .toggleClass("expanded").toggleClass("collapsed")
                .find("+ ul").find("select").attr("disabled","disabled").end()
                .slideToggle("medium",function(){
                    $(this).find("select").each(function(){this.disabled=false;});
                });
        });

give that a try and report back.

You will probably want to style (CSS) the disabled select lists to look identical to non-disabled ones.


NOTES

The above has a side effect of not submitting the selected values when a form is submitted. If this is a problem for you then I suggest you either remove all options from the select list except for the selected option before animating and then add them all back in once animation is complete. And if that won't work for you naugtur's suggestion to use HTML-styled select lists is probably the best option.

p.s. don't bother trying to use the readonly attribute on the select list...it doesn't exist.

David Murdoch
Thanks but this solution fails. Flashing persists and now with the added "delay" of waiting for the disabled to clear. If only small values in the selects are selected, flashing disappears, suggesting this solution does not have the desired effect.
Tom
Hm, It did for me. I have no idea what you mean by "waiting for the disabled to clear"
David Murdoch
I'm thinking you should go with naugtur's suggestion of HTML select lists.
David Murdoch