`
hax
  • 浏览: 967264 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

getUsedValue 0.1

    博客分类:
  • AJAX
阅读更多
前不久写了一个小脚本,用来获取页面中CSS样式的 used value 。

什么是Used Value?

简单来说就是样式表应用到页面元素的最终结果值。比方说一个p元素,可能有多份样式表的多个样式规则,都关系到p元素的最终样式,但是最终一个特定p元素的实际css属性,比如它的width,当然只能有一个值。这个值就叫做used value。更精确的定义,请看CSS 2.1规范的6.1.3节

大多数同志可能知道computed value,因为符合标准的浏览器上有window.getComputedStyle()方法。

实际上,按照我对规范的理解,这个getComputedStyle方法返回的其实应该是used value,而不是computed value。这两者的差别是,computed value是无需layout时所能得到的值。比如,经过cascade计算后,实际应用于p的width为10em,假设p的fontSize是16px,则它的width的computed value就是160px,这是可以确定计算出来的。但是不是所有computed value都能这样确定下来,有些值要到layout时才能确定。比如p的width为50%,假定p是body的子元素,则这个50%是相对于body的宽度的。如果body没有设过明确的宽度,那实际上就是窗口的客户区宽度(clientWidth)。所以p的width是多少,只有到了layout的步骤才能算出来。另一个例子是table,本身td的宽度可以设定为table宽度的百分比,而非fixed的table本身的宽度又是要根据所有td(如内容多少、字体大小、换行设置等)来确定的,这个layout计算过程相当复杂。

所以,将computed value加上所有相关依赖,最后得到的结果,就叫做used value。

那么为什么DOM方法叫做getComputedStyle,而不是getUsedStyle呢?这是因为DOM level 2 CSS规范制定时(2000年)还没有CSS2.1。DOM是按照更早的CSS2规范来的,在CSS2中还没有提出computed value和used value的区分。严格的说,CSS2中定义的computed value,既不是CSS2.1的computed value,也不是CSS2.1的used value,而是某种有点含糊的两者混合体。CSS2.1之所以加上这个区分,可能是为了澄清有关inherit的值如何计算的问题。

抛开规范的问题,获得used value对于ajax开发者来说有时是很有用的。比如典型的垂直居中问题,通常的做法,你要获得实际的元素高度和容器高度的used value。就这个特定问题,通常利用clientHeight、offsetHeight(它们不是CSS property)等来计算。不过如果要满足一些其他要求,你可能还需要获得padding、border、margin、outline的used value。getUsedValue还具有更广泛的用途,例如获取某个元素里font的实际大小、或者某个元素的实际颜色之类的信息。另外,浏览器的功能有多强大,bug就有多烦人。有时候你难免要为特定问题打patch,而getUsedValue是打patch的居家必备武器。

我写的这个getUsedValue的API如下:
getUsedValue(element, cssPropertyName, cssUnit)


前两个参数比较好理解,最后一个参数是指定css长度单位(如果你要取的是一个css长度值的话)。如果不指定这个值(就传前两个参数),则返回的是一个字符串,例如"12px",如果你指定了cssUnit为“px”,则返回的是一个数字,即12。这对于要进行运算来说比较方便,省得你自己去parseFloat和parseInt。

来看一个简单的使用例子:

function verticalAlign(e, ratio) {
	ratio = ratio || 0.382
	var p = e.offsetParent || e.parentNode
	var s = e.style
	var minTop = getUsedValue(e, 'marginTop', 'px')
	s.position = 'relative'
	s.top = Math.max(minTop, (p.clientHeight - e.offsetHeight) * ratio - minTop) + 'px'
}


这个verticalAlign,用来将一个元素(由第一个参数e指定)在其父元素内垂直定位,默认会放在中间偏上一些(符合黄金分割的比例)。具体实现主要是通过clientHeight和offsetHeight来的,本来可以不用getUsedValue。但是我们有个额外的要求,希望在垂直定位时,仍然保留marginTop的用途,即元素上方至少要保留由元素的marginTop指定的空间。所以我们要得到marginTop的值,作为垂直定位时top的下限。
var minTop = getUsedValue(e, 'marginTop', 'px')
这句就是了。它表示获得e元素的marginTop以px单位记的数值。

下面是这个getUsedValue函数的源代码。代码以LGPL方式发布。

// getUsedValue() version 0.1
// Copyright 2009 hax<johnhax@gmail.com>

// You can use and distribute these codes under LGPL v3 license.

var CSSValueUnitTypes = [
	'unknown', 'number', '%',
	'em', 'ex', 'px',
	'cm', 'mm', 'in', 'pt', 'pc',
	'deg', 'rad', 'grad',
	'ms', 's', 'hz', 'khz',
	'dimension', 'string', 'uri', 'indent',
	'attr', 'counter', 'rect', 'rgbcolor'
]
function isLengthProperty(prop) {
	return '\
		top right bottom left \
		marginLeft marginRight marginTop marginBottom outlineWidth \
		borderTopWidth borderRightWidth borderBottomWidth borderLeftWidth \
		paddingTop paddingRight paddingBottom paddingLeft \
		maxHeight maxWidth minHeight minWidth height width \
		fontSize lineHeight textIndent letterSpacing wordSpacing \
		borderSpacing backgroundPosition clip \
		'.indexOf(prop) != -1
}
function isLengthUnit(unit) {
	return ('% em ex px cm mm in pt pc'.indexOf(unit) != -1)
}
function isAbsoluteLengthUnit(unit) {
	return ('cm mm in pt pc'.indexOf(unit) != -1)
}
function convertAbsoluteLength(n, u, u2) {
	if (u == u2) return n
	var t = { in_cm:2.54, in_pt:72, cm_mm:10, pc_pt:12 }
	t.in_mm = t.in_cm * t.cm_mm //25.4
	t.in_pc = t.in_pt / t.pc_pt //6
	t.cm_pt = t.in_pt / t.in_cm
	t.cm_pc = t.in_pc / t.in_cm
	t.mm_pt = t.in_pt / t.in_mm
	t.mm_pc = t.in_pc / t.in_mm
	var r
	if (r = t[u + '_' + u2]) return n * r
	if (r = t[u2 + '_' + u]) return n / r
	throw Error('Can not convert: ' + u + ' -> ' + u2)
}
function cssPropertyName(cssAttributeName) {
	return cssAttributeName.replace(/([A-Z])/g, '-$1').toLowerCase()
}

function getDPI() {
	if (!getDPI._1in) {
		getDPI._1in = document.createElement('div')
		var s = getDPI._1in.style
		s.margin = s.borderWidth = s.padding = '0'
		s.maxWidth = s.minWidth = s.width = '1in'
	}
	return getDPI._1in.style.pixelWidth
}
function calcPixelLength(elt, cssLength) {
	var s
	if (!calcPixelLength._temp) {
		calcPixelLength._temp = document.createElement('div')
		s = calcPixelLength._temp.style
		s.margin = s.borderWidth = s.padding = '0'
		s.display = 'none'
	}
	s = calcPixelLength._temp.style
	s.maxWidth = s.minWidth = s.width = cssLength
	elt.appendChild(calcPixelLength._temp)
	var r = s.pixelWidth
	elt.removeChild(calcPixelLength._temp)
	return r
}
function calcFontSize(size) {
	var s
	if (!calcFontSize._temp) {
		calcFontSize._temp = document.createElement('div')
		s = calcFontSize._temp.style
		s.margin = s.borderWidth = s.padding = '0'
		s.maxWidth = s.minWidth = s.width = '1em'
	}
	s = calcFontSize._temp.style
	s.fontSize = size
	return s.pixelWidth
}

function getUsedValue(elt, prop, unit) {
	
	if (typeof getComputedStyle == 'function') {
		if (unit == null) {
			return getComputedStyle(elt, null)[prop]
		} else {
			var unitType = CSSValueUnitTypes.indexOf(unit)
			var value = getComputedStyle(elt, null).
				getPropertyCSSValue(cssPropertyName(prop))
			switch (unitType) {
				default: return value.getFloatValue(unitType)
			}
		}
	}
	
	if (elt.currentStyle) {
		
		var v = elt.currentStyle[prop]
		
		if (!v) return v
		
		if (isLengthUnit(unit) || isLengthProperty(prop)) {
			
			if (prop == 'fontSize') {
				if ('xx-small x-small small medium large x-large xx-large'.
					indexOf(v) != -1) {
					var n = calcFontSize(v)
					if (unit == null) return n + 'px'
					if (unit == 'px') return n
					if (isAbsoluteLengthUnit(unit))
						return convertAbsoluteLength(n / getDPI(), 'in', unit)
					throw Error('px -> ' + unit + ' convertion has not implemented yet')
				}
				if ('smaller larger'.indexOf(v) != -1)
					throw Error('Calculation of font relative size has not implemented yet')
			}
			
			var n, u
			var a = /^([+-]?([0-9]+|[0-9]*[.][0-9]+))(em|ex|px|in|cm|mm|pt|pc|%)?/.exec(v)
			if (a == null) throw Error('Unknown length format: ' + v)
			n = parseFloat(a[1]), u = a[3]
			
			if (u == unit) return n
			
			if (u == 'px') {
				if (unit == null) return v
				if (isAbsoluteLengthUnit(unit))
					return convertAbsoluteLength(n / getDPI(), 'in', unit)
				throw Error('px -> ' + unit + ' convertion has not implemented yet')
			}
			if (isAbsoluteLengthUnit(u)) {
				if (isAbsoluteLengthUnit(unit))
					return convertAbsoluteLength(n, u, unit)
				n = convertAbsoluteLength(n, u, 'in')  * getDPI()
				if (unit == 'px') return n
				if (unit == null) return n + 'px'
				throw Error(u + ' -> ' + unit + ' convertion has not implemented yet')
			}
			var pixelProp = {
				width:'pixelWidth',height:'pixelHeight',
				top:'pixelTop',bottom:'pixelBottom',
				left:'pixelLeft',right:'pixelRight'
			}[prop]
			if (pixelProp) {
				n = elt.currentStyle[pixelProp]
				if (unit == 'px') return n
				if (unit == null) return n + 'px'
				throw Error('px -> ' + unit + ' convertion has not implemented yet')
			}
			if (u == '%') {
				throw Error('Percentage value calculation has not implemented yet')
			}
			if (u == 'ex') {
				throw Error('ex value calculation has not implemented yet')
			}
			if (u == 'em') {
				if (prop == 'fontSize') {
					n = calcPixelLength(elt.parentNode, v)
				} else {
					n *= getUsedValue(elt, 'fontSize', 'px')
				}
				if (unit == 'px') return n
				if (unit == null) return n + 'px'
				throw Error('em -> ' + unit + ' convertion has not implemented yet')
			}
			throw Error('Unknown unit type: ' + u)
		
		} else {
			
			return v
			
		}
	}
}


注意,目前还只是0.1版,没有经过充分的测试。

已知的问题包括:

1. 标准浏览器下,依赖getComputedStyle,而getComputedStyle函数,由于之前讲过的标准制定上的不同步,导致实际各浏览器实现可能有差异(可参考http://www.nabble.com/getComputedStyle-results-td16390247.html)。
2. getComputedStyle不支持px以外的相对单位,即使specified value确实是em/ex,也只能得到绝对值,无法倒算回去。
3. IE下是根据currentStyle来进一步计算的。currentStyle介于specified value和computed value之间(当然实际上都不是,因为IE的CSS模型根本完全不合标准,所以只是就概念上说部分属性相当于specified value,部分属性相当于computed value)。目前只针对css长度进行了进一步计算,而且不支持百分比和ex计算。em只在currentStyle返回原值是em时才可用。
4. 复杂情况,如table,如visibility:hidden、display:none之类的,都尚未有时间测试。

下一步的计划是:

1. 做更多实例的测试
2. 做更多浏览器的测试(目前其实只测了IE6和FF3)
3. 完全支持em和%长度的计算(ex不会支持,因为基本没人用,且无法准确测量ex,ex依赖于字体,bug之多无法计数)
4. 支持各种格式的颜色值的计算

欢迎大家测试和反馈bug。
3
0
分享到:
评论
12 楼 cloudgamer 2009-04-22  
ie8和ff的规律是一行中第一个和最后一个td的实际宽度是offsetWidth减去boder的一半
中间的还是相同的
11 楼 cloudgamer 2009-04-22  
上面的ie是ie8
10 楼 cloudgamer 2009-04-22  
发现一个用offsetWidth也不准确的情况
<table border="30" width="300" style="border-collapse:collapse;">
<tr>
<td id="tt" bgcolor="#FF0000">&nbsp;1</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
</table>

ie中tt的offsetWidth是98但实际应该是约83
ff中offsetWidth是111但实际应该是约95
相差都差不多是15(border的一半)
9 楼 hax 2009-04-21  
因为border比较麻烦,所以暂时没有支持。下个小版本(明天吧)会增加对border的支持。
8 楼 cloudgamer 2009-04-21  
看来还是得用原始的方法了
你好像忘了考虑border哦
7 楼 hax 2009-04-21  
更新了,版本0.1.3,加入了对IE下width:auto/height:auto的支持,另外修正了一个小bug。

地址不变:http://pie.googlecode.com/svn/trunk/src/util/getUsedValue.js
6 楼 cloudgamer 2009-04-21  

确实需要runtimeStyle
忘了考虑

但还有一个情况
<div style="width:500px;">
<div id="tt"></div>
</div>
这样tt得到的width是auto
就得不到结果了
还有什么方法吗
5 楼 cloudgamer 2009-04-21  
今天才知道有个pixelWidth的属性
谢谢!
貌似直接style.pixelWidth就能得到结果了
4 楼 hax 2009-04-21  
使用更新的0.1.1版本:http://pie.googlecode.com/svn/trunk/src/util/getUsedValue.js

加上了对于 width/height/top/left/right/bottom 在IE下的支持,不过注意:似乎只在IE6的standard模式下才正确,在quirk mode下,得到的值似有问题。

<!DOCTYPE html>
<style>
#test { width:50%; }
</style>

<div id="test" style="padding:2px; border:2px solid black;">test</div> 

<script src="http://pie.googlecode.com/svn/trunk/src/util/getUsedValue.js"></script>
<script> 
var div = document.getElementById("test")
try {
	alert(getUsedValue(div, 'width'))
} catch(e) {
	alert(e.message)
}
</script> 


3 楼 cloudgamer 2009-04-20  
这么快哦
例如

<div id="tt" style="width:50%"></div>


<script>
var o=document.getElementById("tt");
if(document.defaultView){
alert(document.defaultView.getComputedStyle(o, null).width)
}else{
alert(o.currentStyle.width)
}
</script>

currentStyle是50%
getComputedStyle是准确值
我需要在ie获取准确值有什么方法

求教
2 楼 hax 2009-04-20  
@cloudgamer 能否说得更详细一点,给出testcase是最好的。
1 楼 cloudgamer 2009-04-20  
宽度获取document.defaultView.getComputedStyle
和currentStyle不相同
怎么解决

相关推荐

    部门绩效考核评价表excel.xls

    部门绩效考核评价表excel

    全面的公司行政费用统计表.xls

    全面的公司行政费用统计表

    视觉跟踪算法综述.pdf

    视觉跟踪算法综述.pdf

    CMD 命令行高级教程精选合编

    CMD 命令行高级教程精选合编

    apr-devel-1.4.8-7.el7.x64-86.rpm.tar.gz

    1、文件内容:apr-devel-1.4.8-7.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/apr-devel-1.4.8-7.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、安装指导:私信博主,全程指导安装

    10-4-生产主管绩效考核表(自动计算、等级评价).xlsx

    10-4-生产主管绩效考核表(自动计算、等级评价)

    深度学习python基础(第三节) 函数、列表

    深度学习python基础(第三节) 函数、列表

    岗位绩效考核评定表excel表格模板.xlsx

    岗位绩效考核评定表excel表格模板

    成品库仓管员绩效考核表.xls

    成品库仓管员绩效考核表

    环卫业务 基础知识培训(小步创想)PPT(133页).pptx

    一、智慧环卫管理平台的建设背景与目标 智慧环卫管理平台的建设源于对环卫管理全面升级的需求。当前,城管局已拥有139辆配备车载GPS系统、摄像头和油耗传感器的环卫车辆,但环卫人员尚未配备智能移动终端,公厕也缺乏信息化系统和智能终端设备。为了提升环卫作业效率、实现精细化管理并节省开支,智慧环卫管理平台应运而生。该平台旨在通过信息化技术和软硬件设备,如车载智能终端和环卫手机App,实时了解环卫人员、车辆的工作状态、信息和历史记录,使环卫作业管理透明化、精细化。同时,平台还期望通过数据模型搭建和数据研读,实现更合理的环卫动态资源配置,为环卫工作的科学、健康、持续发展提供决策支持。 二、智慧环卫管理平台的建设内容与功能 智慧环卫管理平台的建设内容包括运行机制体制建设、业务流程设计、智慧公厕系统建设、网络建设、主机和储存平台需求、平台运维管理体系、硬件标准规范体系以及考核评价体系等多个方面。其中,智慧公厕系统建设尤为关键,它能实时监控公厕运行状态,保障公厕的清洁和正常运行。平台建设还充分利用了现有的电子政务网络资源,并考虑了有线和无线网络的需求。在功能上,平台通过普查、整合等手段全面收集环卫车辆、企业、人员、设施、设备等数据,建立智慧环卫基础数据库。利用智能传感、卫星定位等技术实现环卫作业的在线监管和远程监控,实现对道路、公共场所等的作业状况和卫生状况的全面监管。此外,平台还建立了环卫作业网格化管理责任机制,实现从作业过程到结果的全面监管,科学评价区域、部门、单位和人员的作业效果。 三、智慧环卫管理平台的效益与风险规避 智慧环卫管理平台的建设将带来显著的环境、经济和管理效益。环境方面,它将有力推进环境卫生监管服务工作,改善环境卫生状况,为人民群众创造更加清洁、卫生的工作和生活环境。经济方面,通过智慧化监管,大大降低了传统管理手段的成本,提高了监管的准确性和效率。管理方面,平台能够追踪溯源市民反映的问题,如公厕异味、渣土车辆抛洒等,并找到相应的责任单位进行处置,防止类似事件再次发生。同时,平台还拥有强大的预警机制功能,能够在很多环卫问题尚未出现前进行处置。然而,平台建设也面临一定的风险,如部门协调、配合问题,建设单位选择风险以及不可预测的自然灾害等。为了规避这些风险,需要加强领导、统一思想,选择优秀的系统集成商承接项目建设,并做好计算机和应用系统的培训工作。同时,也要注意标准制定工作和相关法律法规的制定工作,以保证系统建设完成后能够真正为环卫管理工作带来便利。

    基于平衡计分卡绩效考核表(管理高层)模板.xls

    基于平衡计分卡绩效考核表(管理高层)模板

    网站运营各部门绩效考核表.xls

    网站运营各部门绩效考核表

    XX公司行政部绩效考核指标.xls

    XX公司行政部绩效考核指标

    基于齿向修形的抛物线锥齿轮仿真分析.pdf

    基于齿向修形的抛物线锥齿轮仿真分析.pdf

    三相半桥逆变器低电压穿越控制策略设计:两级式光伏并网系统电路原理与容量优化报告,两级式光伏并网系统及其低电压穿越控制策略设计,容量30kW 三相半桥逆变器,boost电路作前级 带低电压穿越,有一

    三相半桥逆变器低电压穿越控制策略设计:两级式光伏并网系统电路原理与容量优化报告,两级式光伏并网系统及其低电压穿越控制策略设计,容量30kW。 三相半桥逆变器,boost电路作前级。 带低电压穿越,有一万七千字的报告,没有水文字。 报告内容,电路原理,pi参数设计,bode和根轨迹分析,波形良好 ,关键词:两级式光伏并网系统;低电压穿越控制策略;30kW容量;三相半桥逆变器;boost电路;前级设计;低电压穿越功能;报告内容;电路原理;PI参数设计;Bode和根轨迹分析;波形良好。,基于30kW容量两级式光伏并网系统的控制策略设计:低电压穿越及高效逆变技术研究

    毕业设计文本预测项目python源码+托尔斯泰《战争与和平》文本分析数据集-最新出炉.zip

    毕业设计文本预测项目python源码+托尔斯泰《战争与和平》文本分析数据集-最新出炉 关于数据集 背景: 该数据集包含列夫·托尔斯泰的《战争与和平》的全文,这是一部于 1869 年出版的开创性文学作品。作为公共领域文本,它为对文学分析、自然语言处理和历史研究感兴趣的研究人员和爱好者提供了丰富的资源。这部小说以俄国拿破仑战争为背景,探讨了战争、和平和人类状况的主题。 内容: 数据集由一个纯文本文件组成,其中包含《战争与和平》的完整叙述。文本已进行预处理,以方便分析和建模,使其适用于各种应用,包括文本挖掘、情感分析和机器学习项目。该文件可通过以下链接访问:战争与和平文本数据集。

    18 -广告部经理绩效考核表1.xlsx

    18 -广告部经理绩效考核表1

    永磁同步电机电流内环PR控制Simulink仿真模型:转速电流双闭环矢量控制,波形完美带原理说明与文献参考,永磁同步电机电流内环PR控制Matlab simulink仿真模型,参数已设置好,可直接运行

    永磁同步电机电流内环PR控制Simulink仿真模型:转速电流双闭环矢量控制,波形完美带原理说明与文献参考,永磁同步电机电流内环PR控制Matlab simulink仿真模型,参数已设置好,可直接运行。 属于PMSM转速电流双闭环矢量控制系统模型。 电流内环采用PR控制器,不需要旋转坐标变,在静止坐标下进行矢量控制,转速外环采用PI控制器。 波形完美,包含原理说明文档和参考文献。 ,关键词:永磁同步电机;电流内环PR控制;Matlab simulink仿真模型;PMSM转速电流双闭环矢量控制系统;PR控制器;PI控制器;波形完美;原理说明文档;参考文献。,"基于PR控制的永磁同步电机电流内环仿真模型:静止坐标矢量控制与波形解析"

    基于主从博弈理论的共享储能与综合能源微网优化运行策略研究:Stackelberg均衡下的优化调度与运行框架,基于主从博弈理论的共享储能与综合能源微网优化运行研究 关键词:主从博弈 共享储能 综合能源微

    基于主从博弈理论的共享储能与综合能源微网优化运行策略研究:Stackelberg均衡下的优化调度与运行框架,基于主从博弈理论的共享储能与综合能源微网优化运行研究 关键词:主从博弈 共享储能 综合能源微网 优化调度 参考文档:《基于主从博弈理论的共享储能与综合能源微网优化运行研究》完全复现 仿真平台:MATLAB yalmip+cplex 主要内容:代码主要做的是基于主从博弈理论的共享储能与综合能源微网优化运行研究,首先介绍了系统运行框架,分析了系统内各利益体的功能。 其次,分别针对微网运营商、共享储能服务商以及用户聚合商建立优化运行模型。 进一步,分析了微网运营商与用户聚合商间的博弈关系,提出共享储能背景下微网运营商与用户聚合商间的 Stackelberg 博弈模型,并证明Stackelberg 均衡解的存在性与唯一性。 最后,在 MATLAB平台上进行算例仿真,通过 Yalmip 工具与 CPLEX 求解器进行建模与求解,利用启发式算法与求解器相结合的方法优化微网运营商与用户聚合商的策略。 结果表明,本文所提模型所提模型不仅能有效权衡微网运营商与用户聚合商的利益,也实现了用户聚合商

Global site tag (gtag.js) - Google Analytics