harvesthq / chosen

Deprecated - Chosen is a library for making long, unwieldy select boxes more friendly.
http://harvesthq.github.io/chosen/
Other
21.85k stars 4.1k forks source link

Dropdown at the bottom of the page breaks layout #155

Open JAStanton opened 13 years ago

JAStanton commented 13 years ago

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 commented 13 years ago

+1

emilime commented 13 years ago

+1

stof commented 13 years ago

+1

adamelevate commented 13 years ago

+1

adamelevate commented 13 years ago

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

adamelevate commented 13 years ago

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

wiramax commented 12 years ago

+1

eamonnfaherty commented 12 years ago

+1

danimt commented 12 years ago

+1

This is a must-have feature

stephencarr commented 12 years ago

+1

dexcell commented 12 years ago

still not fixed yet?

kin-hong commented 12 years ago

+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 commented 12 years ago

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

coldman333 commented 12 years ago

+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 commented 12 years ago

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 commented 12 years ago

+1

coreyworrell commented 12 years ago

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 commented 12 years ago

牛逼

SuarezB commented 11 years ago

+1

sathishbabu007 commented 11 years ago

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

l-i-n-k commented 11 years ago

+1

I can’t see the fix in the examples…

PaulvdDool commented 11 years ago

I wanted to let my dropdown list 'drop up'. I fixed my issue by changing the top for .chzn-drop to the negative value of the max-height of chzn-results. Also changed max-height to just height else it produces an empty space if there aren't enough results in the drop list. It's an ugly fix I know, but it worked for me.

p.s. Changing the styling a bit is required. For example: the border will be missing on the top and needs to be removed on the bottom of the drop list.

trisweb commented 11 years ago

Please work on #1330 so this can be resolved! Ran into this bad bug in our app -- chosen is just non-functional when placed near the bottom edge of the screen.

wimglenn commented 10 years ago

+1

jeffjrare commented 10 years ago

lot of +1

xel1045 commented 10 years ago

+1

genichyar commented 10 years ago

+1

Bibendus83 commented 10 years ago

+1 anyone with a working solution yet?

BlueHotDog commented 10 years ago

+1

guys, seems like huge demand, whats up?

fenisteel commented 10 years ago

Hi!

Add this to the CSS file:

.chosen-container-active.chosen-with-dropup .chosen-single{
  -moz-border-radius-topright: 0 !important;
  border-top-right-radius: 0 !important;
  -moz-border-radius-topleft: 0 !important;
  border-top-left-radius: 0 !important;
}

.chosen-dropup {
  top: auto !important;
  bottom: 23px;
  border: solid #aaa;
  border-width: 1px 1px 0 1px;
  margin-top: -1px;
  border-radius: 4px 4px 0px 0px !important;
}

Then modify the JS file:

Find Chosen.prototype.results_hide = function() then add this:

this.container.removeClass("chosen-with-drop");
//Added by Fenistil
this.container.removeClass("chosen-with-dropup");
//End
this.form_field_jq.trigger("chosen:hiding_dropdown", {

Find Chosen.prototype.close_field = function() then add this:

this.container.removeClass("chosen-container-active");
//Added by Fenistil
this.container.removeClass("chosen-with-dropup");
//End
this.clear_backstroke();

Find Chosen.prototype.activate_field = function() then add this:

this.container.addClass("chosen-container-active");
//Added by Fenistil
var windowHeight = $(window).height() + $('html').scrollTop(),
  totalHeight  = this.dropdown.height() + Math.ceil(this.dropdown.offset().top);

if (totalHeight > windowHeight) {
  this.container.addClass("chosen-with-dropup");
  this.dropdown.addClass('chosen-dropup');
}
//End
this.active_field = true;

Find Chosen.prototype.results_show = function() then add this:

this.container.addClass("chosen-with-drop");
//Added by Fenistil
var windowHeight = $(window).height() + $('html').scrollTop(),
      totalHeight  = this.dropdown.height() + Math.ceil(this.dropdown.offset().top);

if (totalHeight > windowHeight) {
  this.container.addClass("chosen-with-dropup");
  this.dropdown.addClass('chosen-dropup');
}
//End
this.results_showing = true;
this.search_field.focus();

Greetings!

PS.: Thanks to coreyworrell, this is based on his code.

abulhasanlakhani commented 10 years ago

UPDATE:

Hey thanks for this solution @fenisteel. This works well only with one little exception. It calculates whether to drop up or down based on the window height, not the scroll position.. It is not dynamic as the bootstrap select. For example on page load, user is on the top of the page, chosen at the bottom of the page will dropup. But if the user, scroll down the page, which eventually creates more space for that bottom chosen, it should then drop down as usual

http://silviomoreto.github.io/bootstrap-select/

When you scroll the page up and down, the dropdown should open-up or down based on the new positions not the positions on page load.

dileepar commented 10 years ago

Thanks @fenisteel. It's great to see that after three years have a decent solution.

raysunqi commented 9 years ago

Why there is no dynamic solutions after all the years? I like the control very much except this problem.

mveldhuizen commented 9 years ago

Thanks @fenisteel. Your solution works great and very clear instructions on what needed to be changed.

stof commented 9 years ago

@fenisteel can you send a PR with your changes ?

rockerest commented 9 years ago

+1 to a PR from @fenisteel. I use Chosen via Bower in production, and can't edit library files in the repository.

totszwai commented 9 years ago

+1 wish this could get out asap in v1.3.1

lode commented 9 years ago

I've turned the changes by @fenisteel into a PR, see https://github.com/harvesthq/chosen/pull/2312.

peimn commented 9 years ago

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 commented 9 years ago

@peimn this not worked for me

wkerswell commented 9 years ago

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

haga2112 commented 9 years ago

+1...

uaoleg commented 9 years ago

+1

JayAdra commented 9 years ago

+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 commented 8 years ago

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

laulaz commented 8 years ago

+1

gopeter commented 8 years ago

+1

yairEO commented 8 years ago

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 commented 8 years ago

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 commented 8 years ago

@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...)