yanickrochon / jquery.uix.multiselect

Completely rewritten, multiselect widget with a more concise API
http://mind2soft.com/labs/jquery/multiselect/
MIT License
139 stars 62 forks source link

getElementDeclaredSize doesn't account for inherited CSS #84

Closed ksiegel closed 10 years ago

ksiegel commented 10 years ago

Not sure if you're still working this feature out, but as far as checking the size of the cloned element... the clone won't inherit the same CSS as the original.

In my installation I style my original select with #fieldPickerContainer select{ width: 400px; height: 200px}

The clone doesn't get that applied to it, and since my select is empty on init, it's calculating a random 2.7% width.

Also, side note, line 553... var container = $('<div>', {style: {}}) should be var container = $('<div>', { css: {}}) Because style can only be a string. It was resulting in <div style='[object Object]'>

I assume the end goal was related to the sizing... My idea would be try to follow how datatables does it (http://datatables.net/release-datatables/examples/basic_init/flexible_width.html) - They say it only obeys percentages when the width is specified inline. When widths and heights are specified inline, jQuery.css('width') returns that percentage rather than the calculated value.. but only when specified inline. It'll remove a lot of complexity

yanickrochon commented 10 years ago

Yeah, I guess this should have gone into an experimental branch... silly me.

The point of this function was to identify the approximate size in percentage, because it is impossible to retrieve the original declared styles from JavaScript. I quickly wrote the function this morning, in the middle of two things and should have created a testing branch for it.

Since the function is only executed when the widget is initialized, I didn't see any time-cost-related issues around it.

I believe the problem comes if your element is originally hidden (not rendered), thus it's width is undefined and mess the function's purpose. A solution would be to clone the element inside the parent, unresized, get it's width, resize the parent and then get the element's width again. For a fixed width element, it's size will not change. For a relative, percentage size, then it's width will have grown (or reduced...) within the reiszed parent. The bottom line is that the element must be rendered for the "initial" and "other" size to be correct.

ksiegel commented 10 years ago

Heh, it happens, no big deal. I went back to a commit from yesterday meanwhile.

I'll try to reword what I'm saying a little bit.. It wasn't a hidden select I had, it was a select that has a width of about 50px by default, but only when it's inside of #fieldPickerContainer it gets set to 200px. So when running the getElementDeclaredSize function, it went from 200px down to 50px. In reality it should be the same size and the clone width should equal the original width, but since the CSS isn't inherited from its parent, it changes. This makes it think that the select is a percentage width, rather than set in pixels.

I know the intention is to support fluid width elements, and I think I have an idea that could work well for this. Originally the width was read using element.outerWidth() and element.outerHeight() - I'm suggesting using element.css('width') and element.css('width') instead: element.outerWidth() will always return pixels, which was the problem element.css('width') will return percentages if percentages are used to set the width in-line, it will return pixels any other time. This is the way I've seen other plugins do it, and it will remove the need to try to clone and calculate things... There's a lot of ways the clone and calculate can go wrong, such as the styling inheritance problem I described.

yanickrochon commented 10 years ago

Alright, I get it. I'll remove that function and try the proposal you are suggesting. However, this "flexible" layout will not work as intended anyhow, because the components inside are initialized with fixed width too. I'm not sure how this will perform if using using percentage sizes.

To fully support a fluid layout, the way the components are initialized and positioned will have to be rewritten.

I'll have to dive into client programming again... haven't done that in months now!

yanickrochon commented 10 years ago

I just did a quick test and this.element.css('width') does not return the declared width in percentage, even if the element has style="width:60%;". This was tested in Chrome.

Edit

Well... seeing all the questions about it on StackOverflow, I'd suggest to make it into an option. The widget would calculate the percentage of the element based upon it's parent and apply a fluid layout, regardless if the element was using a percentage or a fixed width at all.

The bottom line is that the auto-detection of this value seems to be impossible (thus my experimental function).

ksiegel commented 10 years ago

You're right. jQuery has changed its behavior, it seems. Good news is raw DOM JS can still read it. See below

image

cubiclesoft commented 10 years ago

Hmm...this issue might be why resizing this widget in Admin Pack has such wildly different widths. Was going to open an issue but then saw this.

yanickrochon commented 10 years ago

Last night's commit should have reverted the problem. I am actually looking for a way to accommodate this issue, that will also resolve #41 as well.

ksiegel commented 10 years ago

Yeah, saw that commit. Thanks! Check out the image I attached though if you're looking for ideas on how to do it... DOMElement.style.width does indeed return percentages! I guess this issue can be closed with eda54171adcd8eb0eef1f05b47426089d4bf1957?

yanickrochon commented 10 years ago

Since this issue is only related to the regression then, yes it may be closed.

As for the image, consider this

$('<div id="foo" style="width:75%;">').css("width")
// => "75%"
$('<div id="foo" style="width:75%;">').appendTo($('body')).css("width")
// => "1013px"

Therefore, the css function may only work for elements generated that are not yet added to the DOM. As soon as the element is added, the width is translated to it's computed value and the only way to retrieve it is by performing the direct CSS manipulation

$('<div id="foo" style="width:75%;">').appendTo($('body'))[0].style.width
// => "75%"