Material: select: md-select-menu positioned above control when options are async

Created on 29 Oct 2015  路  35Comments  路  Source: angular/material

I'm using the async variant of md-select. Instead of the menu appearing directly over the md-select it appears a few pixels above. The bug only occurs if it contains only a few elements.

screen shot 2015-10-29 at 18 37 00

This bug seems to be related to my layout, am I using the containers wrong or is this a bug?

I've created a codepen http://codepen.io/anon/pen/VvdZYx

- Lots of Comments minor fixed

Most helpful comment

+1
My work around for opening below the input is:

$provide.decorator('$mdSelect',['$delegate','$timeout','$interval',          function($delegate,$timeout,$interval) {
            var origShow = $delegate.show;
            var stop;
            $delegate.show = function(){
                var onShow = origShow.apply(this, arguments);
                onShow.then(function(){
                    $interval.cancel(stop);
                    stop = undefined;
                });
                var selectInput = arguments[0].target;
                var selectDropDown = arguments[0].element;
                //angular.element(selectDropDown[0]).find('md-select-menu')[0].style.display = 'none';
                $timeout(function(){
                    var boundings = selectInput.getBoundingClientRect();
                    selectDropDown[0].style.left = boundings.left + "px" ;
                    selectDropDown[0].style.top = (boundings.top + boundings.height) + "px";
                    var dropDownBoundings = selectDropDown[0].getBoundingClientRect();
                    if(dropDownBoundings.width < boundings.width){
                        selectDropDown[0].style.width = boundings.width + "px";
                    }
                    //angular.element(selectDropDown[0]).find('md-select-menu')[0].style.display = '';
                    stop = $interval(function(){
                        selectDropDown[0].style.left = boundings.left + "px" ;
                        selectDropDown[0].style.top = (boundings.top + boundings.height) + "px";
                    },13);
                },60);
                return onShow;
            };

            return $delegate;
        }]);

All 35 comments

Did you tried it outside of code pen?
If we change the body element in your example to div it's working
If you didn't tried it outside please check, i'm sure it's working.

Yep, the issue comes from the app I'm currently developing. I tried to replicate the problem inside Codepen to make the issue more feasible.

The actual code looks like that:

<body layout="column">
  <stm-navbar></stm-navbar>
  <div layout="row" flex>
    <!-- Sidebar -->
    <stm-sidenav layout="column"></stm-sidenav>
    <!-- Content container -->
    <div layout="column" class="ui-view-container" flex style="overflow: auto">
      <md-progress-linear ng-show="$appLoading" md-mode="indeterminate"></md-progress-linear>
      <stm-breadcrumbs></stm-breadcrumbs>
      <div ui-view></div>
    </div>
  </div>
</body>

It seems like the issue is not related to a certain layout or browser:

Chrome:

chrome_1
chrome_2

Firefox:

firefox_1
firefox_2

Here is the example code used: https://gist.github.com/fridolin-koch/2c9fc5b24a5f558a9ca1

It's odd, I had a look on this but nothing formal yet, I'm on it.

@EladBezalel assigning to you only because you claimed it. Feel free to kick it back to me if you're no longer investigating

I have this issue too .
@fridolin-koch like you say it only happen when have few of options in my case is two .
I use md-select in my md-tabs .

sample image
screen shot 2558-11-13 at 1 50 00 pm

but if i have more options it will render collectly
screen shot 2558-11-13 at 1 55 53 pm

Similar problem here... The options are just floating on top of the page, no matter where the md-select is, no matter how many options, no matter whether async or not... (rc3)

@rschmukler can you take a look at this also?

I'm having trouble recreating this issue. Using the code from @fridolin-koch I made this demo which appears to be working. Can you guys try on master and confirm that this issue still exists? If it does, can someone please supply me with a demo that I can work from

@rschmukler when upgrading your codepen to angular 1.4.7 (which is what he's using in his demo) it's seems to be broken..

The behaviour slightly changed in _v1.0.0-rc5_ using angular.js _1.4.8_. Notice the options are less detached from the control.

v1.0.0-rc5: http://codepen.io/fridolin-koch/pen/QjXOxg
v1.0.0-rc4-master-5e5e6cd: http://codepen.io/fridolin-koch/pen/dYBZKB

this or some variation of it occurs on the doc site. in Demo of Basic Address select of State
the first selection positions the options correctly
subsequent attempts at selection position the options at various vertical positions relative to top of something depending on how far down the option list is currently selected.

if the md-select element is lower on the page, the behavior is even more bizarre

FWIW, in trying to research the code, I discovered that a horizontal resize causes the option list to be placed where intended.

in function calculateMenuPositions(scope, element, opts) {
this calculation
transformOrigin = (centeredRect.left + targetRect.width / 2) + 'px ' +
(centeredRect.top + centeredRect.height / 2 - contentNode.scrollTop) + 'px 0px';

uses contentNode.scrollTop which is always zero when selecting (re-opening) the select menu after it has been populated because contentNode is a hidden element appended at the bottom of the body

I haven't figured out what the calculations are trying to do but there is some never dead code in this function which doesn't make it easier.

@rschmukler @jelbourn
Well, other code considerations aside, the issue seems to be that isScrollable is not set properly. I moved it and the other line to immediately after all the initial variable settings, as follows

  // give the container some shape befor depending on it
  containerNode.style.display = 'block';
  var isScrollable = contentNode.scrollHeight > contentNode.offsetHeight;

Seems to work as expected now. I'll continue testing in various window sizes and let you know if I find anything else.

I confirm with 1.0.0 I am getting the same issue (codepen with Scooby Doo above) in my app.

Recap:

  • with an horizontal resize causes the option list to be placed where intended
  • setting ng-selected="$index == 0" the option list is ok

Any workaround?

(my md-select snippet seems quite standard, I hope)

        <md-input-container>
            <md-select ng-model="vm.selectedGranularity" aria-label="Granularity"
                ng-change="vm.changeChart(vm.selectedInstrument, vm.selectedGranularity)">
                <md-option ng-value="granularity"
                    ng-selected="granularity == vm.selectedGranularity || $index == 0"
                    ng-repeat="granularity in vm.granularities">
                    {{ granularity }}
                </md-option>
            </md-select>
        </md-input-container>

In Material 1.0.1, my use case is fixed. Thanks!

I'cant confirm this, in 1.0.1 the behaviour is still different from "regular" selects:

http://codepen.io/fridolin-koch/pen/ZQOpXN

untitled

I'm also having this issue, and only on async selects that already have a selected option. I'm using v1.0.4

+1

+1

+1

+1
My work around for opening below the input is:

$provide.decorator('$mdSelect',['$delegate','$timeout','$interval',          function($delegate,$timeout,$interval) {
            var origShow = $delegate.show;
            var stop;
            $delegate.show = function(){
                var onShow = origShow.apply(this, arguments);
                onShow.then(function(){
                    $interval.cancel(stop);
                    stop = undefined;
                });
                var selectInput = arguments[0].target;
                var selectDropDown = arguments[0].element;
                //angular.element(selectDropDown[0]).find('md-select-menu')[0].style.display = 'none';
                $timeout(function(){
                    var boundings = selectInput.getBoundingClientRect();
                    selectDropDown[0].style.left = boundings.left + "px" ;
                    selectDropDown[0].style.top = (boundings.top + boundings.height) + "px";
                    var dropDownBoundings = selectDropDown[0].getBoundingClientRect();
                    if(dropDownBoundings.width < boundings.width){
                        selectDropDown[0].style.width = boundings.width + "px";
                    }
                    //angular.element(selectDropDown[0]).find('md-select-menu')[0].style.display = '';
                    stop = $interval(function(){
                        selectDropDown[0].style.left = boundings.left + "px" ;
                        selectDropDown[0].style.top = (boundings.top + boundings.height) + "px";
                    },13);
                },60);
                return onShow;
            };

            return $delegate;
        }]);

The issue still seems to be persist in 1.0.5. The dropdowns are pretty inconsistent.

Can confirm this issue, it seems stable when it's about 4-5 items but when it's 10+ it shoots up to the top

Issue exists in 1.0.7 after loading items from async call. .md-select-menu-container breaks outside of container and spans entire page.

The issue still exists in latest master version. It makes select options unclickable due to the wrong positioning.

@ThomasBurleson Is this issue open to PR?

In version 1.0.6 i have the same problem, there is way to fix this ?
image

I have the same issue in angular-material v1.1.1, it appears only when I scroll the window to bottom. suggest any fix.

Same issue here on v1.1.1 as @haneefatel. If i put a md-menu inside of a dialog box, it jumps out of place if i scroll to the bottom of the list to select something

Edit: I decided to revert back to regular <select></select> elements for dialog boxes only and used this Codepen to keep it looking nice: http://codepen.io/bephf/pen/ogNBYW

@haneefatel Had the same issue, Modified macdasi provider so it forces dropdown go upward when it's close to bottom of the page.

+1

+1

till the day

This seems to be working fine in 1.1.7 (and possibly a few versions prior to that). Here's a CodePen demo of that.

Was this page helpful?
0 / 5 - 0 ratings