[FIXED] [#903] [3.3] ComboBox boundlist disappears after grid horizontal scroll

Page 1 of 2 12 LastLast
  1. #1

    [FIXED] [#903] [3.3] ComboBox boundlist disappears after grid horizontal scroll

    This was fun to isolate (not really). Tested against 3.2.1 SVN rev 6569. Happens in IE11. Not an issue in Firefox as far as I could tell. I tested the JS-only version (below) against Sencha Fiddle to see if it was coming from Ext JS and it's present in 5.1.1.451 but not 6.0.1.250.

    Steps to elicit the issue:
    1. Click the trigger in the window combo to cause the combo's boundlist to render. (You don't have to select anything.)
    2. Close the window, and scroll the grid horizontally right 3/4 of the way or so. If you have the DOM explorer on, you can see the boundlist's top and left style attributes change to something strange.
    3. Click 'Show Window' to bring the window back up.
    4. Click the combo's trigger again - it doesn't appear as expected. (If it does, repeat steps 2-4 to try again.)


    <%@ Page Language="C#" %>
    
    <!DOCTYPE html>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title></title>
    </head>
    <body>
        <ext:ResourceManager runat="server" />
    
        <ext:GridPanel runat="server" ID="MainGrid" Height="200" Width="250">
            <Store>
                <ext:Store runat="server" ID="GridStore">
                    <Fields>
                        <ext:ModelField Name="fld1" Type="Int" />
                        <ext:ModelField Name="fld2" Type="String" />
                        <ext:ModelField Name="fld3" Type="String" />
                    </Fields>
                </ext:Store>
            </Store>
            <ColumnModel>
                <Columns>
                    <ext:Column runat="server" Text="field 1" DataIndex="fld1" Width="300" />
                    <ext:Column runat="server" Text="field 2" DataIndex="fld2" Width="300" />
                    <ext:Column runat="server" Text="field 3" DataIndex="fld3" Width="300" />
                </Columns>
            </ColumnModel>
            <TopBar>
                <ext:Toolbar runat="server">
                    <Items>
                        <ext:Button runat="server" Text="Show Window" Handler="#{TestWindow}.show();" />
                    </Items>
                </ext:Toolbar>
            </TopBar>
        </ext:GridPanel>
    
        <ext:Window runat="server" ID="TestWindow"
            CloseAction="Hide"
            Modal="true"
            Height="300"
            Width="300">
            <Items>
                <ext:ComboBox runat="server"
                    QueryMode="Local"
                    ValueField="code"
                    DisplayField="desc">
                    <Items>
                        <ext:ListItem Value="a" Text="One" />
                        <ext:ListItem Value="b" Text="Two" />
                        <ext:ListItem Value="c" Text="Three" />
                    </Items> 
                </ext:ComboBox>
            </Items>
        </ext:Window>
    </body>
    </html>
    Here's the JS-only code:
    Ext.onReady(function() {
        var testWindow = new Ext.window.Window({
            closeAction: 'hide',
            height: 300,
            width: 300,
            items: [{
                xtype: "combobox",
                displayField: "desc",
                queryMode: "local",
                valueField: "code",
                store: [
                    ["a", "One"],
                    ["b", "Two"],
                    ["c", "Three"]
                ]
            }],
            modal: true
        });
        
        Ext.create("Ext.grid.Panel", {
            renderTo: Ext.getBody(),
            height: 200,
            width: 250,
            store: {
                fields: [{
                    name: "fld1",
                    type: "int"
                }, {
                    name: "fld2",
                    type: "string"
                }, {
                    name: "fld3",
                    type: "string"
                }]
            },
            tbar: {
                xtype: "toolbar",
                items: [{
                    handler: function() {
                        testWindow.show();
                    },
                    text: "Show Window"
                }]
            },
            columns: [{
                dataIndex: "fld1",
                text: "field 1",
                width: 300
            }, {
                dataIndex: "fld2",
                text: "field 2",
                width: 300
            }, {
                dataIndex: "fld3",
                text: "field 3",
                width: 300
            }]
        });
        
        testWindow.show();
    });
    Last edited by Daniil; Oct 16, 2015 at 10:42 AM. Reason: [FIXED] [#903] [3.3]
  2. #2
    What the hell is going on? I mean it is quite a weird issue:)

    Hello Scott!

    Thank you for the report! Especially, for investigating in details and providing a standalone test case to reproduce it.

    Created an Issue.
    https://github.com/extnet/Ext.NET/issues/903

    For I've narrowed the problem down a bit. At least, it doesn't depend on a Window's Modal="true". Also it doesn't depend on a GridPanel. It is reproducible with a regular Container. Though, it appears to be not reproducible with a simple div. I will continue investigating.

    Example
    <%@ Page Language="C#" %>
    
    <!DOCTYPE html>
    
    <html>
    <head runat="server">
        <title>Ext.NET v3 Example</title>
    </head>
    <body>
        <ext:ResourceManager runat="server" />
    
        <ext:Button runat="server" Text="Show Window" Handler="App.TestWindow.show();" />
    
        <ext:Container 
            runat="server" 
            Height="100" 
            Width="200" 
            AutoScroll="true">
            <Content>
                <div style="width: 500px;"></div>
            </Content>
        </ext:Container>
    
        <%-- It appears to be not reproducible with a regular div --%>
        <%--<div style="border: 1px solid; height: 100px; width: 200px; overflow: auto;">
            <div style="width: 800px; height: 50px;"></div>
        </div>--%>
    
        <ext:Window 
            ID="TestWindow"
            runat="server" 
            Height="300"
            Width="300">
            <Items>
                <ext:ComboBox runat="server">
                    <Items>
                        <ext:ListItem Value="a" Text="One" />
                        <ext:ListItem Value="b" Text="Two" />
                        <ext:ListItem Value="c" Text="Three" />
                    </Items>
                </ext:ComboBox>
            </Items>
        </ext:Window>
    </body>
    </html>
  3. #3
    I've chased it as far as being an issue with the returned top value from Ext.dom.Element.getStyle(). It looks like the strange top/left values are a feature as they also update in Firefox during scrolling and the Ext JS code appears to factor these in (in Ext.util.Positionable.translateXY). The issue seems to be that IE11 isn't doing the calculations properly.

    I set a breakpoint on ext-all-debug.js line 21612 with condition Ext.String.startsWith(this.id, 'boundlist')

    When it breaks here after you've done the horizontal scroll and are attempting to show the boundlist again, you can walk through and see the incorrect top value. With the next statement at line 21631, I can see that dom.style.top matches the (large negative) value for top as seen in DOM explorer. But it then uses dom.ownerDocument.defaultView.getComputedStyle(dom, null) on line 21639 to normalize the styles and the value from that for top isn't the same. I do note that the left values from dom.style and from getComputedStyle still match.

    For reference, here's the call stack from that breakpoint:
    Call stack:
    getStyle [Line: 21612, Col: 13], ext-all-debug.js
    translateXY [Line: 16430, Col: 13], ext-all-debug.js
    translatePoints [Line: 16420, Col: 9], ext-all-debug.js
    setXY [Line: 22712, Col: 17], ext-all-debug.js
    callParent [Line: 7408, Col: 13], ext-all-debug.js
    setXY [Line: 29069, Col: 17], ext-all-debug.js
    setXY [Line: 41270, Col: 9], ext-all-debug.js
    beginLayout [Line: 120348, Col: 13], ext-all-debug.js
    resetLayout [Line: 144459, Col: 13], ext-all-debug.js
    invalidate [Line: 144219, Col: 17], ext-all-debug.js
    flushInvalidates [Line: 144053, Col: 13], ext-all-debug.js
    run [Line: 144485, Col: 9], ext-all-debug.js
    statics.flushLayouts [Line: 38589, Col: 17], ext-all-debug.js
    statics.updateLayout [Line: 38618, Col: 21], ext-all-debug.js
    updateLayout [Line: 41209, Col: 17], ext-all-debug.js
    onShow [Line: 40498, Col: 9], ext-all-debug.js
    callParent [Line: 7408, Col: 13], ext-all-debug.js
    onShow [Line: 121475, Col: 9], ext-all-debug.js
    show [Line: 41021, Col: 21], ext-all-debug.js
    expand [Line: 119998, Col: 13], ext-all-debug.js
    doQuery [Line: 122137, Col: 17], ext-all-debug.js
    onTriggerClick [Line: 122290, Col: 21], ext-all-debug.js
    callback [Line: 5325, Col: 17], ext-all-debug.js
    onClick [Line: 105777, Col: 13], ext-all-debug.js
    fire [Line: 11885, Col: 21], ext-all-debug.js
    fire [Line: 18703, Col: 21], ext-all-debug.js
    publish [Line: 18679, Col: 21], ext-all-debug.js
    onRecognized [Line: 19179, Col: 9], ext-all-debug.js
    fire [Line: 69269, Col: 9], ext-all-debug.js
    onTouchEnd [Line: 70136, Col: 9], ext-all-debug.js
    invokeRecognizers [Line: 19217, Col: 13], ext-all-debug.js
    onTouchEnd [Line: 19370, Col: 9], ext-all-debug.js
    Anonymous function [Line: 4555, Col: 29], ext-all-debug.js
    fireHandlers [Line: 4358, Col: 21], ext-all-debug.js
  4. #4
    Well, this ugly hack seems to get it working. I don't know if there are other circumstances where domStyle[camel] has a non-empty value and getComputedStyle returns a different value from dom.style and the computed value is desired. I suppose I could make it only do this if camel === 'top', but maybe there are other places where IE11 gets it wrong?

    if (Ext.isIE11) {
        Ext.define('Ext.override.dom.Element', {
            override: 'Ext.dom.Element',
    
            getStyle: function (property, inline) {
                var me = this,
                    dom = me.dom,
                    multiple = typeof property !== 'string',
                    hooks = me.styleHooks,
                    prop = property,
                    props = prop,
                    len = 1,
                    domStyle, camel, values, hook, out, style, i;
                if (multiple) {
                    values = {};
                    prop = props[0];
                    i = 0;
                    if (!(len = props.length)) {
                        return values;
                    }
                }
                if (!dom || dom.documentElement) {
                    return values || '';
                }
                domStyle = dom.style;
                if (inline) {
                    style = domStyle;
                } else {
    
    
    
    
                    style = dom.ownerDocument.defaultView.getComputedStyle(dom, null);
    
                    if (!style) {
                        inline = true;
                        style = domStyle;
                    }
                }
                do {
                    hook = hooks[prop];
                    if (!hook) {
                        hooks[prop] = hook = {
                            name: Ext.dom.Element.normalize(prop) // changed from internal reference to Element
                        };
                    }
                    if (hook.get) {
                        out = hook.get(dom, me, inline, style);
                    } else {
                        camel = hook.name;
                        out = domStyle[camel] || style[camel]; // added to take domStyle value if available
                    }
                    if (!multiple) {
                        return out;
                    }
                    values[prop] = out;
                    prop = props[++i];
                } while (i < len);
                return values;
            }
        });
    }
  5. #5
    Thank you for the investigation!

    I would be afraid of overriding the Element's getStyle method... Quite difficult to realize/foresee if it can break something else.

    I would try overriding the Ext.util.Floating's onAlignToScroll. Not a real fix, though. Just a workaround - avoiding re-aligning of a hidden element on scroll in IE9 and later (it appears to be okay in IE8).
    Ext.Component.override({
        privates: {
            onAlignToScroll: function (scroller) {
                var me = this,
                    el = me._lastAlignToEl,
                    dom;
    
                if (el && !scroller.getElement().contains(me.el) && !(Ext.isIE9p && !el.isVisible())) { // #903: added the "&& !(Ext.isIE9p && !el.isVisible())" condition
                    dom = el.isElement ? el.dom : el;
                    if (dom && !Ext.isGarbage(dom)) {
                        me.alignTo(el, me._lastAlignToPos);
                    } else {
                        me.clearAlignEl();
                    }
                }
            }
        }
    });
    Last edited by Daniil; Oct 16, 2015 at 10:38 AM.
  6. #6
    Thanks. I put your override into our test environment, so we'll see how it goes.
  7. #7
    Thank you, Scott. I tend to commit the override to SVN and your feedback in a while like "all is good" would finally persuade me to commit it.
  8. #8
    Okay, I guess all is good with the override so far. I've committed it to the SVN trunk in the revision #6602. It goes to the 3.3 release.

    Thank you again!
  9. #9
    Here is a related forum thread with another test case to reproduce a similar issue.
    http://forums.ext.net/showthread.php?60346

    The initial fix has been adjusted.
  10. #10
    Daniil,

    Thanks for the update. I updated the override in my code. We're hopefully only a few weeks away from going live with the project it's in, but there haven't been any issues with the previous override during testing so far.
Page 1 of 2 12 LastLast

Similar Threads

  1. Replies: 6
    Last Post: Nov 12, 2014, 8:35 PM
  2. [CLOSED] .x-boundlist in ComboBox, MultiSelect
    By osef in forum 2.x Legacy Premium Help
    Replies: 9
    Last Post: Aug 11, 2014, 8:56 PM
  3. Replies: 9
    Last Post: Apr 04, 2014, 9:10 PM
  4. Replies: 8
    Last Post: Jun 06, 2013, 12:42 PM
  5. Replies: 3
    Last Post: Aug 01, 2012, 6:51 PM

Posting Permissions