Ember-power-select: Select box can get into undefined state when opened while still closing

Created on 19 Jan 2017  路  10Comments  路  Source: cibernox/ember-power-select

It is possible to get select box into undefined state by clicking on it when it is closing (like double click on opened selectbox). This closes and opens select box correctly but position class is missing as you can see on screenshot. Super fast double click is not always required - any factor that affect browser speed will make a difference like cpus that are under a load, old browsers, large amount of items,...

power-select-bug

Most helpful comment

Hi, I've also been looking into this issue and I found out a few things. It seems that the problem is not actually in ember-power-select, but in ember-basic-dropdown. Although the problem seems to be easier to replicate with ember-power-select, I've been able to replicate it on the ember-basic-dropdown homepage (see http://i.imgur.com/JqJEnU4.png) with _very_ fast clicking.

Now usually when opening the dropdown, the following happens:

  • basic-dropdown#open() sets isOpen to true via a call to basic-dropdown#updateState()
  • This changes the publicAPI property, which fires content#didReceiveAttrs()
  • didReceiveAttrs sees that it was not open previously but it's open now, so it schedules content#open() to run
  • content#open() runs and in turn calls basic-dropdown#reposition(), which calls basic-dropdown#applyReposition() which finally sets the position and size of the dropdown

Now when closing a dropdown, the following happens:

  • basic-dropdown#close() first sets hPosition, vPosition, top, left, right, width and height to null and then it sets isOpen to false via a call to basic-dropdown#updateState()
  • Both these changes casue content#didReceiveAttrs() to fire
  • didReceiveAttrs sees that it was open and it's not open now and closes itself

This works fine, the problem seems to happen when the dropdown is closed and then opened again in a very short span of time, so that it all happens during a single run or something like that.

In that case the following happens:

  • basic-dropdown#close correctly sets isOpen to false and clears all the attributes
  • basic-dropdown#open() correctly sets isOpen to true
  • These changes cause content#didReceiveAttrs() to fire, which sees that it was and is open and therefore it doesn't have to do anything. This is clearly wrong, because all the positions and sizes have been se to null, which causes the dropdown to render in the top left.

A simple fix to this issue is to schedule content#open() not only when the state has changed from closed to open, but also when the dropdown is open and all the top, left, etc. properties are set to null, but it's not a very pretty solution.

@cibernox What is your opinion on this?

All 10 comments

What versions are you using (and browser)?

I tried to reproduce it and I couldn't.

I didn't find browser and version(in reasonable range) where this does not happen.

Browsers

  • Chromium 57.0.2969
  • Chrome 55.0.2883.87
  • Firefox 50.1.0
  • Opera 42.0
  • Internet Explorer 11.0.9600.18537

Versions

  • current website
  • v1.2.0
  • v1.0.0
  • v1.0.0.beta-any

You won't be able to do it on that website with normal(200ms long) doubleclick. The website is really lightweight so it will be already fully closed by the time second click hits. I am able to break it there with mouse click + touchpad click.

I took me a while but I was able to reproduce in the Select of strings with value tracking on http://www.ember-power-select.com/legacy-demo which was 300 options if I throtthe the CPU 10x, but only a small fraction of the times I try so.

I'm unsure how create a consistent reproduction of this problem so I track it down. I suspect that this has to do with the ghost clone of the dropdown that I create for animate the closing of the dropdown. I'll check if adding animations makes this easier to reproduce.

Uf, I am happy that you were able to finally reproduce it. I initially wanted to create javascript code that would do it but I was not able to find working selectors for it.

Hi, I've also been looking into this issue and I found out a few things. It seems that the problem is not actually in ember-power-select, but in ember-basic-dropdown. Although the problem seems to be easier to replicate with ember-power-select, I've been able to replicate it on the ember-basic-dropdown homepage (see http://i.imgur.com/JqJEnU4.png) with _very_ fast clicking.

Now usually when opening the dropdown, the following happens:

  • basic-dropdown#open() sets isOpen to true via a call to basic-dropdown#updateState()
  • This changes the publicAPI property, which fires content#didReceiveAttrs()
  • didReceiveAttrs sees that it was not open previously but it's open now, so it schedules content#open() to run
  • content#open() runs and in turn calls basic-dropdown#reposition(), which calls basic-dropdown#applyReposition() which finally sets the position and size of the dropdown

Now when closing a dropdown, the following happens:

  • basic-dropdown#close() first sets hPosition, vPosition, top, left, right, width and height to null and then it sets isOpen to false via a call to basic-dropdown#updateState()
  • Both these changes casue content#didReceiveAttrs() to fire
  • didReceiveAttrs sees that it was open and it's not open now and closes itself

This works fine, the problem seems to happen when the dropdown is closed and then opened again in a very short span of time, so that it all happens during a single run or something like that.

In that case the following happens:

  • basic-dropdown#close correctly sets isOpen to false and clears all the attributes
  • basic-dropdown#open() correctly sets isOpen to true
  • These changes cause content#didReceiveAttrs() to fire, which sees that it was and is open and therefore it doesn't have to do anything. This is clearly wrong, because all the positions and sizes have been se to null, which causes the dropdown to render in the top left.

A simple fix to this issue is to schedule content#open() not only when the state has changed from closed to open, but also when the dropdown is open and all the top, left, etc. properties are set to null, but it's not a very pretty solution.

@cibernox What is your opinion on this?

@fast4shoot that's a very deep breakdown of what is happening! 馃檱

The main problem with this is being able to create a reproduction example better than "just click the dropdown like crazy". But your explanation makes sense. I'll allocate some time over the weekend to see of I can see.
I think that delaying the nullyfication of those properties could work tho.

I found out that it becomes very easy to reproduce when you add a onfocus handler that just takes a long time to finish, I've used a simple active wait:

focus() {
    const start = new Date();
    while ((new Date()  - start) < 150);
}

Although for some reason when you do this, you have to click the trigger three times when the dropdown is closed, but you don't have to be especially quick.

Obviously, in practice you wouldn't want a focus handler that takes hundreds of milliseconds to complete, so this is a very artificial example, but it's useful for testing.

Anyway, delaying the nullification might work. I can imagine it being triggerred from content#close() just like basic-dropdown#reposition() is done in content#open().

Fantastic trick! I might have time today for investigating this. I will probably impossible to add a regression test for this tho.

I am not sure that it's the same bug, but I was able to reproduce the same behavior on ember-basic-dropdown docs page.
ember-basic-dropdown

Fixed in https://github.com/cibernox/ember-basic-dropdown/pull/301. Thanks everyone for great work :+1:

Was this page helpful?
0 / 5 - 0 ratings