jQuery Mobile以下简称jm,是一个移动js框架。在jquery的基础上增加了针对移动设备的接口和ui组件。可以轻松的帮助用户实现好看的、可跨设备的Web应用程序。

因为项目需要,所以找时间研究了一下。之前对移动开发接触很少,没办法只是先简单的看了下jm的ui组件构建流程,以方便自己的项目参照使用。jm需要和jq结合,虽然功能很强大,但是不可避免的体积也很大。不过即使没法在项目中使用,学习jm的设计思想还是对手机上的web开发很有帮助的。

具体的也没有细看,先记录下jm的执行流程:
1. domReady,一切的开始当然是这个事件
2. initializePage,这是jm内部机制的开始,不管是ajax page还是本地page。
3. changePage,无论你设置了多少个page,只会显示一个
4. enhancePage
5. page.page(),jm的每一个组件都有一个同名的方法,相当于组件的入口
6. $.data( this, name, new object( options, this ) ); object为组件的构造器,基类是$.mobile,例如$.mobile.page,$.mobile.button等都是继承它。
7. 调用基类的_createWidget
8. 子类实现的_create()
9. _trigger(‘create’),这里每一个组件的事件都会被加上相应的前缀,例如page的就是pagecreate
10. 子类实现的_init(),触发pageinit事件,这也和官网上说明的事件顺序一致,用户应该注册pageinit而不是pagecreate。
11. 到这里page的基本流程差不多完成了,剩下的还有transitionPage等等不用太关注
12. removeClass(‘ui-mobile-rendering’),page流程结束,只有移除了htmlElement上的这个class,页面才能显示出来。
13. trigger(‘pagechange’)
14. 一些scroll的处理,domReady结束。

jm组件的auto self-init都是监听pagecreate事件,然后查找data-role规则。
如果不使用autoInit,也可以通过方法来构建,例如:$(‘a’).buttonMarkup({corners: “false”});
但是使用方法一次要把参数传完整,多次调用有些参数会不起作用~

markup就是jm的特色,组件可以通过tag上的属性来被构建。

jm的内部实现很复杂,但是思想还是值得借鉴的。今天先看到这里,日后在开发中在详细研究它的method和event~!

一个click的超链,肯定是限制性click,才会执行href。所以很多时候我们会在onclick中reture false来阻止href。但如果有target属性呢,在各浏览器的表现就不相同了。

前提

<a onclick="alert(2);" href="javascript:alert(1);" target="_blank">测试</a>

ie/firefox:
2 blank 1

chrome/safari:
如果href是js,就不执行href
2 1 no blank

有iframe的情况

<a onclick="alert(2);" href="javascript:alert(1);" target="newframe">点我</a>
<iframe id="newframe" name="newframe" src="test.html" width="320" height="240"></iframe><

chrome/safari中target依然被忽略,ie/firefox中先弹出2,然后执行iframe中的alert(1)!

从ie8开始为了贴近标准和向前兼容,微软在ie上搞出了这两个东西。

“浏览器模式”用于切换IE针对该网页的默认文档模式、对不同版本浏览器的条件备注解析、发送给网站服务器的用户代理(User-Agent)字符串的值。网站可以根据浏览器返回的不同用户代理字符串判断浏览器的版本和安装的功能,这样就可以向不同的浏览器返回不同的页面内容。

“文档模式”用于指定IE的页面排版引擎(Trident)以哪个版本的方式来解析并渲染网页代码。切换文档模式会导致网页被刷新,但不会更改用户代理字符串中的版本号,也不会从服务器重新下载网页。切换浏览器模式的同时,浏览器也会自动切换到相应的文档模式。

今天测试页面的时候发现全部以ie9兼容模式试图,ie7标准来显示,觉得很奇怪。检查了下即没有错误,也没有设置x-ua-compact。百思不得其解下偶然直接打开本地页面发现竟然好了,于是想会不会是域名的问题呢?上网一查果然是这个原因。原来微软有个兼容性视图列表,位于C:\Users\name\AppData\Local\Microsoft\Internet Explorer\IECompatData\iecompatdata.xml中,域名在这个列表里的网页都会以指定的模式显示。

另外在列表中的和制定了x-ua-compact模式的页面都不会显示兼容性试图按钮,只有ie自己检测出的不兼容页面才会显示那个按钮。

附上doctype对渲染模式的影响:

dtd模式

off方法的作用是移除事件处理函数,它可以接收很多类型的参数。有一种情况需要注意,如果第一个参数是$.Event对象,并且有handleObject这个属性,就说明是在事件处理函数中调用的off。

off: function( types, selector, fn ) {
        // event有handleObj这个属性说明是在callback中调用off,例如$.fn.one
		if ( types && types.preventDefault && types.handleObj ) {
			// off( event )  唯一参数jq.Event对象
			var handleObj = types.handleObj;
			jQuery( types.delegateTarget ).off(
				handleObj.namespace? handleObj.type + "." + handleObj.namespace : handleObj.type,
				handleObj.selector,
				handleObj.handler
			);
			return this;
		}
		if ( typeof types === "object" ) {
			// ( types-object [, selector] )
			for ( var type in types ) {
				this.off( type, selector, types[ type ] );
			}
			return this;
		}
		if ( selector === false || typeof selector === "function" ) {
			// ( types [, fn] )
			fn = selector;
			selector = undefined;
		}
		if ( fn === false ) {
			fn = returnFalse;
		}
		return this.each(function() {
			jQuery.event.remove( this, types, fn, selector );
		});
	}

off调用了$.event.remove,可以根据事件类型或命名空间来移除事件处理函数。 (更多…)

$.event.trigger的作用十分广泛,可以触发自定义事件,dom事件,独有事件,并且支持事件的传播和默认行为。同时jq的事件提供了namespace支持,这在使用trigger时候也需要注意:
1. 如果指定了namespace,就只会触发符合的事件或者其超集的事件。
2. 如果没有指定namespace,就会触发该type下的所有事件。
3. 如果没传element,会触发type下的全局事件。如$.ajax.success。
4. event path事件传播路径,最高可达到window,且不论是监听函数,还是inline函数都可以实现Propagation。
5. 有selector的事件就是代理事件,delegate event不会在同级触发。

源码附上:

trigger: function( event, data, elem, onlyHandlers ) {
		// Don't do events on text and comment nodes
		if ( elem && (elem.nodeType === 3 || elem.nodeType ===8) ) {
			return;
		}

		// Event object or event type  trigger('click', ...) trigger(e, ...)
		var type = event.type || event,
			namespaces = [],
			cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType;

        // 独有的事件  setData  getData...
		if ( type.indexOf( "!" ) >= 0 ) {
			// Exclusive events trigger only for the exact event (no namespaces)
			type = type.slice(0, -1);
			exclusive = true;
		}

        // 带有namespace的事件
		if ( type.indexOf( "." ) >= 0 ) {
			// Namespaced trigger; create a regexp to match event type in handle()
			namespaces = type.split(".");
			type = namespaces.shift();
			namespaces.sort();
		}

		if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
			// No jQuery handlers for this event type, and it can't have inline handlers
			return;
		}

		// Caller can pass in an Event, Object, or just an event type string
        // 包装成jq.Event对象
		event = typeof event === "object" ?
			// jQuery.Event object
			event[ jQuery.expando ] ? event :
			// Object literal
			new jQuery.Event( type, event ) :
			// Just the event type (string)
			new jQuery.Event( type );

		event.type = type;
		event.isTrigger = true;
        // dispatch中用到
		event.exclusive = exclusive;
		event.namespace = namespaces.join( "." ); // event.handleObject.namespace

        // 如果tirgger指定了namespace,就会有这个判断,否则会触发type下所有的handle
		event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
		// 行内handle
        ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";

		// triggerHandler() and global events don't bubble or run the default action
		if ( onlyHandlers || !elem ) {
			event.preventDefault();
		}

		// 如果没传elem,触发cache中所有符合type的事件,ajax中全局事件会用到
		if ( !elem ) {

			// TODO: Stop taunting the data cache; remove global events and always attach to document
			cache = jQuery.cache;
			for ( i in cache ) {
				if ( cache[ i ].events && cache[ i ].events[ type ] ) {
					jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
				}
			}
			return;
		}

		// Clean up the event in case it is being reused
		event.result = undefined;
		if ( !event.target ) {
			event.target = elem;
		}

		// 创建handle的参数列表,将event作为第一个参数
		data = data != null ? jQuery.makeArray( data ) : [];
		data.unshift( event );

		// Allow special events to draw outside the lines
		special = jQuery.event.special[ type ] || {};
		if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
			return;
		}

		// 确定event的传播路径, per W3C events spec (#9951)
		// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
		eventPath = [[ elem, special.bindType || type ]];
        // noBubble: focus、blur
		if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {

			bubbleType = special.delegateType || type;
			old = null;
			for ( cur = elem.parentNode; cur; cur = cur.parentNode ) {
				eventPath.push([ cur, bubbleType ]);
				old = cur;
			}

			// 只有到达document时才加入window (e.g., not plain obj or detached DOM)
			if ( old && old === elem.ownerDocument ) {
				eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
			}
		}

		// 在传播路径上触发事件处理句柄
		for ( i = 0; i < eventPath.length; i++ ) {

			cur = eventPath[i][0];
			event.type = eventPath[i][1];

            // 确定这个elem(cur)添加了事件处理函数
			handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
			if ( handle ) {
                // 执行的是dispatch
				handle.apply( cur, data );
			}
            // 执行inline handle
			handle = ontype && cur[ ontype ];
			if ( handle && jQuery.acceptData( cur ) ) {
				handle.apply( cur, data );
			}

            // 如果停止了传播,就推出循环
			if ( event.isPropagationStopped() ) {
				break;
			}
		}
		event.type = type;

		// If nobody prevented the default action, do it now
		if ( !event.isDefaultPrevented() ) {

			if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
				!(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {

				// 执行dom上与event同名的方法.
				// Can't use an .isFunction() check here because IE6/7 fails that test.
				// 不在window上执行,因为可能是全局的方法
				// IE<9 不能在隐藏element上执行focus、blur (#1486)
				if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {

					// Don't re-trigger an onFOO event when we call its FOO() method
					old = elem[ ontype ];

					if ( old ) {
						elem[ ontype ] = null; // 阻止elem.onXXX
					}

					// Prevent re-triggering of the same event, since we already bubbled it above
                    // 防止再次触发相同的事件
					jQuery.event.triggered = type; // 阻止dispatch
					elem[ type ]();
					jQuery.event.triggered = undefined;

					if ( old ) {
						elem[ ontype ] = old;
					}
				}
			}
		}

		return event.result;
	}

可以看出,trigger时会执行eventHandle也就是添加在dom上的监听函数$.event.dispath。这个方法针对每个event只会添加一次,它负责从$.cache中取得对应事件的所有handlers,并处理执行这些事件句柄。 (更多…)

jQuery1.7对event处理做了较大的调整,live、delegate、bind等方法全部改为由on来处理。至此jQuery对于dom事件,自定义事件,事件代理的添加及卸载包括触发全部统一了入口。下面就来分析一下on以及它内部用到的一系列方法。

// 参数selector就是使用事件代理的标志
on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
		var origFn, type;

		// Types can be a map of types/handlers
		if ( typeof types === "object" ) {
			// on( types-Object, selector, data )
			if ( typeof selector !== "string" ) {
				// on( types-Object, data )
				data = selector;
				selector = undefined;
			}
			for ( type in types ) {
				this.on( type, selector, data, types[ type ], one );
			}
			return this;
		}

		if ( data == null && fn == null ) {
			// on( types, fn )
			fn = selector;
			data = selector = undefined;
		} else if ( fn == null ) {
			if ( typeof selector === "string" ) {
				// ( types, selector, fn )
				fn = data;
				data = undefined;
			} else {
				// ( types, data, fn )
				fn = data;
				data = selector;
				selector = undefined;
			}
		}
		if ( fn === false ) {
			fn = returnFalse;
		} else if ( !fn ) { // 实在没传fn
			return this;
		}

        // 以上都是在处理不同的参数形式,one和之前版本一样,只执行一次handle
		if ( one === 1 ) {
			origFn = fn;
                        // 重写fn,添加off处理
			fn = function( event ) {
				// 可以用空集合,因为event包含了需要的信息
				jQuery().off( event );
				return origFn.apply( this, arguments );
			};
			// 设置guid,移除事件时会用到
			fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
		}
		return this.each( function() {
			jQuery.event.add( this, types, fn, data, selector );
		});
	}

由代码可见on只是做了一些准备工作,主要是处理参数,$.event.add才是核心方法。 (更多…)

对象字面量,可以是用{}或者new Object创建的,但是不能带有参数。
先看下用new Object创建对象的情况吧

1. 参数是一个对象,核心js对象(native ECMAScript object)或宿主对象(host object),那么将直接返回该对象。 
其生成的对象构造器仍然是所传参数对象的构造器。这样造成的后果是虽然该对象是new Object,但其constructor不一定是Object。

function Person(){this.name='jack';}
var w = new Object(window),
    d = new Object(document),
    p = new Object(new Person());

console.log(w.constructor); //-> Window
console.log(d.constructor); //-> HTMLDocument
console.log(p.constructor); //-> Person

2. 参数是基本类型对象,如字符串(String),数字(Number),布尔值(Boolean),将其包装成对象(转换成其对应的包装类)后返回。

var s = new Object('hello'),
    n = new Object(22),
    b = new Object(true);

console.log(typeof s); //-> Object
console.log(typeof n); //-> Object
console.log(typeof b); //-> Object

console.log(s.constructor); //-> String
console.log(n.constructor); //-> Number
console.log(b.constructor); //-> Boolean

所以我们判断plainObject的时候,需要对constructor及prototype做判断。
如果存在object.constructor,object本身没有constructor属性,并且object.constructor.prototype != Object.prototype,那么这个对象就不是plainObject。
所以jQuery中会判断if ( obj.constructor && !hasOwn.call(obj, “constructor”) && !hasOwn.call(obj.constructor.prototype, “isPrototypeOf”) ) { return false;}

写一段js代码或者一个js库,除了性能和通用性之外,最需要考虑的就是兼容性了。现在市面上的浏览器众多,作为前段开发人员不得不将各种古怪的情况考虑进去。

浏览器分析html代码,生成dom树,会为每一个dom节点建立与html 属性(attribute)对应的特性值(property)。有一些值会同步,如id、name。有的会有初始值及当前值之分,比如value。还有一些值得名字并不与html属性对应,如className。想获取、设置这些值,最好的是用两套方法,get/setAttr,get/setProp。但旧的习惯让我们把这两种值统称为属性。

首先来看attribute,对于html属性来说,使用标准Element接口提供的getAttribute/setAttribute就好,但是ie6,ie7的问题导致有的属性无法这样设置或获取。这种情况可以用getAttributeNode、createAttributeNode来解决。需要注意的是对于id,name这两个属性,即使不存在,ie8以下的浏览器也会返回一个空字符串而不是null。还有attributes这个集合,会关联所有规定可用的属性而标准浏览器不会这么做,所以还需要判断specified。

特性值通过dom 节点对象直接获取,可能还要对name做转换(class -> className、for -> htmlFor …)。

除次之外,浏览器还制造了一些奇怪的麻烦:

1. 在radio上重新设置type会导致value恢复初始值。

2. value的当前值与初始值的问题

3. tabindex在不同浏览器下不统一,@see:http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/

4. href、src的绝对路径和相对路径德问题

5. ie6、ie7下contenteditable不能设为空

6. ie9以下给input,button元素设置type会发生异常

7. ie6、ie7下getAttribute(‘style’)返回style对象