Results 1 to 10 of 15

Thread: [4.0.2a] Bug in Element.slideIn

Hybrid View

Previous Post Previous Post   Next Post Next Post
    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
    Join Date
    Dec 2016
    Posts
    1

    Default

    Ext.override(Ext.dom.Element, {
    insertSibling: function(el, where, returnDom) {
    var me = this,
    DomHelper = Ext.core.DomHelper,
    oldUseDom = DomHelper.useDom,
    isAfter = (where || 'before').toLowerCase() == 'after',
    rt, insertEl, eLen, e;


    if (Ext.isArray(el)) {
    // append all elements to a documentFragment
    insertEl = Ext.fly(document.createDocumentFragment(), '_internal');
    eLen = el.length;


    // DocumentFragments cannot accept innerHTML
    DomHelper.useDom = true;
    for (e = 0; e < eLen; e++) {
    rt = insertEl.appendChild(el[e], returnDom);
    }
    DomHelper.useDom = oldUseDom;


    // Insert fragment into document
    me.dom.parentNode.insertBefore(insertEl.dom, isAfter ? me.dom.nextSibling : me.dom);
    return rt;
    }


    el = el || {};


    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 = DomHelper.append(me.dom.parentNode, el, !returnDom);
    } else {
    rt = DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
    }
    }
    return rt;
    }

    })

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
  •