Page 1 of 2 12 LastLast
Results 1 to 10 of 15

Thread: [4.0.2a] Bug in Element.slideIn

    This duplicates another bug already reported in our system: EXTJS-3637
  1. #1
    Sencha Premium User westy's Avatar
    Join Date
    Feb 2009
    Location
    Bath, UK
    Posts
    1,038

    Default [4.0.2a] Bug in Element.slideIn

    Hi,

    Been seeing issues with animation and older versions of IE ever since Ext 4 launched.
    They're a little hard to track what is going on, and how though, and given that it was older versions I just thought it was them being rubbish so disabled it.

    The issue is seen mostly when tree animation occurs, normally during an expansion that causes a load, and no, I don't have a canned repeatable test case at the moment

    Since 4.0.2a I've been seeing issues in IE9 though, so thought I'd try and look deeper.

    Am seeing the following error:
    Unable to get value of the property 'insertBefore': object is null or undefined
    ext-all-debug.js, line 9351 character 21

    The line in question is in Element.anim.js, in the huge slideIn method (which makes it a pain to override, might I add):
    PHP Code:
    if (wrap.dom) {
        
    wrap.dom.parentNode.insertBefore(me.domwrap.dom); 
        
    wrap.remove();

    Basically, it seems that sometimes the dom node wrapped by an animation has a null parentNode, but is used blindly.


    Worth noting that also seeing the same thing in Firefox, but in a different place, line 7963 of ext-all-debug, which is in Element.insertion.js, insertSibling method (which looks like it has the scope to blow in a couple of places):
    PHP Code:
            if(el.nodeType || el.dom){
                
    rt me.dom.parentNode.insertBefore(Ext.getDom(el), isAfter me.dom.nextSibling me.dom);
                if (!
    returnDom) {
                    
    rt Ext.get(rt);
                }
            }else{
                if (
    isAfter && !me.dom.nextSibling) {
                    
    rt Ext.core.DomHelper.append(me.dom.parentNodeel, !returnDom);
                } else {
                    
    rt Ext.core.DomHelper[isAfter 'insertAfter' 'insertBefore'](me.domel, !returnDom);
                }
            } 

    Firstly, why or how can a dom elements parentNode be null? Is it anything I can control?
    Secondly, how about some defense around the above usage of the parentNode, i.e. if (blah.parentNode)? What side-effects would that have?
    Thirdly, could some of this be made easier to override?

    Would welcome some thoughts, and a speedy fix please!
    IE unusable at the moment!

    Cheers,
    Westy
    Product Architect
    Altus Ltd.

  2. #2
    Sencha Premium User westy's Avatar
    Join Date
    Feb 2009
    Location
    Bath, UK
    Posts
    1,038

    Default

    Have had to 'override' the slideIn one, since once it happens it appears that no windows can open, collapsing panels no longer works, etc.
    Generally, it's bad m'kay.

    I say 'override' since had to apply it to Ext.core.Element.prototype, since this is how the class is defined.

    My fix surrounded by a WestyFix tag
    PHP Code:
    Ext.apply(Ext.core.Element.prototype, {
        
    /*
         * Override of this method, to try and protect against a parentNode in the DOM being null.
         * See: ATG-391 & http://www.sencha.com/forum/showthread.php?137685-4.0.2a-Bug-in-Element.slideIn&p=616171
         * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
         * @param {Object} options (optional) Object literal with any of the Fx config options
         * @return {Ext.core.Element} The Element
         */
        
    slideIn: function(anchorobjslideOut) { 
            var 
    me this,
                
    elStyle me.dom.style,
                
    beforeAnimwrapAnim;

            
    anchor anchor || "t";
            
    obj obj || {};

            
    beforeAnim = function() {
                var 
    animScope this,
                    
    listeners obj.listeners,
                    
    boxpositionrestoreSizewrapanim;

                if (!
    slideOut) {
                    
    me.fixDisplay();
                }

                
    box me.getBox();
                if ((
    anchor == 't' || anchor == 'b') && box.height == 0) {
                    
    box.height me.dom.scrollHeight;
                }
                else if ((
    anchor == 'l' || anchor == 'r') && box.width == 0) {
                    
    box.width me.dom.scrollWidth;
                }
                
                
    position me.getPositioning();
                
    me.setSize(box.widthbox.height);

                
    wrap me.wrap({
                    
    style: {
                        
    visibilityslideOut 'visible' 'hidden'
                    
    }
                });
                
    wrap.setPositioning(position);
                if (
    wrap.isStyle('position''static')) {
                    
    wrap.position('relative');
                }
                
    me.clearPositioning('auto');
                
    wrap.clip();

                
    // This element is temporarily positioned absolute within its wrapper.
                // Restore to its default, CSS-inherited visibility setting.
                // We cannot explicitly poke visibility:visible into its style because that overrides the visibility of the wrap.
                
    me.setStyle({
                    
    visibility'',
                    
    position'absolute'
                
    });
                if (
    slideOut) {
                    
    wrap.setSize(box.widthbox.height);
                }

                switch (
    anchor) {
                    case 
    't':
                        
    anim = {
                            
    from: {
                                
    widthbox.width 'px',
                                
    height'0px'
                            
    },
                            
    to: {
                                
    widthbox.width 'px',
                                
    heightbox.height 'px'
                            
    }
                        };
                        
    elStyle.bottom '0px';
                        break;
                    case 
    'l':
                        
    anim = {
                            
    from: {
                                
    width'0px',
                                
    heightbox.height 'px'
                            
    },
                            
    to: {
                                
    widthbox.width 'px',
                                
    heightbox.height 'px'
                            
    }
                        };
                        
    elStyle.right '0px';
                        break;
                    case 
    'r':
                        
    anim = {
                            
    from: {
                                
    xbox.box.width,
                                
    width'0px',
                                
    heightbox.height 'px'
                            
    },
                            
    to: {
                                
    xbox.x,
                                
    widthbox.width 'px',
                                
    heightbox.height 'px'
                            
    }
                        };
                        break;
                    case 
    'b':
                        
    anim = {
                            
    from: {
                                
    ybox.box.height,
                                
    widthbox.width 'px',
                                
    height'0px'
                            
    },
                            
    to: {
                                
    ybox.y,
                                
    widthbox.width 'px',
                                
    heightbox.height 'px'
                            
    }
                        };
                        break;
                    case 
    'tl':
                        
    anim = {
                            
    from: {
                                
    xbox.x,
                                
    ybox.y,
                                
    width'0px',
                                
    height'0px'
                            
    },
                            
    to: {
                                
    widthbox.width 'px',
                                
    heightbox.height 'px'
                            
    }
                        };
                        
    elStyle.bottom '0px';
                        
    elStyle.right '0px';
                        break;
                    case 
    'bl':
                        
    anim = {
                            
    from: {
                                
    xbox.box.width,
                                
    width'0px',
                                
    height'0px'
                            
    },
                            
    to: {
                                
    xbox.x,
                                
    widthbox.width 'px',
                                
    heightbox.height 'px'
                            
    }
                        };
                        
    elStyle.right '0px';
                        break;
                    case 
    'br':
                        
    anim = {
                            
    from: {
                                
    xbox.box.width,
                                
    ybox.box.height,
                                
    width'0px',
                                
    height'0px'
                            
    },
                            
    to: {
                                
    xbox.x,
                                
    ybox.y,
                                
    widthbox.width 'px',
                                
    heightbox.height 'px'
                            
    }
                        };
                        break;
                    case 
    'tr':
                        
    anim = {
                            
    from: {
                                
    ybox.box.height,
                                
    width'0px',
                                
    height'0px'
                            
    },
                            
    to: {
                                
    ybox.y,
                                
    widthbox.width 'px',
                                
    heightbox.height 'px'
                            
    }
                        };
                        
    elStyle.bottom '0px';
                        break;
                }

                
    wrap.show();
                
    wrapAnim Ext.apply({}, obj);
                
    delete wrapAnim.listeners;
                
    wrapAnim Ext.create('Ext.fx.Anim'Ext.applyIf(wrapAnim, {
                    
    targetwrap,
                    
    duration500,
                    
    easing'ease-out',
                    
    fromslideOut anim.to anim.from,
                    
    toslideOut anim.from anim.to
                
    }));

                
    // In the absence of a callback, this listener MUST be added first
                
    wrapAnim.on('afteranimate', function() {
                    if (
    slideOut) {
                        
    me.setPositioning(position);
                        if (
    obj.useDisplay) {
                            
    me.setDisplayed(false);
                        } else {
                            
    me.hide();   
                        }
                    }
                    else {
                        
    me.clearPositioning();
                        
    me.setPositioning(position);
                    }
                    if (
    wrap.dom) {
                        
    // <WestyFix>
                        
    if (wrap.dom.parentNode) {
                            
    wrap.dom.parentNode.insertBefore(me.domwrap.dom); 
                        } else {
                            
    // FIXME: No idea what to do here...
                        
    }
                        
    // </WestyFix>
                        
    wrap.remove();
                    }
                    
    me.setSize(box.widthbox.height);
                    
    animScope.end();
                });
                
    // Add configured listeners after
                
    if (listeners) {
                    
    wrapAnim.on(listeners);
                }
            };

            
    me.animate({
                
    durationobj.duration obj.duration 1000,
                
    listeners: {
                    
    beforeanimate: {
                        
    fnbeforeAnim
                    
    },
                    
    afteranimate: {
                        
    fn: function() {
                            if (
    wrapAnim && wrapAnim.running) {
                                
    wrapAnim.end();
                            }
                        }
                    }
                }
            });
            return 
    me;
        }
        
    }); 
    Product Architect
    Altus Ltd.

  3. #3
    Sencha Premium User westy's Avatar
    Join Date
    Feb 2009
    Location
    Bath, UK
    Posts
    1,038

    Default

    Doh, have had to do this one aswell...

    PHP Code:
        /*
         * Override of this method, to try and protect against a parentNode in the DOM being null.
         * @param {Mixed/Object/Array} el The id, element to insert or a DomHelper config to create and insert *or* an array of any of those.
         * @param {String} where (optional) 'before' or 'after' defaults to before
         * @param {Boolean} returnDom (optional) True to return the .;ll;l,raw DOM element instead of Ext.core.Element
         * @return {Ext.core.Element} The inserted Element. If an array is passed, the last inserted element is returned.
         */
        
    insertSibling: function(elwherereturnDom){
            var 
    me thisrt,
            
    isAfter = (where || 'before').toLowerCase() == 'after',
            
    insertEl;

            if(
    Ext.isArray(el)){
                
    insertEl me;
                
    Ext.each(el, function(e) {
                    
    rt Ext.fly(insertEl'_internal').insertSibling(ewherereturnDom);
                    if(
    isAfter){
                        
    insertEl rt;
                    }
                });
                return 
    rt;
            }

            
    el el || {};

            
    // <WestyFix>
            
    if ((el.nodeType || el.dom) && me.dom.parentNode) {
            
    // </WestyFix>
                
    rt me.dom.parentNode.insertBefore(Ext.getDom(el), isAfter me.dom.nextSibling me.dom);
                if (!
    returnDom) {
                    
    rt Ext.get(rt);
                }
            }else{
                if (
    isAfter && !me.dom.nextSibling) {
                    
    rt Ext.core.DomHelper.append(me.dom.parentNodeel, !returnDom);
                } else {
                    
    rt Ext.core.DomHelper[isAfter 'insertAfter' 'insertBefore'](me.domel, !returnDom);
                }
            }
            return 
    rt;
        } 
    Product Architect
    Altus Ltd.

  4. #4
    Sencha Premium User westy's Avatar
    Join Date
    Feb 2009
    Location
    Bath, UK
    Posts
    1,038

    Default

    Following this issue, I've had to change my insertSibling override to:
    PHP Code:
        insertSibling: function(elwherereturnDom){
            var 
    me thisrt,
            
    isAfter = (where || 'before').toLowerCase() == 'after',
            
    insertEl;

            if(
    Ext.isArray(el)){
                
    insertEl me;
                
    Ext.each(el, function(e) {
                    
    rt Ext.fly(insertEl'_internal').insertSibling(ewherereturnDom);
                    if(
    isAfter){
                        
    insertEl rt;
                    }
                });
                return 
    rt;
            }

            
    el el || {};

            
    // <WestyFix>
            
    if (me.dom.parentNode) {
            
    // </WestyFix>
                
    if(el.nodeType || el.dom){
                    
    rt me.dom.parentNode.insertBefore(Ext.getDom(el), isAfter me.dom.nextSibling me.dom);
                    if (!
    returnDom) {
                        
    rt Ext.get(rt);
                    }
                }else{
                    if (
    isAfter && !me.dom.nextSibling) {
                        
    rt Ext.core.DomHelper.append(me.dom.parentNodeel, !returnDom);
                    } else {
                        
    rt Ext.core.DomHelper[isAfter 'insertAfter' 'insertBefore'](me.domel, !returnDom);
                    }
                }
            }
            return 
    rt;
        } 
    Product Architect
    Altus Ltd.

  5. #5
    Sencha Premium User westy's Avatar
    Join Date
    Feb 2009
    Location
    Bath, UK
    Posts
    1,038

    Default

    Still no guard against use of parentNode in 4.0.5
    Product Architect
    Altus Ltd.

  6. #6
    Sencha Premium User westy's Avatar
    Join Date
    Feb 2009
    Location
    Bath, UK
    Posts
    1,038

    Default

    ...or 4.0.6
    Product Architect
    Altus Ltd.

  7. #7
    Sencha User edspencer's Avatar
    Join Date
    Jan 2009
    Location
    Palo Alto, California
    Posts
    1,939

    Default

    This one's a bit of a wider issue regarding maintaining correct Component state while animating things. I'll ask Don to take a look at your suggestion to see if it's enough to clear this issue type
    Ext JS Senior Software Architect
    Personal Blog: http://edspencer.net
    Twitter: http://twitter.com/edspencer
    Github: http://github.com/edspencer

  8. #8
    Sencha Premium User westy's Avatar
    Join Date
    Feb 2009
    Location
    Bath, UK
    Posts
    1,038

    Default

    Quote Originally Posted by edspencer View Post
    This one's a bit of a wider issue regarding maintaining correct Component state while animating things. I'll ask Don to take a look at your suggestion to see if it's enough to clear this issue type
    Nice one, thanks again.
    Product Architect
    Altus Ltd.

  9. #9
    Sencha User edspencer's Avatar
    Join Date
    Jan 2009
    Location
    Palo Alto, California
    Posts
    1,939

    Default

    Hmm I think the above are just symptoms of the real problem so addressing them would be layering on more bandaids. We're discussing this as part of our 4.1 upgrades but it might not be feasible/efficient to attempts to address in 4.0.x...
    Ext JS Senior Software Architect
    Personal Blog: http://edspencer.net
    Twitter: http://twitter.com/edspencer
    Github: http://github.com/edspencer

  10. #10
    Sencha Premium User westy's Avatar
    Join Date
    Feb 2009
    Location
    Bath, UK
    Posts
    1,038

    Default

    Quote Originally Posted by edspencer View Post
    Hmm I think the above are just symptoms of the real problem so addressing them would be layering on more bandaids. We're discussing this as part of our 4.1 upgrades but it might not be feasible/efficient to attempts to address in 4.0.x...
    Agreed, these fixes simply work around the fact that parentNode is sometimes undefined.
    I'd much rather parentNode was always there, and accessible.

    I have to keep the overrides, since cannot have exceptions flying all the time. If there's a better, proper fix in the future then I welcome it.

    Whatever you do in 4.1 I implore you to test in old versions of IE (at least 7 & 8), and stuff more complex than many of your examples. Issues tend to crop up very quickly.

    Regards,
    Westy
    Product Architect
    Altus Ltd.

Page 1 of 2 12 LastLast

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •