洞悉特性、属性和样式

  在DOM特性和属性之间不仅会有一些非常细微的行为差别,在其他一些方面也会有很多bug以及跨浏览器问题。

  但特性和属性都是很重要的概念:特性是DOM构建的一个组成部分,而属性是元素保持运行时信息的主要手段,并且通过属性可以访问这些运行时信息。看个例子。

    var img = document.getElementsByTagName('img')[0];
    var newSrc = 'img/thumb.jpg';

    image.src = newSrc;

在上述代码中,我们创建一个图像标签,获取他的引用,并将他的src属性修改为一个新值,这看起来非常简单,但是我们运行测试验证一下。

    img.src === newSrc;

    img.getAttribute('src') === 'img/goods.webp';

测试src属性的值就是我们新赋的值,毕竟如果我们赋值x=213,当然希望x的值就是213,我们并没有改变其特性(attribute),所以它应该保持不变,对吧?

但当我们两个测试都失败了,我们看到src属性的值不是我们所赋予的值,而是类似如下的值

file:///E:/code/bookShopin/img/thumb.jpg"

给一个属性赋值,难道不应该去往那个属性就是所赋予的值吗?更奇怪的是,即使我们并没有改变元素上的特性(attribute),失败的测试表明,src的特性值已经变成如下内容了:

"img/thumb.jpg"

到底发生了什么事情,我们将检查与元素属性和特性相关的所有浏览器难题,我们会研究为什么结果不是我们所预期的。这同样适用于css和元素样式,构造一个动态web应用程序时,我们会在设置或获取元素样式方面遇到很多的困难,我们讨论一下元素的样式处理知识。

DOM特性和DOM属性

  在访问元素的特性值时,有两种选择:使用传统的DOM方法叫getAttribute和setAttribute,或使用DOM对象上与之对应的属性。举例来说,一个元素保存在变量e中,要获取其id特性的话,我们可以使用如下方式,

e.getAttribute('id');
    e.id

性能注意事项

总的来说,属性的访问速度比相应的DOM特性方法的访问速度要快,特别是在IE中,经过在多个浏览器上测试,属性的滑入和设置操作几乎总比getAttribute()和setAttribute()快。所以要提高性能,我们可能想要实现一个方法,在属性值存在的时候就访问属性值,属性值不存在的时候利用DOM方法作为后备,考虑如下代码。

(function () {
        var translations = {
            'for': 'htmlFor',
            'class': 'className',
            readonly: 'readOnly',
            maxlength: 'maxLength',
            cellspacing: 'cellSpacing',
            rowspan: 'rowSpan',
            colspan: 'colSpan',
            tabindex: 'tabIndex',
            cellpadding: 'cellPadding',
            usemap: 'useMap',
            frameborder: 'frameBorder',
            contenteditable: 'contentEditable'
        };

        window.attr = function (element, name, value) {
            var property = translations[name] || name;
            var propertyExists = typeof element[property] !== 'undefined';

            if(typeof value !== 'undefined') {
                if(propertyExists){
                    element[property] = value;
                }else {
                    element.setAttribute(name, value);
                }
            }


            return propertyExists ?
                element[property] :
                element.getAttribute(name);
        }
    })();

跨浏览器的attribute问题

五大浏览器都会将表单input元素的id和name特性作为元素的属性值进行引用,产生的这些属性,会主动覆盖form元素上已经存在的同名属性。

<input type="text" for="sufubo" id="test">

<form id="testForm" action="/" >
    <input type="text" id="id"/>
    <input type="text" name="action"/>
</form>

<script type="text/javascript">
var form = document.getElementById('testForm');
form.id === <input type="text" id="id"/>
form.action === <input type="text" name="action"/>


</script>

测试显示了,这个不幸的特性会导致标签数据的损失。
我们可以用获取描述元素特性本身的原始DOM节点,该节点任然没有被浏览器所修改。可以使用getAttributeNode(‘action’).nodeValue来进行获取action特性的值,该问题不能被认为是一个bug,因为这是浏览器预期的行为,当元素的引用很容易使用像document.getElementById()或其他类似的方法获取到时,该问题就很具有破坏性。

URL规范化

在所有的现代浏览器中,有一个违反最少意外原则的特性,在访问一个引用了URL的属性时,该URL值会自动将原始值转换成完成规范的URL。

<a href="package.json" id="testSubject">self</a>


<script type="text/javascript">
var link = document.getElementById('testSubject');

var linkHref = link.getAttributeNode('href').nodeValue;

link.href === 'package.json'


</script>

通过DOM的原始节点找出该标签的原始值。我们要对该值进行验证,测试失败,因为该值被转为为规范的URL值了。

type特性

另外还有一个IE特性,IE8以及之前的版本对元素的type特性的影响,没有任何合理的解决方案,一旦元素被插入到文档,他的type特性就不能再改变了。如果修改,IE就会抛出一个异常。

样式属性命名

我们编写一个简单的api自动将样式转化为驼峰格式


//访问样式的简单方式
function style(element, name, value) {
    name = name.replace(/-([a-z])/ig,
                        function (all, letter) {
                            return letter.toUpperCase();
                        });

    if(typeof value !== 'undefined') {
        element.style[name] = value;
    }

    return element.style[name];

}

对于隐藏的元素,如果希望获取它在非隐藏状态时的尺寸,我们可以使用一个技巧,暂时取消元素的隐藏,然后获取值,然后再将其隐藏,我们希望那这种做法不会有明显的视觉效果,而是只在幕后做,如何才能将一个隐藏的元素,在不可见的情况下变为不隐藏的呢?
方法如下:
1.将display属性设置为block
2.将visibility设置为hidden
3.将position设置为absolute
4.获取元素尺寸
5.恢复先前更改的属性
将display属性修改为block,可以让我们获取offsetHeight和offsetWidth的真实值,但他将元素变成显示状态而因此可见,要让该元素不可见,需要将visibility设置为hidden,但是这种做法将导致在元素的位置上显示一片空白,所以我们需要将position属性设置为absolute,以便将元素移出正常的显示流。代码实现如下。

(function () {
    var PROPERTES = {
        position: 'absolute',
        visibility: 'hidden',
        display: 'block'
    };

    window.getDimensions = function (element) {

        //把原先的保存一下
        var previous = {};

        for(var key in PROPERTIES) {
            previous[key] = element.style[key];
            element.style[key] = PROPERTIES[key];
        }

        var result = {
            width: element.offsetWidth,
            height: element.offsetHeight
        }

        for (key in PROPERTIES) {
            element.style[key] = previous[key];
        }

        return result;
    }
})();

获取计算样式

function fetchComputedStyle(element, property) {

    if(window.getComputedStyle) {

        var computedStyles = window.getComputedStyle(element);

        if(computedStyles) {
            property = property.replace(/([A-Z])/g, '-$1').toLowerCase();
            return computedStyles.getpropertyValue(property);
        }
    }else if (element.currentStyle) {
        property = property.replace(
            /-([a-z])/ig,
            function (all, letter) {
                return letter.toUpperCase();
            });
            return element.currentStyle[property];
        )
    }
}

这个函数不论是在元素上显示声明的,还是继承自样式表的,都可以获取得到。还要注意,两种方式都指定了color属性,但是返回的是在元素上显式指定的值,元素的style特性指定的样式优先级永远高于继承的样式,即便继承的样式标记为!important也没用。
在处理样式属性的时候,还有一个问题我们需要注意:混合属性,css允许我们使用快捷方式表示混合属性,比如border属性,不必强迫单独对四个边框都指定颜色,宽度,边框样式,只需要使用如下规则即可:

border: 1px solid crimson;

注意,在获取属性的时候,我们需要检索的是底层的单个属性,我们不能检索border,而是应该检索像border-top-color和border-top-width这样的属性,就想在示例中所做的那样。

总结

在遇到跨浏览器兼容性问题时,获取和设置DOM特性、属性以及样式,可能不是浏览器javaScript开发中最严重的问题,但确实有它的问题,值得庆幸的是,我们学到了可以处理这些问题的方式,使得代码可以兼容跨浏览器,而无需使用浏览器探测。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值