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

Chosen gets "cut" when placed in area with "overflow:hidden". #86

Open ethaniel opened 13 years ago

ethaniel commented 13 years ago

I have a div with a form. The div has "overflow:hidden" option in the css. When Chosen gets created, and it goes below the bottom line of the div, it gets cut.

Here is a screenshot:

Imgur

reconbot commented 13 years ago

This is the same as issue #59

dfischer commented 13 years ago

Isn't this unavoidable since the container element, is well, "overflow :hidden" that's just how CSS works. The only way around this is to render the drop down outside of the container and then use absolute positioning.

johnnyfreeman commented 13 years ago

Yeah, I agree with dfischer. Doesn't seem like a Chosen issue. By definition, the overflow property specifies what happens if content overflows an element's boundaries. If you don't want it the overflow to be hidden, then change that property.

reconbot commented 13 years ago

While true, I think this is a common use case with dialog boxes. (Especially jquery dialog)

dfischer commented 13 years ago

It is a common use case, however based on this ticket, like I previously mentioned... the entire structure and implementation has to change if you want to accomplish this.

You are required to render outside of the container for this to happen and then use jQuery/JS magic to figure out the positioning relative to the trigger.

jQuery Dialog's case doesn't depend upon a container element so it just uses positioning based on center dimensions and width of the dialog.

dfischer commented 13 years ago

This is more of a CSS issue, not a Chosen issue. Don't put overflow: hidden on the container. You're going to have to use another technique to clear the floats below it if that's what you're doing.

medelbrock commented 13 years ago

I just wrote some code which I think does what you're looking for:

$.fn.extend({ chosen: function (data, options) { if ($(this).parent().css("overflow") == "hidden") { //get the offsets between parent and child to calculate the diff //when we push to absolute var y = $(this).offset().top - $(this).parent().offset().top, x = $(this).offset().left - $(this).parent().offset().left, $t1 = $("<div/>", { css: { "position": "relative", "height": $(this).parent().height, "width": $(this).parent().width } }), $t2 = $("<div/>", { css: { "position": "absolute", "top": y, "left": x } }); $t1.insertBefore($(this).parent()); $(this).parent().appendTo($t1); $t2.appendTo($t1); $(this).appendTo($t2); } return $(this).each(function (input_field) { if (!($(this)).hasClass("chzn-done")) { return new Chosen(this, data, options); } }); } });

(btw I'm new to github as of today so if there is a different place where I should be posting code let me know)

That code effectively does the following:

  1. calculate the offset between the select box and the parent.
  2. create a parent div with the same dimensions as your overflow hidden and set to relative.
  3. create a parent for the select box creating the element as absolute using the x and y offsets from step 1.
  4. insert new parent before the select's parent, append the overflow div inside of the new parent, append that new select parent inside of the master parent, append the select to the new child parent.
tompaton commented 13 years ago

Interesting idea Matthew, but I think that's not going to work in a jQuery-UI dialog is it?

It's more likely that the dropdown element (.chzn-drop) needs to be pulled out of the .chzn-container and up to the document body...

medelbrock commented 13 years ago

Just add a style to the page and your answer is yes:

.ui-dialog{ overflow:visible; }

ethaniel commented 13 years ago

Doesn't work for me, since I create my pages using div's as columns (as opposed to td's and tables). I use "overflow:hidden" to stop runaway content from ruining neighbour divs.

I suppose many of you use the same structure.

tompaton commented 13 years ago

That won't work, as the dialogs are set to overflow:auto so they can have scrollbars if necessary.

On Fri, Jul 29, 2011 at 11:47 PM, ethaniel reply@reply.github.com wrote:

Doesn't work for me, since I create my pages using div's as columns (as opposed to td's and tables). I use "overflow:hidden" to stop runaway content from ruining neighbour divs.

I suppose many of you use the same structure.

Reply to this email directly or view it on GitHub: https://github.com/harvesthq/chosen/issues/86#issuecomment-1681303

medelbrock commented 13 years ago

Before we get any further into this conversation, what I've done is a theoretical fix. It can't be applied to all scenarios because all it takes is one element having a counter-attribute to screw it up. The above solution with the overflow visible will only work with dialog option resizable:false. Why would you need to have a select overflow outside of a dialog?

tompaton commented 13 years ago

Ok, here are a bunch of screenshots which hopefully clarify the issue.

http://i.imgur.com/9ZY9O.png http://i.imgur.com/c2PLo.png http://i.imgur.com/1oqZ7.png http://i.imgur.com/ZBrQj.png

Screenshots 1 & 2 show how it behaves, there isn't enough space for the dropdown "inside" the dialog, so it scrolls and is hidden as a result. This is very clunky to use and ugly and not how the built in select controls work (they show over the top of the content, no matter how deep inside it they are or what it's overflow style is.)

Screenshots 3 & 4 show my current work-around, which is to put a whole heap of space below the dropdown. This is also ugly and makes the form harder to use.

So, to summarise, the real point is that native select controls aren't restricted to their parent container and if it's possible, the chosen enhanced select control shouldn't be either, since it's designed to be a drop-in replacement for the native control.

medelbrock commented 13 years ago

This theoretically can be done but it would require a completely different construction/positioning system by the chosen plugin. This change would be extensive and require about a 30% rewrite of the current plugin. I consider this a feature request and not a bug. All browsers have a default render for select elements.Chosen replicates it using divs but is not treated like a select element.

dfischer commented 13 years ago

Like I said, the only way to do this is absolute positioning OUTSIDE of the triggered element. There's no other way. Correct on @medelbrock

reconbot commented 13 years ago

While it would be very nice to have chosen behave like a browser widget, I agree this should be considered a feature not a bug. Can we currently set a max height?

tompaton commented 13 years ago

@veloper: I gave that a go (as best I could) and it didn't seem to help. I can't see how it would work either, as the div.chzn-drop is still contained in the dialog, so will be clipped regardless of whether it is absolutely positioned or not.

The only way to stop that is going to be to pull the div.chzn-drop out of the dialog and make it a child of <body>, but as everyone is pointing out, that is a bigger change than anyone is willing to get their hands dirty trying.

tompaton commented 13 years ago

Unfortunately using overflow:visible isn't an option, the dialog needs to be scrollable if necessary.

chris-herring commented 13 years ago

I think I've solved the issue by setting the drop down position to absolute and whenever showing the drop down setting it's position. e.g. Changing (editing the javascript)

this.dropdown.css({
    "top": dd_top + "px",
    "left": 0
});

to

var parent = this.dropdown.parent();
var offset = parent.offset();
dd_top = offset.top + parent.outerHeight();
dd_width = parent.width();
this.dropdown.css({
    "top": dd_top + "px",
    "left": offset.left,
    "width": dd_width + "px"
});
Polemarchus commented 13 years ago

I'm running into this problem myself while trying to make Chosen work inside a Wijmo / jQuery UI Dialog. I may be way out of my league here, but isn't there a fairly simple fix of making the "dropdown" portion have "display: none" while hidden, and then "display: block" when shown? You'd still incur the scroll bars when the dropdown is open, but that's probably better than having the scroll bars always present.

levushka commented 13 years ago

The fix (tested in Chrome 13, Firefox 7 and IE 9) :

In chosen.css:

.chzn-container .chzn-drop { position: fixed; }

and in Chosen.prototype.results_show

var offset = this.container.offset(); this.dropdown.css({ "top": (offset.top+dd_top) + "px", "left": offset.left + "px", "display": "block" });

http://img30.imageshack.us/img30/4094/chosen3.png

tompaton commented 13 years ago

That's the stuff, good thinking @levushka. Resolved now in my branch: https://github.com/tompaton/chosen/commit/fda07051161f3fffe6049362b6c9b66ffbe857d1

tompaton commented 13 years ago

Ok, it's a little more complicated than that. The above fix didn't work if the page was scrolled.

I've updated my repo with a changeset that sort of fixes it, the dropdown is positioned correctly now, but if the page is scrolled while the dropdown is visible it doesn't move as expected. That's annoying, but not a show stopper.

jokeyrhyme commented 13 years ago

iOS 5 might solve this, but iOS currently doesn't support position: fixed in the same way that desktop browsers do. Chosen works terrifically on the desktop, but this might limit its uses on poorly designed mobile browsers...

I'm not just picking on Apple here, older version of Android also lack support for position: fixed.

jokeyrhyme commented 13 years ago

As of jQuery 1.7 (not yet released, but in release candidate) they have a built-in test for CSS Position Fixed support: http://bugs.jquery.com/ticket/6809

Might be worth detecting jQuery 1.7 and using their result, or copying their code and getting a separate result otherwise.

fluxsaas commented 13 years ago

+1 for position at the end of body element.

check the jquery widget "autocomplete". pretty good solution for that problem:

http://jqueryui.com/demos/autocomplete/

great plugin by the way.

frostyoni commented 12 years ago

I had this issue mostly in webkit browsers. IE just goes mental with chosen, so i disabled it if ie. Using jquery, i check if the browser is a webkit one, and adjust the overflow of the div containing my chosens like so:

if($.browser.webkit) $(this).css("overflow",($(this).is(":visible"))?"visible":"hidden");

where this is the div containing the chosens.

JonoB commented 12 years ago

+1 @levushka and @tompaton Not perfect, but good enough-ish. Barely :)

Evandar276 commented 12 years ago

@levushka solution is great, but it is global ... so at window.top when you scroll down and click to chosen, it roll down in wrong position. So here is solutuion for normal pages and popup windows

in Chosen.prototype.results_show

replace:

  this.dropdown.css({
    "top": dd_top + "px",
    "left": 0
  });

with:

if($('.popup').length!=0) {

  var offset = this.container.offset();
  this.dropdown.css({
    "top": (offset.top+dd_top) + "px",
    "left": offset.left + "px",
    "display": "block"
  });

} else {

  this.dropdown.css({
    "top": dd_top + "px",
    "left": 0
  });

}

CSS:

   .popup .chzn-container .chzn-drop { position:fixed; }

If you are using jQuery UI or something else, you can have problems with z-index, so set chosen z-index to higher values (jQuery UI is it 1010+)

Now it is wokring in FF and for Chrome you need:

     .popup { overflow-x:visible; overflow-y:visible; }

lover elements can have overlow:hidden with no problems.

pohnean commented 12 years ago

Why not render the dropdown in the body and use Jquery UI's position function to position the box? http://jqueryui.com/demos/position/. This is the standard method used for many jquery ui widgets.

$( ".czn-drop" ).position({ of: $( "#container" ), my: "left top", at: "left bottom", });

noir2501 commented 12 years ago

Forgive me for barging in, I had the same problem with displaying chosen drop down in a jQueryUI dialog. I tried the usual stuff with overflow but this was messing my other dialogs.

What I did instead, was to set my dialog to a fixed height and set the chosen drop down to a fixed height as well. It would be nice to have an option to do this from chosen's options instead of css.

.chzn-drop { overflow: auto; max-height: 75px; }

Maybe this helps someone else.

aL3xa commented 12 years ago

Excuse-moi for a quite n00bish/RTFM question, but how can I render the dropdown outside the container (say, on body)? Right now, the only thing that comes to my mind is to fork the plugin and shove that piece of code into it. There must be a more elegant way of achieving this.

PilotBob commented 12 years ago

Is there an event the choosen fires when the drop down is open/closed. If so, I can just change the overflow setting of the parents divs to visible and then back. I tried that with:

$(".chzn-drop").show(0, function () { 
   $(this).parents("div").css("overflow", "visible");
});    

And, yea, it worked. But, then when I added:

$(".chzn-drop").hide(0, function () {
   $(this).parents("div").css("overflow", "");
});

It no longer worked. I assume because I have several choosen controls on the form. If I could add this code inside an open/close event then I think it would get me where I want to go.

stof commented 12 years ago

You can listen to the liszt:showing_dropdown and liszt:hiding_dropdown dispatched on the original select element

PilotBob commented 12 years ago

Excellent... so this solved my issue:

$(".chzn-select").on("liszt:showing_dropdown", function () { $(this).parents("div").css("overflow", "visible"); }); $(".chzn-select").on("liszt:hiding_dropdown", function () { $(this).parents("div").css("overflow", ""); });

Of course, it may not work with all layouts.

Thanks... BOb

gil commented 12 years ago

I've forked the code to make the dropdown child of <body/>. I didn't test it enough yet, but it's working here perfectly -> https://github.com/gil/chosen

Can you see if it solves your problem, @tompaton? I think I just have to detect scrolling now, to fix the position manually.

geoffweatherall commented 12 years ago

The fork fixed the display issue for me, and worked well at the bottom of the page where it limited the height it displayed to something reasonable. However, the fork stopped the multi-select functionality from working.

gil commented 12 years ago

@geoffweatherall Really? Nice! :D About the problem with multi-selection, what stopped working? Could you please try the example.jquery.html file and tell me if it's not working? For me it seems to be okay.

geoffweatherall commented 12 years ago

Yes, examples work fine (FF 12).

The single select example is in a fairly plain html page with "panels" that use overflow hidden. However, the multi select example is in a "dialog" created using colorbox (http://www.jacklmoore.com/colorbox) - don't ask me why, the project was like that when I arrived. I guess it might have something to do with that, I will look into it.

geoffweatherall commented 12 years ago

The fork fixes the cut-off issue in a number of locations in our application, including multi-selects that are not nested within colorboxes (http://www.jacklmoore.com/colorbox). I believe the issue with colorbox is no fault of chosen. Great fork :-)

geoffweatherall commented 12 years ago

Actually I found an issue with the fork that shows in the sample page (example.jquery.html). For the multi select control, if you click in the control to open the list of options, and then use the up arrow and down arrow keys to navigate the list, the list of options makes a surprise move to the top of the page. This issue does not occur for single selects. Tested in FF12, Chrome 18 and IE8.

gil commented 12 years ago

@geoffweatherall Maybe the problem with the colorbox is that it's being rendered over the Chosen dropdown. Try something like this to see if it helps:

.chzn-drop {
    z-index: 999999 !important;
}

Or try setting the colorbox opacity to 0.1 to check if you can see the dropdown bellow the colorbox.

The other problem, with the keys, I'll try to find some solution. I didn't use the multi-selection in the project I'm working now, so I almost didn't test it. Focused more on the single-selection option.

PilotBob commented 12 years ago

Ok, I tried this and I did get the drop downs visible, however the position is wrong. They are dropping down below the text box by quite a bit. Here is a jing showing the behavior. http://screencast.com/t/c4PCIHC176RX

pruimmartin commented 12 years ago

Hi Gil,

I've tried your version and it's working! Thank you so much! There's only one small thing that stopped working.. It's the disable_search_threshold option. The search field won't dissapear.

Have you got any idea?

Thanks!

pruimmartin commented 12 years ago

Nevermind! I managed to fix it myself, however.. maybe you should change that in the code aswell:

Change:

    if (this.form_field.options.length <= this.disable_search_threshold) {
      this.container.addClass("chzn-container-single-nosearch");
    } else {
      this.container.removeClass("chzn-container-single-nosearch");
    }

To

    if (this.form_field.options.length <= this.disable_search_threshold) {
      this.dropdown.addClass("chzn-container-single-nosearch");
    } else {
      this.dropdown.removeClass("chzn-container-single-nosearch");
    }
PilotBob commented 12 years ago

What's the status of this fork? Has it be merged into master? Or is it still in testing?

PilotBob commented 12 years ago

One other issue.. the fork seems to work will for me, but in firefox I see the dropdown div at the bottom of the page. It seems that the negative left position doesn't move it out of the display area. Perhaps it is due to some other styles I have... thought. Any idea? Why isn't it's display being set to none?

tompaton commented 12 years ago

I made a pull request a long time ago for my original fix but it was never accepted (no idea why).

There have been a lot of additional tweaks suggested in this thread since then, I haven't played with any of them yet, and haven't checked to see if someone has a fork which incorporates them (if there isn't one, someone should create one, and if there is, that would be a good answer to your question.)

On Thu, Jun 7, 2012 at 6:44 AM, Bob Archer reply@reply.github.com wrote:

One other issue.. the fork seems to work will for me, but in firefox I see the dropdown div at the bottom of the page. It seems that the negative left position doesn't move it out of the display area. Perhaps it is due to some other styles I have... thought. Any idea? Why isn't it's display being set to none?


Reply to this email directly or view it on GitHub: https://github.com/harvesthq/chosen/issues/86#issuecomment-6162299

kaoskitn commented 12 years ago

I tried them all, and none of them worked for me, because I have multiple fieldsets that are hidden and a long form that scrolls. So, my solution was to make the dropdown static and hidden initially, and shown when opened, which stretches the parent container. This works in all of my forms, even if it's at the very bottom of the screen.

in Chosen.prototype.results_show:

      this.dropdown.css({
        "top": dd_top + "px",
        "left": 0,
        "display": "block"
      });

In Chosen.prototype.results_hide:

      this.dropdown.css({
        "left": "-9000px",
        "display": "none"
      });

in chosen.css:

.chzn-container .chzn-drop {
  position: static;
  display: none;
}
Twipped commented 12 years ago

@gil, I just want to say thank you for your fork, I was about to give up on using Chosen for my project because of this bug.

I'm honestly quite surprised that the harvest guys don't see the flaw inherent in binding a floating popup to a constrained container. Even regardless of overflow rules, that's just not a good idea for a public library that has no idea what people may be doing around the widget.