Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dropdown at the bottom of the page breaks layout #155

Open
JAStanton opened this issue Aug 4, 2011 · 60 comments
Open

Dropdown at the bottom of the page breaks layout #155

JAStanton opened this issue Aug 4, 2011 · 60 comments

Comments

@JAStanton
Copy link

If you have dropdown that's at the bottom of your page. The hidden dropdown menu is pushing the bottom of the page down. That needs to be hidden so that it doesn't effect the styles of the page, it would be nice if the drop down detected the bottom of the page, did a little math to figure out if it has room to expand or not and if it doesn't have room it will switch styles to appearing above and now bellow.

@cevarief
Copy link

cevarief commented Aug 9, 2011

+1

3 similar comments
@emilime
Copy link

emilime commented Aug 11, 2011

+1

@stof
Copy link
Collaborator

stof commented Aug 12, 2011

+1

@adamelevate
Copy link

+1

@adamelevate
Copy link

Wrote a little function into it, works perfect for pagination now
http://jsfiddle.net/adamelevate/WFejY/

@adamelevate
Copy link

Mine is driven off of a css class on the select box, so it still isn't dynamic, sry....

@wiramax
Copy link

wiramax commented Nov 14, 2011

+1

@eamonnfaherty
Copy link

+1

@danimt
Copy link

danimt commented Mar 23, 2012

+1

This is a must-have feature

@stephencarr
Copy link

+1

@dexcell
Copy link

dexcell commented Apr 5, 2012

still not fixed yet?

@kin-hong
Copy link

+1. In our case, Chosen is placed at the bottom of a modal dialog, and is causing a "phantom" vertical scrollbar to show. So need Chosen to appear above when it is at the bottom of a viewport (not just page).

@silviapfeiffer
Copy link

I've successfully avoided this by making sure that all parent elements have overflow:visible.

@coldman333
Copy link

+1
fixed on my project like this

 #selectId_chzn .chzn-drop {
    bottom: 20px;
    top: auto !important;
}

but I hope you can do this automatically

@AdrianTP
Copy link

So nobody has figured out a way to deal with this that involved intelligent edge detection? My project doesn't afford me the luxury of concretely identifying Chosen elements based on a container that is guaranteed to induce overflow. I have given a little thought to how to get Chosen to recognize if there's room to expand downward, and if not, apply a style similar to what @coldman333 suggested, and haven't figured out a great or reliable way to do it, yet. It would also need to give the dropdown list a border-top and remove its border-bottom, to keep the styles consistent. Perhaps it should just apply a special CSS class instead, using jQuery's addClass() to save processing power.

@Nemesisprime
Copy link

+1

@coreyworrell
Copy link

Okay, I've got a working solution, but it's just kinda hacked up in the compiled javascript file. I'll paste the code here though if anyone want's to create a pull request from it.

If it detects that the dropdown will extend below the window, it will resize it and add a scrollbar, but still drop DOWN. UNLESS there is less than 100px between the dropdown and the window, then it will add a chzn-above class to the dropdown and show it above the input.

/chosen/chosen.jquery.js
[around line 326]

this.search_results = this.container.find('ul.chzn-results').first();
// Start additional code
this.search_results.data('initialMaxHeight', this.search_results.css('max-height'));
// End additional code
this.search_field_scale();

[around line 568]

Chosen.prototype.results_show = function() {
  var dd_top;
  if (!this.is_multiple) {
    this.selected_item.addClass("chzn-single-with-drop");
    if (this.result_single_selected) {
      this.result_do_highlight(this.result_single_selected);
    }
  }
  dd_top = this.is_multiple ? this.container.height() : this.container.height() - 1;
  this.dropdown.css({
    "top": dd_top + "px",
    "left": 0
  });

  // Start additional code
  this.search_results.css('max-height', 'none');

  var windowHeight = $(window).height() + $('html').scrollTop(),
      dropdownHeight = this.dropdown.height(),
      dropdownTop    = Math.ceil(this.dropdown.offset().top),
      totalHeight    = dropdownHeight + dropdownTop;

  if (totalHeight > windowHeight) {
    var difference = totalHeight - windowHeight,
        height     = dropdownHeight - difference;

    if (height > 100) {
      this.search_results.css('max-height', height);
    } else {
      this.dropdown.addClass('chzn-above');
      this.search_results.css('max-height', this.search_results.data('initialMaxHeight'));
    }
  } else {
    this.search_results.css('max-height', this.search_results.data('initialMaxHeight'));
  }
  // End additional code

  this.results_showing = true;
  this.search_field.focus();
  this.search_field.val(this.search_field.val());
  return this.winnow_results();
};

/chosen/chosen.css

/* Add this rule */
.chzn-container .chzn-drop.chzn-above {
  top:auto !important;
  bottom:29px;
  border:solid #aaa;
  border-width:1px 1px 0 1px;
}

@ghost
Copy link

ghost commented Oct 10, 2012

牛逼

@SuarezB
Copy link

SuarezB commented Feb 17, 2013

+1

@sathishbabu007
Copy link

Hi,
This issue is fixed. But in the case of chzn-select-deselect option is not working for this feature. Please check this.

Thanks,
Sathish Babu

@lode
Copy link

lode commented Apr 20, 2015

I've turned the changes by @fenisteel into a PR, see #2312.

@peimn
Copy link

peimn commented Jul 8, 2015

This works for me

jQuery('.chosen-single').click(function() {
        var $chosePosition = this.getBoundingClientRect(),
            $choseDropListHeight = $(this).next().outerHeight();
        if($(this).hasClass('above')) $(this).removeClass('above');
        if (($chosePosition.bottom + $choseDropListHeight + 10) > $(window).height() && ($chosePosition.top - $choseDropListHeight) > 10) {
            $(this).next().addClass("above");
            $(this).addClass("above");
        }
        else {
            $(this).next().removeClass("above");
            $(this).removeClass("above");
        }
        $chosensingle = $(this);
        $chosenvar = this;
        $(window).bind('mousewheel DOMMouseScroll', function(event){
            $chosePosition = $chosenvar.getBoundingClientRect(),
                $choseDropListHeight = $chosensingle.next().outerHeight();
            console.log($chosePosition.bottom + $choseDropListHeight + 10, '-',$(window).height(), '-',$chosePosition.top - $choseDropListHeight);
            if (($chosePosition.bottom + $choseDropListHeight + 10) > $(window).height() && ($chosePosition.top - $choseDropListHeight) > 10) {
                $chosensingle.next().addClass("above");
                $chosensingle.addClass("above");
            }
            else {
                $chosensingle.next().removeClass("above");
                $chosensingle.removeClass("above");
            }
        });
    });

@nokian125
Copy link

@peimn this not worked for me

@wkerswell
Copy link

This was opened how long ago? why hasn't this been a priority to fix?

@haga2112
Copy link

+1...

@uaoleg
Copy link

uaoleg commented Sep 16, 2015

+1

@JayAdra
Copy link

JayAdra commented Oct 18, 2015

+1

Any ETA on this? A much needed feature. I am currently using the solution above, but having to hack it in. It'd be great if this was part of the source.

@roginator
Copy link

+1 - this has been an outstanding request for 4 years. Any chance of getting it in there?

@laulaz
Copy link

laulaz commented Jan 1, 2016

+1

1 similar comment
@gopeter
Copy link

gopeter commented Jan 14, 2016

+1

@yairEO
Copy link

yairEO commented Jan 19, 2016

I would recommend using Tether.js for positioning the dropdown, it's a much better way because it places it in the body and not where the select element itself is located, so any overflow:hidden on a parent element won't clip it. Also, Tether.js is very smart at positioning according to the viewport and you can control everything.

@Taeon
Copy link

Taeon commented Jan 20, 2016

Seems like the author isn't interested in fixing this and I don't like the idea of messing with the core files, so I came up with the below, which uses chosen's events to trigger adding/removing a 'chosen-drop-up' class where appropriate. You can include it anywhere after you've initialised your select(s).

The code is a bit verbose -- deliberately so, so that you can see what's going on.

$('#my-select').on('chosen:showing_dropdown', function(event, params) {
   var chosen_container = $( event.target ).next( '.chosen-container' );
   var dropdown = chosen_container.find( '.chosen-drop' );
   var dropdown_top = dropdown.offset().top - $(window).scrollTop();
   var dropdown_height = dropdown.height();
   var viewport_height = $(window).height();

   if ( dropdown_top + dropdown_height > viewport_height ) {
      chosen_container.addClass( 'chosen-drop-up' );
   }

});
$('#my-select').on('chosen:hiding_dropdown', function(event, params) {
   $( event.target ).next( '.chosen-container' ).removeClass( 'chosen-drop-up' );
});

I haven't posted any CSS because mine is custom so it wouldn't help anyone. But you can start with

.chosen-container.chosen-drop-up .chosen-drop{
   top: auto;
   bottom: 100%;
}

...which will put the drop-down above the input element. The rest is all about fiddling with borders and so on.

Hope this helps someone.

@claudio-silva
Copy link

@Taeon Thank you very much for your solution, it works perfectly!
This should really be merged in.
Would you submit a pull request? (one more in 80, I know, but still...)

@Taeon
Copy link

Taeon commented Jan 21, 2016

@claudio-silva : since the author has ignored all related PRs so far I'm not sure there's much point. And anyway, this code is specifically NOT intended to be merged into the core -- it's meant to work without modifying core files. That way it will still work even if I update chosen.

@bhubbard
Copy link

bhubbard commented Feb 1, 2016

+1

@nhurion
Copy link

nhurion commented Mar 8, 2016

I've implemented the solution proposed by @Taeon (well, more or less, computation is different due to application specific stuff)
Here is the css I came up with to make it looks ok. (Not a css pro here)

.chosen-container.chosen-drop-up .chosen-drop {
  border-bottom: 0;
  border-radius: 4px 4px 0 0;
  border-top: 1px solid #aaa;
  bottom: 100%;
  box-shadow: none;
  top: auto;
}

.chosen-container-active.chosen-with-drop.chosen-drop-up .chosen-single {
  background-image: -webkit-gradient(linear,50% 0,50% 100%,color-stop(40%,#eee),color-stop(90%,#fff));
  background-image: -webkit-linear-gradient(#fff 40%,#eee 90%);
  background-image: -moz-linear-gradient(#fff 40%,#eee 90%);
  background-image: -o-linear-gradient(#fff 40%,#eee 90%);
  background-image: linear-gradient(#fff 40%,#eee 90%);
  border-radius: 0 0 4px 4px;
  border-top: 0;
  box-shadow: 0 1px 0 #fff inset;
}

@yairEO
Copy link

yairEO commented Mar 10, 2016

I've improved upon my excellent peers (above this comment) with a complete solution which uses a little better JS and SCSS to achieve the important goal for countless people worldwide, the despicable maintainer is so happily ignoring:

JS

$('select').on('chosen:showing_dropdown chosen:hiding_dropdown', function(e){
    var chosen_container = $( e.target ).next( '.chosen-container' ),
        classState = e.type == 'chosen:showing_dropdown' && dropdownExceedsBottomViewport();

    function dropdownExceedsBottomViewport(){
        var dropdown        = chosen_container.find( '.chosen-drop' ),
            dropdown_top    = dropdown.offset().top - document.documentElement.scrollTop,
            dropdown_height = dropdown.height(),
            viewport_height = document.documentElement.clientHeight;

        return dropdown_top + dropdown_height > viewport_height;
    }

    chosen_container.toggleClass( 'chosen-drop-up', classState );
});

SCSS

.chosen-container {
    &.chosen-drop-up .chosen-drop {
        border-bottom: 0;
        border-radius: 4px 4px 0 0;
        border-top: 1px solid #aaa;
        bottom: 100%;
        box-shadow: 0 -4px 5px rgba(#000, .15);
        top: auto;
    }
}

.chosen-container-active {
    &.chosen-with-drop {
            .chosen-single:not(.chosen-drop-up) {
                border: 1px solid #aaa;
                border-bottom-right-radius: 0;
                border-bottom-left-radius: 0;
                //background-image:linear-gradient(#eee 20%, #fff 80%);
                //box-shadow: 0 1px 0 #fff inset;
            }
            &.chosen-drop-up{
                .chosen-drop{
                    display:flex;
                    flex-direction: column-reverse;
                }
                .chosen-single {
                    // background-image: linear-gradient(#FFF 40%, #EEE 90%);
                    border-radius: 0 0 4px 4px;
                    border-top: 0;
                    box-shadow: 0 1px 0 #FFF inset;
                    border-top-right-radius: 0;
                    border-top-left-radius: 0;
                }
            }
        }
    }
}

@teagis
Copy link

teagis commented Mar 22, 2016

@Taeon his solution works great for me
+1

@Donny-bsb-dev
Copy link

ok I had the same issue. So I fixed it, may not be the most elegant, but it worked for me. I just added a margin-top: -332px; on .chzn-drop

@stecca21
Copy link

stecca21 commented Aug 4, 2016

@Taeon Thanks for this code! It saved me a few hours or frustration :)

@mikerockett
Copy link

@yairEO - That should totally find its way into the core! Works like a charm!

@mikloshenrich
Copy link

@yairEO - Nice solution, but if we have more than one dropdown with this solution, if one of the select dropdowns is triggering an ajax reload, the "chosen-drop-up" class is not added anymore on the chosen list.

@Razoxane
Copy link

@Taeon - your solution worked a treat when I changed the ID selector to a class for multiple chosen selects on the page 👍

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.