JS获取元素尺寸和位置

获取元素尺寸的方法

1.通过clientWidth, offsetWidth, scrollWidth.

clientWidht, clientLeft, clientX, offsetWidth, scrollWidth等具体的含义看DOM

clientWidth=width+padding相当jQuery的innerWidth();

offsetWidth=width+padding+border相当于jQuery的outerWidth(),outerWidth(true) 则包括margin;

scrollWidth是元素的滚动宽度,包括padding.

clientWidth, offsetWidth, scrollWidth获取的是纯数字的值,但不能用于display:none;的元素,可用于visibility:hidden;的元素。

获取窗口的尺寸会有些不同,这涉及到渲染模式。

…盒模型的渲染在 Standards Mode和Quirks Mode是有很大差别的,在不声明Doctype的情况下,浏览器默认是Quirks Mode。所以为兼容性考虑,我们可能需要获取当前的文档渲染方式。document.compatMode正好派上用场,它有两种可能的返回值:BackCompat和CSS1Compat,对其解释如下:
BackCompat Standards-compliant mode is not switched on. (Quirks Mode)
CSS1Compat Standards-compliant mode is switched on. (Standards Mode)

当页面是怪异模式时,IE无法识别document.documentElement,所以var height = document.compatMode==”CSS1Compat”?document.documentElement.clientHeight : document.body.clientHeight;或者可以通过下面的函数直接获取尺寸。

function getWindowSize(){
 var w=document.documentElement.clientWidth||document.body.clientWidth;
 var h=document.documentElement.clientHeight||document.body.clientHeight;
 return{'w':w,'h':h};
}

其实也可以通过window.innerWidth和window.outerWidth获取屏幕的尺寸,不过IE并不支持。

获取文档的尺寸时,当页面是标准模式下,document.body.clientHeight则是文档的高度。而document.body.offsetHeight, document.body.scrollHeight, document.documentElement.offsetHeight,  document.documentElement.scrollHeight也是文档的高度,但在IE下document.documentElement.offsetHeight则是窗口的高度,很奇怪,自己也有点晕了。

2. 通过document.defaultView.getComputedStyle

IE并不支持这个方法,IE可以通过currentStyle方法。不过,两者在细节上据说还是有所区别的,document.defaultView.getComputedStyle获得的是绝对值,即em单位会转换成px,但currentStyle则不会。

function getStyle(obj,pro){
 return obj.currentStyle ?
 obj.currentStyle[pro] :
 document.defaultView.getComputedStyle(obj,null)[pro];
}

getStyle()函数获取的是带单位的值,可用于display:none;的元素,但此时无法获得未在样式中显示定义的属性,例如高度是自适应的元素,获得的值就是auto。如果元素未定义display:none;即使高度自适应,也可以获得实际的高度值。getStyle()获得的是计算后的样式值。

PS: 那如何获得隐藏的自适应元素的尺寸呢,js获取隐藏元素(display:none)的尺寸有解说。

PS: 如果用该方法获得float和opacity属性值,getStyle()函数则要做一些变动,可以参考prototype的getStyle()函数。

3. obj.style.width

obj.style.width获取的是带单位的值,但只能获取获取内嵌的样式,即<code>&lt;div style=”width:100px; height:100px”&gt;</code>这种形式的属性值,内部样式和外部样式中的属性值都无法获得。如果没有内联样式,获取的值就是空。通常也是通过obj.style来设置尺寸,不过要记得带单位。

获取元素位置的方法

1. getBoundingClientRect()

function getPos(obj){
 return {
   top:document.documentElement.scrollTop+obj.getBoundingClientRect().top,
   left:document.documentElement.scrollLeft+obj.getBoundingClientRect().left
 };
}

2. offsetTop和offsetLeft

function getPos(obj){
 var pos = {"top":0, "left":0};
 if (obj.offsetParent){
   while (obj.offsetParent){
     pos.top += obj.offsetTop;
     pos.left += obj.offsetLeft;
     obj = obj.offsetParent;
   }
 }else if(obj.x){
   pos.left += obj.x;
 }else if(obj.x){
   pos.top += obj.y;
 }
 return pos;
}

方法一的效率比较高,但是safari并不支持方法一,如果需要兼容safari还是得要方法二。

Location 对象

location

location

hash 设置或返回从井号 (#) 开始的 URL(锚)。
host 设置或返回主机名和当前 URL 的端口号。
hostname 设置或返回当前 URL 的主机名。
href 设置或返回完整的 URL。
pathname 设置或返回当前 URL 的路径部分。
port 设置或返回当前 URL 的端口号。
protocol 设置或返回当前 URL 的协议。
search 设置或返回从问号 (?) 开始的 URL(查询部分)。

多级联动select

必须承认我没有写出这个效果,思维很混乱。主要原因是我逻辑没有理清,这是重点,而后又在一些细节上纠结,使我一直觉得这个问题很复杂。对待一个问题,要先把它想得复杂——全面考虑问题,然后简单——理清思路,最后再复杂——注重细节。强调,逻辑分析很重要。

上网查看了一些代码,整理了两种方法,绝对不是原创。主要差别是在数据结构上。

方法一:array存储,Demo

方法二:json存储,Demo

一,背景知识–Select

这个问题主要处理对象是select,即对增加、删除select的option选项等。

1. 清空select

sel.length=0; 或 sel.options.length=0;   //两种写法都一样

2.添加option

(1.新建

var opt = document.createElement('option');
或 var opt = new Option(str, str);

(2.添加

try{
    sel.add(opt, null);    //IE6,7不支持  
}catch(ex){
    sel.add(opt);
}
或sel.appendchild(opt);
或sel.options.add(opt);//都能识别,不知道是否标准

3. 选中

获取:sel.selectedIndex, 返回数值,默认为0;设置:sel.options[i].selected= true/false。

二,关键点

这个问题概括为获取数据,填充数据,即两重循环。选择一个选项,则用通过循环获得和该选项相关联的数据;遍历之后的select,填充相应的数据。这里数据结构是关键,不同的数据结构就决定了读取的效率。个人觉得方法一的效率比较高。

方法一采用数组结构,更为准确的说法,是关联数组,最为巧妙的地方在于索引值的设计。通过遍历select,获得当前select的被选项目的索引号,并连成字符串,例如“1_1_1”,则通过array['1_1_1']就可以获取所需的数据。

方法二采用的是json结构,这种结构值得推荐。但在获取数据时,要遍历整个数组里的json对象,通过对比选中的select的value值和json对象的’val’属性值,以获取下级的数据。数据量较大时,这个方法确实不太好。

这两种方法,还有一些细节也不太相同,比较繁琐就不介绍了。不过被我重写后,有些代码也不够优雅,而且我希望这个控件可以提供较好的数据输入的接口,继续纠结中……

[转]JavaScript Tween算法及缓动效果

转自:http://www.cnblogs.com/cloudgamer/archive/2009/06/21/1369979.html

本文只摘取了文章重要的部分,详情请看原文。

暂时无法将演示代码直接插入页面中,无奈。

效果说明

Linear:无缓动效果;

Quadratic:二次方的缓动(t^2);

Cubic:三次方的缓动(t^3);

Quartic:四次方的缓动(t^4);

Quintic:五次方的缓动(t^5);

Sinusoidal:正弦曲线的缓动(sin(t));

Exponential:指数曲线的缓动(2^t);

Circular:圆形曲线的缓动(sqrt(1-t^2));

Elastic:指数衰减的正弦曲线缓动;

Back:超过范围的三次方缓动((s+1)*t^3 – s*t^2);

Bounce:指数衰减的反弹缓动。

每个效果都分三个缓动方式(方法),分别是:

easeIn:从0开始加速的缓动;

easeOut:减速到0的缓动;

easeInOut:前半段从0开始加速,后半段减速到0的缓动。

其中Linear是无缓动效果,没有以上效果。

参数说明

t: current time(当前时间);

b: beginning value(初始值);

c: change in value(变化量);

d: duration(持续时间)。

算法

同jquery-easing相同,只是写法不同。算法才是灵魂啊。

/*
算法来源:http://www.robertpenner.com/easing/
*/
var Tween = {
	Linear: function(t,b,c,d){ return c*t/d + b; },
	Quad: {
		easeIn: function(t,b,c,d){
			return c*(t/=d)*t + b;
		},
		easeOut: function(t,b,c,d){
			return -c *(t/=d)*(t-2) + b;
		},
		easeInOut: function(t,b,c,d){
			if ((t/=d/2) < 1) return c/2*t*t + b;
			return -c/2 * ((--t)*(t-2) - 1) + b;
		}
	},
	Cubic: {
		easeIn: function(t,b,c,d){
			return c*(t/=d)*t*t + b;
		},
		easeOut: function(t,b,c,d){
			return c*((t=t/d-1)*t*t + 1) + b;
		},
		easeInOut: function(t,b,c,d){
			if ((t/=d/2) < 1) return c/2*t*t*t + b;
			return c/2*((t-=2)*t*t + 2) + b;
		}
	},
	Quart: {
		easeIn: function(t,b,c,d){
			return c*(t/=d)*t*t*t + b;
		},
		easeOut: function(t,b,c,d){
			return -c * ((t=t/d-1)*t*t*t - 1) + b;
		},
		easeInOut: function(t,b,c,d){
			if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
			return -c/2 * ((t-=2)*t*t*t - 2) + b;
		}
	},
	Quint: {
		easeIn: function(t,b,c,d){
			return c*(t/=d)*t*t*t*t + b;
		},
		easeOut: function(t,b,c,d){
			return c*((t=t/d-1)*t*t*t*t + 1) + b;
		},
		easeInOut: function(t,b,c,d){
			if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
			return c/2*((t-=2)*t*t*t*t + 2) + b;
		}
	},
	Sine: {
		easeIn: function(t,b,c,d){
			return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
		},
		easeOut: function(t,b,c,d){
			return c * Math.sin(t/d * (Math.PI/2)) + b;
		},
		easeInOut: function(t,b,c,d){
			return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
		}
	},
	Expo: {
		easeIn: function(t,b,c,d){
			return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
		},
		easeOut: function(t,b,c,d){
			return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
		},
		easeInOut: function(t,b,c,d){
			if (t==0) return b;
			if (t==d) return b+c;
			if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
			return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
		}
	},
	Circ: {
		easeIn: function(t,b,c,d){
			return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
		},
		easeOut: function(t,b,c,d){
			return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
		},
		easeInOut: function(t,b,c,d){
			if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
			return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
		}
	},
	Elastic: {
		easeIn: function(t,b,c,d,a,p){
			if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
			if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
			else var s = p/(2*Math.PI) * Math.asin (c/a);
			return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
		},
		easeOut: function(t,b,c,d,a,p){
			if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
			if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
			else var s = p/(2*Math.PI) * Math.asin (c/a);
			return (a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b);
		},
		easeInOut: function(t,b,c,d,a,p){
			if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5);
			if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
			else var s = p/(2*Math.PI) * Math.asin (c/a);
			if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
			return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
		}
	},
	Back: {
		easeIn: function(t,b,c,d,s){
			if (s == undefined) s = 1.70158;
			return c*(t/=d)*t*((s+1)*t - s) + b;
		},
		easeOut: function(t,b,c,d,s){
			if (s == undefined) s = 1.70158;
			return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
		},
		easeInOut: function(t,b,c,d,s){
			if (s == undefined) s = 1.70158;
			if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
			return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
		}
	},
	Bounce: {
		easeIn: function(t,b,c,d){
			return c - Tween.Bounce.easeOut(d-t, 0, c, d) + b;
		},
		easeOut: function(t,b,c,d){
			if ((t/=d) < (1/2.75)) {
				return c*(7.5625*t*t) + b;
			} else if (t < (2/2.75)) {
				return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
			} else if (t < (2.5/2.75)) {
				return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
			} else {
				return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
			}
		},
		easeInOut: function(t,b,c,d){
			if (t < d/2) return Tween.Bounce.easeIn(t*2, 0, c, d) * .5 + b;
			else return Tween.Bounce.easeOut(t*2-d, 0, c, d) * .5 + c*.5 + b;
		}
	}
}

日期控件

calendar

兼容:FF3.0, IE 6~8, safari 4.0, Opare 9.64, chrome 2.0

由于工作的需要,自己写了一个日期控件,开始想的很简单,在后期的使用过程中出现越来越多的BUG。近期对这个控件进行了改进。

一,计数月的天数

方法1

这是比较传统的方法,列出每个月的天数,其中二月是通过判断闰年来确定的。

var mon = new Array(12);
mon[0] = 31; mon[1] = 28; mon[2] = 31; mon[3] = 30; mon[4]  = 31; mon[5]  = 30;
mon[6] = 31; mon[7] = 31; mon[8] = 30; mon[9] = 31; mon[10] = 30; mon[11] = 31;
if (0==year%4&&((year%100!=0)||(year%400==0))){
   mon[1] = 29;
}

方法2

在JS中,月是以0开头的。而一个月的第0天是上一个月的最后天,因此计算2009年8月有几天如下:

var num = new Date(2009, (7+1), 0).getDate();

二,document.body & document

日期控件中点击除输入框、按钮和日期控件之外的地方,日期控件会关闭。开始是在documnet.body上绑定onclick事件。在演示页面中,body的高度远小于浏览器的可视区域(浏览器全屏的情况下),那么用户点击非body区域是无法关闭日期控件的。因此后把onclick事件绑定在document。

document.onclick = function(e){
		var e = e || window.event;
		var srcElement = e.srcElement || e.target;
		if( srcElement != outTxt && srcElement != outBtn && srcElement.className != "btn" ){
			calHide();
		}
	};

三,性能

在后期中发现原先控件在性能发面有点低(我之前在写JS中很少考虑性能,不过这次是太明显了)。在点击上一个月(年)或下一个月(年)都有一定的耗时,这是因为点击这些按钮都要重新生成当前月的日期。原先的做法是重新生成所有的a元素包括其中的内容;后期的做法是在初始化的时候生成a元素,刷新月份的时候只是刷新a标签中的内容。其中发现,FF不支持innerText,注意一下。

PS:在此谢谢小强同学的建议,虽然他很鄙视我的逻辑。

www.911xq.com
www.enshisjw.com