代码之家  ›  专栏  ›  技术社区  ›  jcubic

如何用代理替换样式属性

  •  0
  • jcubic  · 技术社区  · 6 年前

    我在jQuery终端的单元测试中有这样的代码:

    // https://github.com/tmpvar/jsdom/issues/135
    Object.defineProperties(window.HTMLElement.prototype, {
        offsetLeft: {
            get: function() { return parseFloat(window.getComputedStyle(this).marginLeft) || 0; }
        },
        offsetTop: {
            get: function() { return parseFloat(window.getComputedStyle(this).marginTop) || 0; }
        },
        offsetHeight: {
            get: function() { return parseFloat(window.getComputedStyle(this).height) || 0; }
        },
        offsetWidth: {
            get: function() { return parseFloat(window.getComputedStyle(this).width) || 0; }
        },
        // this will test if setting 1ch change value to 1ch which don't work in jsdom used by jest
        style: {
            get: function() {
                if (this.__style) {
                    return this.__style;
                }
                var self = this;
                var attr = {};
                function set_style_attr() {
                    var str = Object.keys(attr).map((key) => `${key}: ${attr[key]}`).join(';') + ';';
                    self.setAttribute('style', str);
                }
                var mapping = {
                    backgroundClip: 'background-clip',
                    className: 'class'
                };
                var reversed_mapping = {};
                Object.keys(mapping).forEach(key => {
                    reversed_mapping[mapping[key]] = key;
                });
                return this.__style = new Proxy({}, {
                    set: function(target, name, value) {
                        name = mapping[name] || name;
                        if (!value) {
                            delete target[name];
                            delete attr[name];
                        } else {
                            attr[name] = target[name] = value;
                        }
                        set_style_attr();
                        return true;
                    },
                    get: function(target, name) {
                        if (name === 'setProperty') {
                            return function(name, value) {
                                attr[name] = target[name] = value;
                                set_style_attr();
                            };
                        } else {
                            return target[name];
                        }
                    },
                    deleteProperty: function(target, name) {
                        name = reversed_mapping[name] || name;
                        delete target[name];
                        delete attr[name];
                        set_style_attr();
                    }
                });
            }
        }
    });
    

    它适用于我的测试中的1ch属性,如下所示:

        it('should handle wider characters without formatting', function() {
            var input = 'ターミナルウィンドウは黒[[;;]です]';
            var string = $.terminal.format(input, {char_width: 7});
            expect(string).toEqual('<span style="width: 24ch"><span style="widt'+
                                   'h: 24ch">ターミナルウィンドウは黒</span></span'+
                                   '><span style="width: 4ch" data-text="です">'+
                                   '<span style="width: 4ch">です</span></span>');
        });
    

    如果我不使用我的代理,我得到了以像素为单位的宽度,因为我有这样的代码来检查我的代码中是否支持ch:

    var agent = window.navigator.userAgent;
    var is_IE = /MSIE|Trident/.test(agent) || /rv:11.0/i.test(agent);
    var is_IEMobile = /IEMobile/.test(agent);
    // -------------------------------------------------------------------------
    var is_ch_unit_supported = (function() {
        if (is_IE && !is_IEMobile) {
            return false;
        }
        var div = document.createElement('div');
        div.style.width = '1ch';
        return div.style.width === '1ch';
    })();
    

        it('should find inside formatting', function() {
            term.less(big_text.concat(['[[;red;]foo bar baz]']));
            search('bar');
            var spans = term.find('[data-index="0"] > div:first-child span');
            ['foo ', 'bar', ' baz'].forEach(function(string, i) {
                expect(a0(spans.eq(i).text())).toEqual(string);
            });
            [true, false, true].forEach(function(check, i) {
                console.log(spans.get(i).style.getPropertyValue('color'));
                expect([i, !!spans.get(i).attr('style').match(/color:\s*red/)]).toEqual([i, check]);
            });
        });
    

    我试过:

    spans.get(i).style.getPropertyValue('color')
    

    spans.get(i).attr('style')
    

    未定义。这也不行

    spans.get(i).getAttribute('style')
    

    有没有办法 ch 单元支持检查工作,但以同样的方式从样式属性获取值?

    我使用的是jest框架,它使用jsDom从节点运行测试。

    getPropertyValue 中的函数 get

    0 回复  |  直到 6 年前
        1
  •  0
  •   jcubic    6 年前

    我通过在访问属性时临时禁用getter解决了这个问题:

    (function() {
        var style_descriptor = Object.getOwnPropertyDescriptor(window.HTMLElement.prototype, 'style');
    
        Object.defineProperties(window.HTMLElement.prototype, {
            offsetLeft: {
                get: function() { return parseFloat(window.getComputedStyle(this).marginLeft) || 0; }
            },
            offsetTop: {
                get: function() { return parseFloat(window.getComputedStyle(this).marginTop) || 0; }
            },
            offsetHeight: {
                get: function() { return parseFloat(window.getComputedStyle(this).height) || 0; }
            },
            offsetWidth: {
                get: function() { return parseFloat(window.getComputedStyle(this).width) || 0; }
            },
            // this will test if setting 1ch change value to 1ch which don't work in jsdom used by jest
            style: {
                get: function getter() {
                    if (this.__style) {
                        return this.__style;
                    }
                    var self = this;
                    var attr = {};
                    function set_style_attr() {
                        var str = Object.keys(attr).map((key) => `${key}: ${attr[key]}`).join(';') + ';';
                        self.setAttribute('style', str);
                    }
                    var mapping = {
                        backgroundClip: 'background-clip',
                        className: 'class'
                    };
                    var reversed_mapping = {};
                    Object.keys(mapping).forEach(key => {
                        reversed_mapping[mapping[key]] = key;
                    });
                    function disable(fn) {
                        // temporary disable proxy
                        Object.defineProperty(window.HTMLElement.prototype, "style", style_descriptor);
                        var ret = fn();
                        Object.defineProperty(window.HTMLElement.prototype, "style", {
                            get: getter
                        });
                        return ret;
                    }
                    return this.__style = new Proxy({}, {
                        set: function(target, name, value) {
                            name = mapping[name] || name;
                            if (!value) {
                                delete target[name];
                                delete attr[name];
                            } else {
                                attr[name] = target[name] = value;
                            }
                            set_style_attr();
                            disable(function() {
                                self.style[name] = name;
                            });
                            return true;
                        },
                        get: function(target, name) {
                            if (name === 'setProperty') {
                                return function(name, value) {
                                    attr[name] = target[name] = value;
                                    set_style_attr();
                                };
                            } else if (target[name]) {
                                return target[name];
                            } else {
                                return disable(function() {
                                    return self.style[name];
                                });
                            }
                        },
                        deleteProperty: function(target, name) {
                            name = reversed_mapping[name] || name;
                            delete target[name];
                            delete attr[name];
                            disable(function() {
                                delete self.style[name];
                            });
                            set_style_attr();
                        }
                    });
                }
            }
        });
    })();
    
    推荐文章