DOM之样式

jQuery有css这个方法很便利的获取或者设置文档的样式,如果原生的方式呢?可以回顾一下js是如果操作DOM样式的。


className

className属性总是和html上的class特性保持一致的。用法例如:

<body class="class1 class2">
<script>
  alert(document.body.className)
  document.body.className += ' class3'
</script>
</body>

如果想要删除一个class,可以把字符串中的class字符replace成空,也可以应空格split字符串后删除class再join回来。多数js框架都会提供内置方法来做这些工作,例如jq中:

$( "p" ).removeClass( "myClass noClass" ).addClass( "yourClass" );

style

style属性是读写元素style的通道,用它可以修改大多数css的属性,例如:

element.style.width='100px';

这句话就相当于在html上添加了style="width:100px"的特性。有一点要注意的是,和css一样,赋值的时候需要提供测量的单位,例如:'px', '%', 'rem', 'em'等。对于多单词字符属性,需要转换成小驼峰才可以,例如:

background-color => backgroundColor
z-index => zIndex
border-left-width => borderLeftWidth

使用style赋值的属性值总会覆盖用css样式的属性值,所以我们可以这样来删除width的赋值:

element.style.width = '';

又比如这样赋值:

// enter an empty value to reset the color
document.body.style.backgroundColor = prompt('background color?', 'green')

对于有浏览器前缀的使用例子如下,当然把属性放到css中,使用element.className来添加和删除style更加清晰。

//-moz-border-radius, -webkit-border-radius
button.style.MozBorderRadius = '5px'
button.style.WebkitBorderRadius = '5px'

cssText

style.cssText这个属性可以用来读写整个样式声明:

<div>Button</div>

<script>
  var div = document.body.children[0]
 
  div.style.cssText='color: red !important; \
    background-color: yellow; \
    width: 100px; \
    text-align: center; \
    blabla: 5; \
  '
  alert(div.style.cssText)
</script>

看到和用style[property]使用的不同了吧,cssText是对整个html中style特性的覆盖,也就说如果原本拥有的style在cssText中没有被写到,则该style就被清空了。

注意的是cssText中赋值的样式,只有浏览器认识它才会添加,如果不存在的例如blabla, 则会被忽略。

style.cssText是在js中可以添加css值中!important的唯一途径。


读取style中的值

特别需要注意的是,style属性仅仅可以访问那些被直接用element.style[propery]这样赋值或者用html中style特性赋值的样式,所以如下,如果你试图访问marginTop的样式的值你是访问不到的:

<style>
  body { margin: 10px }
</style>
<body>
  <script> 
    alert(document.body.style.marginTop) 
  </script>
</body>

这是因为margin是css样式表作用的渲染结果,并不是html的style特性上的值或者用style属性赋的值。如果你用style属性赋值则可以成功读取值:

<style>
  body { margin: 10px }
</style>
<body>
  <script> 
    document.body.style.margin = '20px'
    alert(document.body.style.marginTop) 
  </script>
</body>

从上面可以看出来,浏览器对margin属性进行了解开,即提供了它相关子属性的读取,有这种现象了属性还有border、background等等。

还有一点需要注意的是,通常不应该用js直接个style属性赋值而应该用css的classes(用js来添加删除class来实现),除非用class无法完成需求(通常比较少,但是有这种情况)。


getComputedStyle and currentStyle

上面说了,使用style无法获取那些在css里定义的样式,那如果我们需要知道此刻渲染到页面上的元素样式时该怎么办呢?

在css样式表和style特性描述以及js操作style属性之后,我们读取此刻元素真正运用上的属性值需要DOM Level2规范中提出的window.getComputedStyle方法。该方法的语法:

getComputedStyle(element, pseudo)

其中pseudo是伪类选择器,例如'hover',如果不需要刻意不传。

这个方法在所有浏览器都支持,除了IE<9,如下:

<style>
  body { margin: 10px }
</style>
<body>

  <script> 
    var computedStyle = getComputedStyle(document.body, null)
    alert(computedStyle.marginTop) 
  </script>

</body>

那么对于低版本IE怎么办呢?IE提供了currentStyle属性,基本和getComputedStyle方法效果一样。但是这个属性有一个缺陷,就是currentStyle返回的值是css中的值,也就是说css中的单位是什么返回的就是什么,而不是被换算成了实际的pixels。例如:

<style>
  body { margin: 10% }
</style>
<body>
  <script> 
    if (window.getComputedStyle) {
      var computedStyle = getComputedStyle(document.body, null)
    } else {
      computedStyle = document.body.currentStyle
    }
    alert(computedStyle.marginTop) 
  </script>
</body>

在火狐中,pixels也许是小数的;在其他浏览器中是整数的pixels;在IE中还是原有的单位,也就是上面会看到%。怎么解决呢?有一个方案可以转换,具体理解可以去参考IE特有的两个属性runtimeStyle和pixelLeft,方法如下:

function getCurrentPixelStyle(elem, prop) {
  var value = elem.currentStyle[prop] || 0

  // we use 'left' property as a place holder so backup values
  var leftCopy = elem.style.left
  var runtimeLeftCopy = elem.runtimeStyle.left

  // assign to runtimeStyle and get pixel value
  elem.runtimeStyle.left = elem.currentStyle.left
  elem.style.left = (prop === "fontSize") ? "1em" : value
  value = elem.style.pixelLeft + "px";

  // restore values for left
  elem.style.left = leftCopy 
  elem.runtimeStyle.left = runtimeLeftCopy 
 
  return value
}

<style> #margin-test { margin: 1% } </style>
<div id="margin-test">margin test</div>

<script>
  var elem = document.getElementById('margin-test')
  if (elem.currentStyle) // IE
    document.write(getCurrentPixelStyle(elem, 'marginTop')) 
  else 
    document.write('Open the page in IE please')
</script>

嗯嗯~幸好我不用兼容IE。。。

翻译原文的链接here