在写chart widget的时候,我们使用了第三方的svg类库Raphael。结果客户给我们报了一个内存泄漏的bug,我们在测试的时候确实存在内存泄漏。经过测试发现了问题的原因。Raphael本身存在一些问题,还有就是在和jQuery使用的时候,我们也没有注意到一个问题。

在创建jQuery对象的时候,jQuery会将jQuery缓存起来,放到jQuery.cache对象中。在调用jQuery的remove方法的时候,会清理掉jQuery的缓存。我们的问题就出在这里,在Raphael中,自己有remove方法来清楚Raphael创建的raphael对象和svg/vml元素。而在chart中,我们会使用jQuery在raphael创建的svg/vml元素上。这样在调用raphael的remove方法的时候,就不能移除jQuery缓存。由此造成了对象一直保留内存中。

在Raphael中,自己的animate和remove方法也存在说明,就是在animate的时候,会创建animate需要的一些对象缓存到raphael对象中,在调用stop方法的时候会清理掉这些元素。而当remove的时候,仅仅做了remove的事情,并没有移除掉这些对象。而在文档中也没有提到需要注意的问题。我们在使用的时候,就没有注意到正在做动画的元素在remove的时候直接调用remove方法,没有在之前调stop。这样也造成了对象不能销毁。

 

上述两个问题是造成内存泄漏的主要原因。在试用jQuery的时候,如果要删除dom对象的话,最好采用jQuery的remove方法,否则可能会出现诸如我们那样的问题。在jQuery的remove方法中还会清楚掉时间绑定等信息。

posted @ 2012-04-08 09:23 dail 阅读(381) 评论(0) 编辑

jQuery1.7在事件上做了改动,将事件绑定统一为on和off了,对以前的绑定方式继续支持. 但是在文档中没有提到的改动有以下两个:

就是jQuery的die和undelegate方法,原来在取消命名空间绑定的时候不需要前面加点,但是在1.7中是需要前面加点。如下:

1.7以前

$(".selector").die("namespace");

$(".selector").undelegate(".selector1", "namespace");

 

1.7以后

$(".selector").die(".namespace");

$(".selector").undelegate(".selector1", ".namespace");

posted @ 2012-04-08 09:09 dail 阅读(33) 评论(0) 编辑

在asp.net中的webcontrol开发的时候采用render方法来呈现控件。在javascript中,对于一些html代码的拼接大多数情况下采用字符串相加的方式来处理,这样做有以下弊端:

1.可读性差,易出错,出错后不易发现。

2.在javascript最小化的时候,长长的字符串不能最下化。

于是在项目中我实现了累死render的方法来处理html代码的拼接。

StringBuilder = function () {
		var self = this,
			strs = [];

		self.append = function (str) {
			strs.push(str);
		};

		self.toString = function () {
			return strs.join("");
		};

		self.dispose = function () {
			strs = null;
		};
	},

	TextElement = function (text) {
		this.text = text;

		this.render = function () {
			return this.text;
		};
	},

	HtmlElement = function (tagName, innerText, attributes) {
		var self = this,
			halfTags = { br: true, img: true, hr: true, input: true };

		self.tagName = tagName || "div";
		self.attributes = attributes || {};
		self.innerText = innerText || "";
		self.children = [];

		if ($.isPlainObject(innerText)) {
			self.innerText = "";
			self.attributes = innerText;
		}

		self._isShortTag = false;

		if (halfTags[self.tagName] === true) {
			self._isShortTag = true;
		}

		self.render = function () {
			var sb = new StringBuilder(),
				html = "";

			sb.append(self._renderBeginTag(self.tagName, self.attributes));
			if (self.innerText !== "") {
				sb.append(self.innerText);
			}

			if (self.children.length > 0) {
				$.each(self.children, function (index, ele) {
					sb.append(ele.render());
				});
			}

			sb.append(self._renderEndTag(self.tagName));
			html = sb.toString();
			sb.dispose();

			return html;
		};

		self.add = function (ele) {
			this.children.push(ele);
		};

		self._renderBeginTag = function (tagName, attributes) {
			var sb = new StringBuilder(),
				strRet = "";
			sb.append("<");
			sb.append(tagName);
			$.each(attributes, function (key, value) {
				sb.append(" ");
				sb.append(key);
				sb.append("=");
				sb.append("\"");
				sb.append(value.toString());
				sb.append("\"");
			});

			if (this._isShortTag) {
				sb.append(" ");
			} else {
				sb.append(">");
			}

			strRet = sb.toString();
			sb.dispose();

			return strRet;
		};

		self._renderEndTag = function (tagName) {
			var sb = new StringBuilder(),
				strRet = "";

			if (this._isShortTag) {
				sb.append("/>");
			} else {
				sb.append("</");
				sb.append(tagName);
				sb.append(">");
			}
			strRet = sb.toString();
			sb.dispose();
			return strRet;
		};
	};

最上面上在javascript中实现了一个stringBuilder类。处理字符串拼接的。用户用的时候采用下面的方法来调用HtmlElement这个类

var div = new HtmlElement("div", {class:"divcss"});
var innertextbox = new HtmlElement("input", {type:"text"});
div.add(innertextbox);
var str =  div.render();

str得到的结果是

<div class="divcss">
<input type="text" />
</div>

在使用的时候可以将创建textbox以及一些常用的html element封装成方法,这样就能提高代码的重用性,减少代码体积,也便于维护。

posted @ 2011-07-21 11:28 dail 阅读(358) 评论(0) 编辑

jquery 1.6已经发布了,在这个版本中最大的变化就是attr方法。原来我们通过这个方法取得jquery对象的属性。现在升级之后,原来采用attr方法的地方可能会出现问题。

新的attr方法只会取得DOM元素上设置的属性。而不会取得DOM元素内部的属性。比如说:

<input type="checkbox" checked="checked" />

在1.6版中,调用方法$(":checkbox").attr("checked")方法将返回"checked"值,而不是true.  而之前的版本则会返回true/false. 如果需要取得DOM的属性的时候,需要调用jquery提供的新方法prop(): $(":checkbox").prop("checked"). 在jquery 1.6中,这样设计应该主要上为了性能方面的考虑!

同时发现jquery的val()方法也许存在bug,就上不能对select元素取其值,取出的结果上undefined。但是官方文档上描述上可以的。

posted @ 2011-05-06 17:32 dail 阅读(584) 评论(0) 编辑

jQuery ui 内置了一组effects,包括:blind,bounce,clip,drop,explode,fade,fold,highlight,pulsate,puff,scale,size,shake,slide,transfer这些效果

这些effect可以通过$("selector").effect(effect,[option],[speed],[callback]),或者是jQuery中包装的show和hide方法调用。其参数和effect调用相同。

这里说明下参数的含义:

effect:就是效果名称,可以是上面列举的effects之一。

option:在不同的effect中包含不同的option。

speed:可以是数值,单位是ms,也可以是"slow","normal", 和 "fast"。

callback:就是执行完动画后的回调函数。

下面就每个effect的option做一个说明:

blind:

  mode:值为hide/show

   direction:值为vertical/horizontal


 bounce: 

   direction: 值为up/down/left/right

  distance: 值为数值,默认为20

  times: 值为数值,默认为5

  duration:值为数值,每个弹跳的速度,默认值250ms

  

 clip:

  mode:值为show/hide

   direction:值为vertical/horizontal


drop

  direction: 值为left/right/top/down

  mode: 值为show/hide

  distance:值为数值。


 explode

  pieces: 值为数字,爆炸的块数。默认为9

  mode: 值为show/hide/toggle


fade

  mode: 值为show/hide


fold

  mode:值为show/hide

  size: 值为数值,折叠的大小,默认为15

  horizFirst:bool值,是否先水平方向折叠

  

highlight

  mode: 值为show/hide

  color: 值为颜色值,默认为#ffff99


pulsate

  mode:值为show/hide

  times:值为数值,跳动的次数

  

puff

  mode:值为show/hide

  percent: 值为数值,膨胀的比例,默认为150

  

scale

  mode:值为show/hide

  percent: 值为数值

  direction:vertical/horizontal/both

  origin:原始大小

  from: 效果开始的大小


size

  mode:值为show/hide

  restore: bool类型,默认false

  scale:vertical/horizontal/both

  origin:原始大小

  from:从一定的高度和宽度开始

  to:到一定的高度和宽度结束

  

shake

   mode:值为show/hide

  direction: 值为left/right/up/down,默认值left

  distance: 值为数字,默认值20

  times:值为数字

  duration:每次效果的速度

   

slide

  mode:值为show/hide

  direction: 值为left/right/up/down

  distance: 值为数值

  

transfer

  to:目标jQuery对象

  className: 需要添加的css样式名称.


jQuery ui 为我们封装了上面的一些效果,在大部分的开发中,上述的效果基本可以满足我们的需求。而不需要我们自己另行开发。此外,在jQuery ui中封装了一组easing

可以在effect中搭配easing的使用,注意easing也是上述所有effect的option中的一个option,option的名称就是easing。    

posted @ 2011-01-14 17:30 dail 阅读(1138) 评论(5) 编辑

在开发widget的时候,发现了一个jquery ui position的一个bug。这个bug是有关collision的。这个功能就是当当前的定位的元素超出屏幕屏幕的时候,会自动翻转定位。比如说定位在右边,而右边不够显示定位元素的时候,就定位到左边。

下面我贴出代码来分析position的代码以及这个bug出现的位置。

 

/*
 * jQuery UI Position 1.8.6
 *
 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Position
 */
(function( $, undefined ) {

$.ui = $.ui || {};

var horizontalPositions = /left|center|right/,
	verticalPositions = /top|center|bottom/,
	center = "center",
	_position = $.fn.position,
	_offset = $.fn.offset;

$.fn.position = function( options ) {
	if ( !options || !options.of ) {
		return _position.apply( this, arguments );
	}

	// make a copy, we don't want to modify arguments
	options = $.extend( {}, options );

	var target = $( options.of ),
		targetElem = target[0],
		collision = ( options.collision || "flip" ).split( " " ),
		offset = options.offset ? options.offset.split( " " ) : [ 0, 0 ],
		targetWidth,
		targetHeight,
		basePosition;
    //如果相对定位的元素是document元素的话
	if ( targetElem.nodeType === 9 ) {
		targetWidth = target.width();
		targetHeight = target.height();
		basePosition = { top: 0, left: 0 };
	// TODO: use $.isWindow() in 1.9
	} else if ( targetElem.setTimeout ) {
		targetWidth = target.width();
		targetHeight = target.height();
		basePosition = { top: target.scrollTop(), left: target.scrollLeft() };
	} else if ( targetElem.preventDefault ) {
		// force left top to allow flipping
		options.at = "left top";
		targetWidth = targetHeight = 0;
		basePosition = { top: options.of.pageY, left: options.of.pageX };
	} else {
		targetWidth = target.outerWidth();
		targetHeight = target.outerHeight();
		basePosition = target.offset();
	}

	// force my and at to have valid horizontal and veritcal positions
	// if a value is missing or invalid, it will be converted to center 
	$.each( [ "my", "at" ], function() {
		var pos = ( options[this] || "" ).split( " " );
		if ( pos.length === 1) {
			pos = horizontalPositions.test( pos[0] ) ?
				pos.concat( [center] ) :
				verticalPositions.test( pos[0] ) ?
					[ center ].concat( pos ) :
					[ center, center ];
		}
		pos[ 0 ] = horizontalPositions.test( pos[0] ) ? pos[ 0 ] : center;
		pos[ 1 ] = verticalPositions.test( pos[1] ) ? pos[ 1 ] : center;
		options[ this ] = pos;
	});

	// normalize collision option
	if ( collision.length === 1 ) {
		collision[ 1 ] = collision[ 0 ];
	}

	// normalize offset option
	offset[ 0 ] = parseInt( offset[0], 10 ) || 0;
	if ( offset.length === 1 ) {
		offset[ 1 ] = offset[ 0 ];
	}
	offset[ 1 ] = parseInt( offset[1], 10 ) || 0;

	if ( options.at[0] === "right" ) {
		basePosition.left += targetWidth;
	} else if (options.at[0] === center ) {
		basePosition.left += targetWidth / 2;
	}

	if ( options.at[1] === "bottom" ) {
		basePosition.top += targetHeight;
	} else if ( options.at[1] === center ) {
		basePosition.top += targetHeight / 2;
	}

	basePosition.left += offset[ 0 ];
	basePosition.top += offset[ 1 ];

	return this.each(function() {
		var elem = $( this ),
			elemWidth = elem.outerWidth(),
			elemHeight = elem.outerHeight(),
			marginLeft = parseInt( $.curCSS( this, "marginLeft", true ) ) || 0,
			marginTop = parseInt( $.curCSS( this, "marginTop", true ) ) || 0,
         //此处就是出现问题的地方,在ie下面如果当前元素的margin没有设置或者设置为auto的话,在取$.curCss(this,"marginRight",true)的时候会去取出为
         //"auto",此问题在其他的浏览器下,不会取出为auto,而是当前元素的css实际值。,在转换的时候会为NAN,这个时候再做前面的运算,都将为NAN,
         //最终此值将被计算为0. 而这样,在下面做反转的时候,将会计算出错误的值,而不会发生发转。目前的解决方法就是在当前的元素上加上
         //margin-right和margin-bottom的值。
  
			collisionWidth = elemWidth + marginLeft +
				parseInt( $.curCSS( this, "marginRight", true ) ) || 0,
			collisionHeight = elemHeight + marginTop +
				parseInt( $.curCSS( this, "marginBottom", true ) ) || 0,
			position = $.extend( {}, basePosition ),
			collisionPosition;

		if ( options.my[0] === "right" ) {
			position.left -= elemWidth;
		} else if ( options.my[0] === center ) {
			position.left -= elemWidth / 2;
		}

		if ( options.my[1] === "bottom" ) {
			position.top -= elemHeight;
		} else if ( options.my[1] === center ) {
			position.top -= elemHeight / 2;
		}

		// prevent fractions (see #5280)
		position.left = parseInt( position.left );
		position.top = parseInt( position.top );

		collisionPosition = {
			left: position.left - marginLeft,
			top: position.top - marginTop
		};

		$.each( [ "left", "top" ], function( i, dir ) {
			if ( $.ui.position[ collision[i] ] ) {
				$.ui.position[ collision[i] ][ dir ]( position, {
					targetWidth: targetWidth,
					targetHeight: targetHeight,
					elemWidth: elemWidth,
					elemHeight: elemHeight,
					collisionPosition: collisionPosition,
					collisionWidth: collisionWidth,
					collisionHeight: collisionHeight,
					offset: offset,
					my: options.my,
					at: options.at
				});
			}
		});

		if ( $.fn.bgiframe ) {
			elem.bgiframe();
		}
		elem.offset( $.extend( position, { using: options.using } ) );
	});
};
//此处就是做翻转的函数的。
$.ui.position = {
	fit: {
		left: function( position, data ) {
			var win = $( window ),
				over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft();
			position.left = over > 0 ? position.left - over : Math.max( position.left - data.collisionPosition.left, position.left );
		},
		top: function( position, data ) {
			var win = $( window ),
				over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop();
			position.top = over > 0 ? position.top - over : Math.max( position.top - data.collisionPosition.top, position.top );
		}
	},

	flip: {
		left: function( position, data ) {
			if ( data.at[0] === center ) {
				return;
			}
			var win = $( window ),
				over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(),
				myOffset = data.my[ 0 ] === "left" ?
					-data.elemWidth :
					data.my[ 0 ] === "right" ?
						data.elemWidth :
						0,
				atOffset = data.at[ 0 ] === "left" ?
					data.targetWidth :
					-data.targetWidth,
				offset = -2 * data.offset[ 0 ];
			position.left += data.collisionPosition.left < 0 ?
				myOffset + atOffset + offset :
				over > 0 ?
					myOffset + atOffset + offset :
					0;
		},
		top: function( position, data ) {
			if ( data.at[1] === center ) {
				return;
			}
			var win = $( window ),
				over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(),
				myOffset = data.my[ 1 ] === "top" ?
					-data.elemHeight :
					data.my[ 1 ] === "bottom" ?
						data.elemHeight :
						0,
				atOffset = data.at[ 1 ] === "top" ?
					data.targetHeight :
					-data.targetHeight,
				offset = -2 * data.offset[ 1 ];
			position.top += data.collisionPosition.top < 0 ?
				myOffset + atOffset + offset :
				over > 0 ?
					myOffset + atOffset + offset :
					0;
		}
	}
};

// offset setter from jQuery 1.4
if ( !$.offset.setOffset ) {
	$.offset.setOffset = function( elem, options ) {
		// set position first, in-case top/left are set even on static elem
		if ( /static/.test( $.curCSS( elem, "position" ) ) ) {
			elem.style.position = "relative";
		}
		var curElem   = $( elem ),
			curOffset = curElem.offset(),
			curTop    = parseInt( $.curCSS( elem, "top",  true ), 10 ) || 0,
			curLeft   = parseInt( $.curCSS( elem, "left", true ), 10)  || 0,
			props     = {
				top:  (options.top  - curOffset.top)  + curTop,
				left: (options.left - curOffset.left) + curLeft
			};
		
		if ( 'using' in options ) {
			options.using.call( elem, props );
		} else {
			curElem.css( props );
		}
	};

	$.fn.offset = function( options ) {
		var elem = this[ 0 ];
		if ( !elem || !elem.ownerDocument ) { return null; }
		if ( options ) { 
			return this.each(function() {
				$.offset.setOffset( this, options );
			});
		}
		return _offset.call( this );
	};
}

}( jQuery ));

 

posted @ 2010-12-13 09:46 dail 阅读(708) 评论(0) 编辑
摘要: 这个widget是包含在wijmo 项目中的一个widget。目前此widget已经开源基于mit和gpl双协议,目前是beta版,可能存在bug。此项目的demo网站http://wijmo.com/Wijmo-Open/samples/[代码]          // If have animation.200 if (o.animationOptions.animated && o.animationOptions.duration > 0) {201 setTimeout($.proxy(function () {202 var o = self.options.animationOp阅读全文
posted @ 2010-10-29 11:11 dail 阅读(418) 评论(2) 编辑
摘要: 在编写widget的时候,一般我们需要绑定一些事件,最好将这些widget的绑定事件加上当前widget的命名空间。如果同一个jQuery对象,使用了两个widget,而两个widget都绑定了相同的事件名称,可能会出现问题。在销毁widget的时候,去除绑定事件也很方便,只需要unbind(”.namespace“)就可以了。在写jQuery的时候,因为jQuery对象是...阅读全文
posted @ 2010-10-27 14:31 dail 阅读(1225) 评论(3) 编辑
摘要: 在开发progress bar 的widget的时候,有这样的一个需求,需要progress bar在运行的时候,可以按照一定的间隔值来滚动。而不是连续的滚动(比如滚动20px后,停顿下,在接着滚动20px)。由于在做widget的时候,我的animation是作用在progress 的那个div的。当时是这样写的progress.animate({width:value},{step:funct...阅读全文
posted @ 2010-08-24 16:14 dail 阅读(1592) 评论(0) 编辑
摘要: 这里是一个简单的widget,在代码里也写了注释。 代码Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->//此widget是将textbox进行修饰一下的。自身没有css,采用的是jqueryuicssframework的样式(function($){...阅读全文
posted @ 2010-06-29 18:31 dail 阅读(1111) 评论(0) 编辑