/*!
- JQ库版本 v1.0.0
- 个人网站 http://MrRetro.github.io/
- 作者 : Mr.Retro
- 邮箱: caijiajia1@126.com
- QQ号: 395336343
- 交流群: 584861789
- Date: 2018/02/10
*/
//自调用结构,沿用1.7版本
(function(w){
var version = '1.0.0';
// 对外暴露的工厂函数
function jQuery( selector ){
return new jQuery.fn.init( selector );
}
// 给原型提供一个简写方式
jQuery.fn = jQuery.prototype ={
constructor: jQuery,
// 获取版本号
jquery: version,
// 代表所有实例默认的选择器,也代表实例是一个jQuery类型的对象
selector: '',
// 代表所有实例默认的长度
length: 0,
// 把实例转换为数组返回
toArray: function() {
return [].slice.call( this );
},
// 获取指定下标的元素,获取的是原生DOM
get: function( i ) {
/*
* 1、如果传入null或undefined,那么转换为数组返回
* 2、如果传入的是正数,按照指定的下标获取元素返回
* 3、如果传入的是负数,按照下标倒着( this.length + 负数 )获取元素返回
* */
// null、undeinfed
if ( i == null ) {
return this.toArray();
}
// 其他
if ( i >= 0 ) {
return this[ i ];
}else {
return this[ this.length + i ];
}
},
_get: function( i ) {
return i == null?
this.toArray() :
( i >= 0? this[ i ] : this[ this.length + i ] );
},
// 遍历实例
each: function( fn ) {
return jQuery.each( this, fn );
},
// 通过实例得到一个新数组
map: function( fn ) {
return jQuery.map( this, fn );
},
// 截取实例的部分元素,构成一个新的jQuery实例返回
slice: function() {
/*
* 1、通过数组的slice截取部分元素(slice返回的是数组),
* 2、把截取到的元素转换为实例对象返回。
* */
// 因为slice的参数会有变化,所以需要是arguments,
// 我们要把arguments中的每一项传给数组的slice,所以需要借用apply平铺传递过去,
// 最后把slice返回数组,通过jQuery工厂保证成实例返回。
var nodes = [].slice.apply( this, arguments );
return jQuery( nodes );
},
_slice: function() {
// 因为slice的参数会有变化,所以需要是arguments,
// 我们要把arguments中的每一项传给数组的slice,所以需要借用apply平铺传递过去,
// 最后把slice返回数组,通过jQuery工厂保证成实例返回。
return jQuery( [].slice.apply( this, arguments ) );
},
// 获取指定下标的元素,获取的是jQuery类型的实例对象。
eq: function( i ) {
/*
* 1、如果传入null或undefined,返回一个新实例,
* 2、如果传入的是正数,按照指定的下标获取元素,再包装成新实例返回
* 3、如果传入的是负数,按照下标倒着( this.length + 负数 )获取元素,再包装成新实例返回
* */
// null、undefined得到新实例
if( i == null ) {
return jQuery();
}
if( i >= 0 ) {
return jQuery( this[ i ] );
}else {
return jQuery( this[ this.length + i ] );
}
},
_eq: function( i ) {
return i == null? jQuery() : jQuery( this.get( i ) );
},
// 获取实例中的第一个元素,是jQuery类型的实例对象。
first: function() {
return this.eq( 0 );
},
// 获取实例中的最后一个元素,是jQuery类型的实例对象。
last: function() {
return this.eq( -1 );
},
// 原型上的方法供实例调用,
// 即实例.push,在调用过程中,push内的this就指向了实例,
// 所以这里不需要通过call和apply改变this指向即可借用数组的方法
push: [].push,
sort: [].sort,
splice: [].splice
}
// 添加extend方法
/*
* 需求:
* 1、传入一个参数,谁调用就给谁混入内容
* 2、传入多个参数,把后面对象的内容混入到第一个对象中
**/
jQuery.extend = jQuery.fn.extend = function() {
// 被混入的目标
var target = arguments[ 0 ];
// 传入一个参数,把这个参数的内容混入到this中
if( arguments.length === 1 ){
target = this;
for ( var key in arguments[ 0 ] ) {
target[ key ] = arguments[ 0 ][ key ];
}
}else if( arguments.length >= 2 ){
// 遍历得到后面所有的对象
for( var i = 1, len = arguments.length; i < len; i++){
// 遍历每一个对象多有的属性
for( var key in arguments[ i ] ){
// 把后面对象的内容混入到第一个对象中
target[ key ] = arguments[ i ][ key ];
}
}
}
return target;
}
// 给jQuery添加一些静态方法
jQuery.extend({
// ajax初始配置
ajaxSettings: {
url: location.href, // 默认的url为本地地址
type: "GET", // 默认请求的方法为GET
async: true, // 默认为异步请求
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
timeout: null, // 默认不看延迟事件
dataType: 'JSON', // 默认请求的回来的数据类型
success: function(){},
error: function(){},
complete: function(){},
},
// 把对象装换为url参数形式的字符串
urlStringify: function( obj ){
var result = '', key;
// 传入的不是对象,就直接返回空字符串
if( !jQuery.isObject( obj )){
return result;
}
for( key in obj ){
result += window.encodeURIComponent( key ) + '=' + window.encodeURIComponent( obj[ key] ) + '&';
}
// 从0截取到倒数第一个字符串返回、
return result.slice( 0, -1 );
},
// 加工options
processOptions: function( options ){
var optionsNew;
// 合并用户和默认的配置,得到一份新的
optionsNew = {};
jQuery.extend( optionsNew, jQuery.ajaxSettings, options );
// 对data进行加工处理
optionsNew.data = jQuery.urlStringify( optionsNew.data );
// 把dataType同意转换为大写,防止意外
optionsNew.dataType = optionsNew.dataType.toUpperCase();
// 把type同意转换为大写,防止意外
optionsNew.type = optionsNew.type.toUpperCase();
// 如果是GET请求,把数据加到URL中
if( optionsNew.type === 'GET' ){
optionsNew.url += '?' + optionsNew.data;
optionsNew.data = null;
}
// 返回加工后的配置
return optionsNew;
},
// ajax封装
ajax: function( options ){
var optionsNew, xhr, result, timer;
// 加工得到一份处理好的配置项
optionsNew = jQuery.processOptions( options );
// 创建xhr对象,发送请求
xhr = new XMLHttpRequest();
xhr.open( optionsNew.type, optionsNew.url, optionsNew.async );
if( optionsNew.type === 'POST' ){
xhr.setRequestHeader( 'Content-Type', optionsNew.contentType );
}
xhr.onreadystatechange = function(){
//先判断请求是否完成,完成就执行complete方法
if( xhr.readyState === 4 ){
// 在指定事件内完成请求,那么清除定时器
clearTimeout( timer );
optionsNew.complete();
// 判断是否请求成功,成果过就执行success方法,否则就执行error方法
if( ( xhr.status >= 200 && xhr.status < 300) || xhr.status === '304' ){
// 根据预期的dataType进行数据转换
switch( optionsNew.dataType ){
case 'JSON':
result = JSON.parse( xhr.responseText );
break;
case 'SCRIPT':
eval( xhr.responseText );
result = xhr.responseText;
break;
case 'STYLE':
$('<style></style>').html( xhr.responseText ).appendTo( 'head' );
result = xhr.responseText;
break;
default:
result = xhr.responseText;
break;
}
optionsNew.success( result );
}else{
optionsNew.error( xhr.status );
}
}
};
// 如果设置超时,那么开启一个定时器
if( optionsNew.timeout ){
// 在指定时间内,请求还没有完成
// 那么直接调用error方法报错
timer = setTimeout( function(){
// 超时执行error
optionsNew.error( '超时' );
// error执行了,事件回调就没有必要执行了
xhr.onreadystatechange = null;
}, optionsNew.timeout);
}
xhr.send( optionsNew.data );
},
//get请求
get: function( url, data, fn ){
// 如果传入两个参数,默认为第二个参数为回调
fn = fn || data || function(){};
jQuery.ajax({
url: url,
data: data,
success: fn
});
},
//post请求
post: function( url, data, fn ){
// 如果传入两个参数,默认为第二个参数为回调
fn = fn || data || function(){};
jQuery.ajax({
url: url,
type:'post',
data: data,
success: fn
});
},
//json请求
getJson: function( url, data, fn ){
// 如果传入两个参数,默认为第二个参数为回调
fn = fn || data || function(){};
jQuery.ajax({
url: url,
type:'json',
data: data,
success: fn
});
},
//css请求
getStyle: function( url, data, fn ){
// 如果传入两个参数,默认为第二个参数为回调
fn = fn || data || function(){};
jQuery.ajax({
url: url,
type:'style',
data: data,
success: fn
});
},
// 兼容添加事件
addEvent: function( ele, type, fn ){
// ele必须是DOM, type必须是字符串, fn必须是函数.
// 有一个不是,那就直接return
if( !ele.nodeType || !jQuery.isString( type ) || !jQuery.isFunction( fn )){
return;
}
// 兼容绑定事件
if( ele.addEventListener){
ele.addEventListener( type, fn );
}else{
ele.attachEvent( 'on' + type, fn );
}
},
// 兼容移除事件
removeEvent: function( ele, type, fn ){
// ele必须是DOM, type必须是字符串, fn必须是函数.
// 有一个不是,那就直接return
if( !ele.nodeType || !jQuery.isString( type ) || !jQuery.isFunction( fn )){
return;
}
// 兼容移除事件
if( ele.removeEventListener){
ele.removeEventListener( type, fn );
}else{
ele.detachEvent( 'on' + type, fn );
}
},
// 获取样式,已经处理了兼容性
getStyle: function( dom, style ){
//优先判断支不支持现代样式获取方式
if( window.getComputedStyle){
return window.getComputedStyle( dom )[ style ];
}else{
return dom.currentStyle[ style ];
}
},
// 遍历对象或类数组
each: function( obj, fn ) {
// 如果是数组或伪数组,按照顺序遍历
if ( 'length' in obj ) {
for ( var i = 0, len = obj.length; i < len; i++ ) {
// 改变回调执行时内部的this为val,
// 通过根据回调的返回值看看要不要中断遍历。
if ( fn.call( obj[ i ], i, obj[ i ] ) === false ) {
break;
}
}
}else {
for ( var key in obj ) {
// 改变回调执行时内部的this为val,
// 通过根据回调的返回值看看要不要中断遍历。
if ( fn.call( obj[ key ], key, obj[ key ] ) === false ) {
break;
}
}
}
return obj;
},
// map实现
map: function( obj, fn ) {
/*
* 1、先判断obj是不是数组或者伪数组,
* 2、如果是,则通过i的方式遍历这个对象
* 3、如果不是,则通过for in的方式遍历这个对象
* 4、在遍历的过程中,把每一次遍历到key和val分别传给回调。
* 5、在给回调传参的时候,需要收集回调的返回值,最后把所有的返回值构成新数组返回。
* */
var i, len, key, result = [];
if( 'length' in obj ) {
for ( i = 0, len = obj.length; i < len; i++ ) {
result.push( fn.call( obj[ i ], obj[ i ], i ) );
}
}else {
for ( key in obj ) {
result.push( fn.call( obj[ key ], obj[ key ], key ) );
}
}
return result;
},
// 去掉首尾空白字符
trim: function( str ) {
// null、undefined、NaN、0、false、''
if ( !str ) {
return str;
}
// 优先使用原生的
if ( str.trim ) {
return str.trim();
}
return str.replace( /^\s+|\s+$/g, '');
},
// 判断是不是html片段
isHTML: function( html ) {
// null、undefined、NaN、0、false、''
if ( !html ) {
return false;
}
// <.>
if( html.charAt(0) === '<' &&
html.charAt( html.length - 1 ) === '>' &&
html.length >= 3 ) {
return true;
}
return false;
},
// 判断是不是html片段
_isHTML: function( html ) {
return !!html &&
html.charAt(0) === '<' &&
html.charAt( html.length - 1 ) === '>' &&
html.length >= 3;
},
// 判断是不是函数
isFunction: function( fn ) {
if ( typeof fn === 'function' ) {
return true;
}
return false;
},
// 判断是不是函数
_isFunction: function( fn ) {
return typeof fn === 'function';
},
// 判断是不是window
isWindow: function( w ) {
// null、undefined、NaN、0、false、''
if( !w ) {
return false;
}
if( w.window === w ) {
return true;
}
return false;
},
// 判断是不是window
_isWindow: function( w ) {
return !!w && w.window === w;
},
// 判断是不是对象
isObject: function( obj ) {
// 防止typeof对null的误判
if ( obj === null ) {
return false;
}
// 如果是object或function,那就是对象
if ( typeof obj === 'object' || typeof obj === 'function' ) {
return true;
}
return false;
},
// 判断是不是字符串
isString: function( str ) {
if ( typeof str === 'string' ) {
return true;
}
return false;
},
// 判断是不是字符串
_isString: function( str ) {
return typeof str === 'string';
},
// 判断是不是真数组或伪数组
isLikeArray: function( arr ) {
// Function、window、!Object
if ( jQuery.isFunction( arr ) || jQuery.isWindow( arr ) || !jQuery.isObject( arr ) ) {
return false;
}
// 判断是不是真数组
if ( ({}).toString.call( arr ) === '[object Array]' ) {
return true;
}
// 判断是不是伪数组
if ( 'length' in arr && ( arr.length === 0 || arr.length - 1 in arr ) ) {
return true;
}
return false;
},
ready: function( fn ) {
// 先统一判断DOMContentloaded有没有触发,
// 通过document.readyState === 'complete'判断
// 如果为true,fn可以直接调用。
// 如果为false,那么判断支不支持addEventListener,
// 如果支持,绑定DOMContentLoaded事件
// 如果不支持,使用attchEvent绑定onreadystatechang事件,
// 注意,需要在里面判断document.readyState === 'complete'才执行fn。
// 防止fn多次执行。
// DOM已经构造完毕,fn可以直接执行
if ( document.readyState === 'complete' ) {
fn();
}
// 如果DOM没有构造完毕,那么判断addEventListener是否兼容
else if( document.addEventListener ) {
document.addEventListener( 'DOMContentLoaded', fn );
}
// 如果不兼容addEventListener,那么采取attachEvent的方式,
// 同时事件变为了onreadystatechange,为了防止这个事件多次触发造成的fn多次执行,
// 所以需要一个包装函数来进行过滤。
else {
document.attachEvent( 'onreadystatechange', function() {
if( document.readyState === 'complete' ) {
fn();
}
} );
}
}
});
//给原型添加方法
//插件方法
// 给原型扩展DOM操作方法,这样jQ实例就可以使用了
jQuery.fn.extend( {
// 绑定事件
on: function( type, fn ){
/*
* 实现思路:
* 1、遍历所有的元素
* 2、判断每一个元素有没有$_event_cache这个属性值
* 3、如果有则继续使用,没有则初始化为一个对象
* 4、在继续判断这个对象有没有对应事件类型的数组
* 5、如果没有,说明是第一次绑定该事件
* 5.1、那么需要给$_event_cache这个对象以type为key添加一个数组
* 5.2、然后把传入的回调push进去
* 5.3、最后还得绑定对应的事件(调用静态的addEvent方法即可)
* 5.4、这个事件回调里面去遍历对应事件的数组,得到每一个事件回调,依次执行
* 5.5、执行时,需要改变内部的this,还需要把事件对象传递过去
* 6、如果有,直接把传入的回调push到对应事件的数组就可以了
* 7、链式编程返回this
**/
this.each( function(){
// 这里的this代表遍历到的每一个元素
var self = this;
this.$_event_cache = this.$_event_cache || {};
// 如果之前没有对应事件的数组, 说明是第一次绑定事件
if( !this.$_event_cache[ type ] ){
this.$_event_cache[ type ] = [];
this.$_event_cache[ type ].push( fn );
// 如果是第一次绑定该事件,那么需要真正调用浏览器的方法绑定事件
jQuery.addEvent( this, type, function( e ){
for(var i = 0, len = self.$_event_cache[ type ].length; i < len; i++){
self.$_event_cache[ type ][ i ].call( self, e );
}
} );
}else{
this.$_event_cache[ type ].push( fn );
}
});
// 链式编程
return this;
},
_on: function( type, fn ){
/*
* 实现思路:
* 1、遍历所有的元素
* 2、判断每一个元素有没有$_event_cache这个属性值
* 3、如果有则继续使用,没有则初始化为一个对象
* 4、在继续判断这个对象有没有对应事件类型的数组
* 5、如果没有,说明是第一次绑定该事件
* 5.1、那么需要给$_event_cache这个对象以type为key添加一个数组
* 5.2、然后把传入的回调push进去
* 5.3、最后还得绑定对应的事件(调用静态的addEvent方法即可)
* 5.4、这个事件回调里面去遍历对应事件的数组,得到每一个事件回调,依次执行
* 5.5、执行时,需要改变内部的this,还需要把事件对象传递过去
* 6、如果有,直接把传入的回调push到对应事件的数组就可以了
* 7、链式编程返回this
**/
this.each( function(){
// 这里的this代表遍历到的每一个元素
var self = this;
this.$_event_cache = this.$_event_cache || {};
// 如果之前没有对应事件的数组, 说明是第一次绑定事件
if( !this.$_event_cache[ type ] ){
this.$_event_cache[ type ] = [];
this.$_event_cache[ type ].push( fn );
// 如果是第一次绑定该事件,那么需要真正调用浏览器的方法绑定事件
jQuery.addEvent( this, type, function( e ){
//遍历所有的回调
jQuery.each( self.$_event_cache[ type ],function( i, val ){
// 这里的this,值得是每一个回调函数
this.call( self, e )
});
} );
}else{
this.$_event_cache[ type ].push( fn );
}
});
// 链式编程
return this;
},
// 移除事件
off: function( type, fn ){
/*
* 实现思路:
* 1、如果没有传参,遍历所有的元素
* 2、然后遍历每一个元素的$_event_cache对象,分别清空这个对象中的每一个数组
* 3、如果没有$_event_cache 如果有$_event_cache继续判断
* 4、线判断有没有参数,没有参数 ()
* 5、如果传入1个参数,那么把元素$_event_cache对象指定类型的数组清空
* 6、如果传2个以上参数,那么把元素$_event_cache对象指定类型的数组中指定的回调删除即可
* 7、返回链式编程this
**/
var argLen = arguments.length;
this.each( function(){
// 没有绑定过任何事件, 就不用处理了
if( !this.$_event_cache ){
return;
}
// 如果绑过事件,需要进一步处理
else{
// 如果没有参数,遍历所有的事件数组,分别清空
if( argLen === 0 ){
for( var key in this.$_event_cache ){
this.$_event_cache[ key ] = [];
}
}
// 如果传入一个参数,则清空指定事件类型的数组
else if( argLen === 1 ){
this.$_event_cache[ type ] = [];
}
// 如果传入多个参数,则清空指定事件类型数组中指定的回调函数
else{
// 遍历对应事件类型的数组,得到每一个回调
for( var i = this.$_event_cache[ type ].length - 1; i >= 0; i--){
// 依次和传入的回调比较,如果相等,则从数组中删除
if( this.$_event_cache[ type ][ i ] === fn ){
this.$_event_cache[ type ].splice( i, 1 );
}
}
}
}
});
return this;
},
// 清空所有元素的内容
empty: function() {
/*
* 实现思路:
* 1、遍历likeArray( 可以考虑使用for遍历,也可以考虑使用each遍历 )
* 2、遍历到的每一个元素清除其内容( 元素.innerHTML = '' )
* */
for ( var i = 0, len = this.length; i < len; i++ ) {
this[ i ].innerHTML = '';
}
// 为了链式编程
return this;
},
// 清空所有元素的内容
_empty: function() {
/*
* 实现思路:
* 1、遍历likeArray( 可以考虑使用for遍历,也可以考虑使用each遍历 )
* 2、遍历到的每一个元素清除其内容( 元素.innerHTML = '' )
* */
this.each( function() {
// 这里面的this指向遍历到的每一个元素
this.innerHTML = '';
} );
// 为了链式编程
return this;
},
// 删除所有的元素
remove: function() {
/*
* 实现思路:
* 1、遍历likeArray( 可以考虑使用for遍历,也可以考虑使用each遍历 )
* 2、遍历到的每一个元素要删除掉
* ( 通过parentNode获取该元素的父元素,然后父元素.removeChild( 被删除元素 ) )
* */
for( var i = 0, len = this.length; i < len; i++ ) {
this[ i ].parentNode.removeChild( this[ i ] );
}
// 为了链式编程
return this;
},
// 删除所有的元素
_remove: function() {
/*
* 实现思路:
* 1、遍历likeArray( 可以考虑使用for遍历,也可以考虑使用each遍历 )
* 2、遍历到的每一个元素要删除掉
* ( 通过parentNode获取该元素的父元素,然后父元素.removeChild( 被删除元素 ) )
* */
this.each( function() {
// 这里面的this指向遍历到的每一个元素
this.parentNode.removeChild( this );
});
// 为了链式编程
return this;
},
// 设置所有元素的内容,获取第一个元素的内容
html: function( html ) {
/*
* 实现思路:
* 1、先通过arguments.length判断有没有传参
* 2、没有传,则获取第一个元素,然后返回这个元素的innnerTHML内容
* 3、如果传了,则遍历likeArray( 可以考虑使用for遍历,也可以考虑使用each遍历 )
* 4、再设置每一个元素的innerTHML为传入的参数。
* */
// 如果没有传参,那么就直接返回第一个元素的innerHTML
if ( arguments.length === 0 ) {
return this[ 0 ].innerHTML;
}
// 如果传参了,那么遍历所以元素,分别设置其innerTHML值为传入的html
else if( arguments.length >= 1 ) {
for ( var i = 0, len = this.length; i < len; i++ ) {
this[ i ].innerHTML = html;
}
}
// 为了链式编程
return this;
},
// 设置所有元素的内容,获取第一个元素的内容
_html: function( html ) {
/*
* 实现思路:
* 1、先通过arguments.length判断有没有传参
* 2、没有传,则获取第一个元素,然后返回这个元素的innnerTHML内容
* 3、如果传了,则遍历likeArray( 可以考虑使用for遍历,也可以考虑使用each遍历 )
* 4、再设置每一个元素的innerTHML为传入的参数。
* */
// 如果没有传参,那么就直接返回第一个元素的innerHTML
if ( arguments.length === 0 ) {
return this.get(0).innerHTML;
}
// 如果传参了,那么遍历所以元素,分别设置其innerTHML值为传入的html
else {
this.each( function() {
// 这里面的this指向遍历到的每一个元素
this.innerHTML = html;
});
}
// 为了链式编程
return this;
},
// 设置所有元素的文本内容,获取所有元素的文本内容
text: function( text ) {
/*
* 实现思路:
* 1、先通过arguments.length判断有没有传参
* 2、没有传,则遍历likeArray( 可以考虑使用for遍历,也可以考虑使用each遍历 )
* 3、把每一个元素的innerText加在一起返回
* 4、则遍历likeArray( 可以考虑使用for遍历,也可以考虑使用each遍历 )
* 5、再设置每一个元素的innerText为传入的参数。
* */
var result = '';
// 没有传参,获取所有元素的所有文本,然后返回
if ( arguments.length === 0 ) {
for ( var i = 0, len = this.length; i < len; i++ ) {
result += this[ i ].innerText;
}
return result;
}
// 如果传参数了,那么设置所有元素的文本
else {
for ( var i = 0, len = this.length; i < len; i++ ) {
this[ i ].innerText = text;
}
}
// 为了链式编程
return this;
},
// 设置所有元素的文本内容,获取所有元素的文本内容
_text: function( text ) {
/*
* 实现思路:
* 1、先通过arguments.length判断有没有传参
* 2、没有传,则遍历likeArray( 可以考虑使用for遍历,也可以考虑使用each遍历 )
* 3、把每一个元素的innerText加在一起返回
* 4、则遍历likeArray( 可以考虑使用for遍历,也可以考虑使用each遍历 )
* 5、再设置每一个元素的innerText为传入的参数。
* */
var result = '';
// 没有传参,获取所有元素的所有文本,然后返回
if ( arguments.length === 0 ) {
this.each( function() {
result += this.innerText;
});
return result;
}
// 如果传参数了,那么设置所有元素的文本
else {
this.each( function() {
this.innerText = text;
});
}
// 为了链式编程
return this;
},
// 把所有的元素添加到指定的元素中
appendTo: function( selector ) {
/*
* 实现思路:
* 1、定义一个数组,用来存储将来所有被添加的元素
* 2、使用jQuery包装一下selector,把不同的参数统一为jQ实例。
* 3、在外层遍历所有的元素(this)
* 4、在内层遍历所有的目标(包装后的jQ实例)
* 5、在内层判断,如果是第一次,则把外面遍历的元素本体添加到内层遍历的元素,
* 如果不是第一次,则把外面遍历的元素clone版本添加到内层遍历的元素。
* 6、最后把存储被添加元素的数组使用jQ包装一下,然后返回。
* */
// 存储被添加的元素
var result = [], tempNode = null;
// 无论传入的是DOM还是jQ对象还是选择器,
// 统一包装成新的JQ实例,这样就可以统一处理了
var $selector = $( selector );
// 遍历每一项元素( this )
for ( var i = 0, len = this.length; i < len; i++ ) {
// 遍历每一个目的地( $selector )
for ( var j = 0, jLen = $selector.length; j < jLen; j++ ) {
// 如果第一次,给遍历到的目的地添加本体
if ( j === 0 ) {
// 存储被添加的元素
tempNode = this[ i ];
// 把元素添加到目的地
$selector[ j ].appendChild( this[ i ] );
// append啥,就把啥存储到result数组中,最后包装成JQ对象返回
// 把元素存储到result数组中
result.push( this[ i ] );
}else {
// 存储被添加的元素
tempNode = this[ i ].cloneNode( true );
// 把元素添加到目的地
$selector[ j ].appendChild( tempNode );
// append啥,就把啥存储到result数组中,最后包装成JQ对象返回
// 把元素存储到result数组中
result.push( tempNode );
}
}
}
// 把所有被添加的元素保证成新实例返回,
// 这样可以对所有被添加的元素进行链式编程。
return jQuery( result );
},
// 把所有的元素添加到指定的元素中
_appendTo: function( selector ) {
/*
* 实现思路:
* 1、定义一个数组,用来存储将来所有被添加的元素
* 2、使用jQuery包装一下selector,把不同的参数统一为jQ实例。
* 3、在外层遍历所有的元素(this)
* 4、在内层遍历所有的目标(包装后的jQ实例)
* 5、在内层判断,如果是第一次,则把外面遍历的元素本体添加到内层遍历的元素,
* 如果不是第一次,则把外面遍历的元素clone版本添加到内层遍历的元素。
* 6、最后把存储被添加元素的数组使用jQ包装一下,然后返回。
* */
// 存储被添加的元素
var result = [], tempNode = null;
// 无论传入的是DOM还是jQ对象还是选择器,
// 统一包装成新的JQ实例,这样就可以统一处理了
var $selector = $( selector );
// 遍历每一项元素( this )
this.each( function() {
// 这里的this指向每一个被添加的元素
var self = this;
// 遍历每一个目的地( $selector )
$selector.each( function( i ) {
// 如果第一次,给遍历到的目的地添加本体
// 先把被添加的元素得到
tempNode = i === 0? self: self.cloneNode( true );
// 这里的this指向每一个目的地,给目的地添加外面遍历的元素
this.appendChild( tempNode );
result.push( tempNode );
});
});
// 把所有被添加的元素保证成新实例返回,
// 这样可以对所有被添加的元素进行链式编程。
return jQuery( result );
},
// 把所有的元素添加到指定的元素的最前面
prependTo: function( selector ){
/*
* 实现思路:
* 1、定义一个数组,用来存储将来所有被添加的元素
* 2、使用jQuery包装一下selector, 把不同的参数统一为jQ实例
* 3、在外层遍历所有的元素(this)
* 4、在内层遍历所有的目标(包装后的JQ实例)
* 5、在内测判断,如果是第一次,则把外面遍历的元素本体添加到内层遍历元素的最前面,
* 如果不是第一次,则把外面遍历的元素clone版本添加到内层遍历元素的最前面。
* 6、最后把存储被添加的元素的数组使用jQ包装一下,然后返回
**/
var result = [], tempNode;
// 无论传入的是DOM还是jQ对象还是选择器
// 统一包装成新的jQ实例, 这样就可以统一处理了
var $selector = jQuery( selector );
for( var i=0, len = this.length; i < len; i++){
// $selector
for( var j=0, jLen = $selector.length; j < jLen; j++){
// 先得到被添加的元素
tempNode = j === 0 ? this[ i ] : this[ i ].cloneNode( true );
// 添加到指定元素的最前面
$selector[ j ].insertBefore( tempNode, $selector[ j ].firstChild );
// 把被添加的元素存储起来
result.push( tempNode );
}
}
// 把所有被添加的元素保证成新实例返回,
// 这样可以对所有被添加的元素进行链式编程。
return jQuery( result );
},
// 把所有的元素添加到指定的元素的最前面
_prependTo: function( selector ){
/*
* 实现思路:
* 1、定义一个数组,用来存储将来所有被添加的元素
* 2、使用jQuery包装一下selector, 把不同的参数统一为jQ实例
* 3、在外层遍历所有的元素(this)
* 4、在内层遍历所有的目标(包装后的JQ实例)
* 5、在内测判断,如果是第一次,则把外面遍历的元素本体添加到内层遍历元素的最前面,
* 如果不是第一次,则把外面遍历的元素clone版本添加到内层遍历元素的最前面。
* 6、最后把存储被添加的元素的数组使用jQ包装一下,然后返回
**/
var result = [], tempNode;
// 无论传入的是DOM还是jQ对象还是选择器
// 统一包装成新的jQ实例, 这样就可以统一处理了
var $selector = jQuery( selector );
this.each(function(){
// 这里this指每一个被添加的元素
var self = this;
$selector.each(function( i ){
// 这里this指每一个被添加元素的目的地
console.log(i)
// 先得到被添加的元素
tempNode = i === 0? self: self.cloneNode( true );
// 添加到指定元素的最前面
this.insertBefore( tempNode, this.firstChild );
// 把被添加的元素存储起来
result.push( tempNode );
});
});
// 把所有被添加的元素保证成新实例返回,
// 这样可以对所有被添加的元素进行链式编程。
return jQuery( result );
},
// 给所有元素添加html内容,或者其他元素
append: function( context ){
/*
* 实现思路:
* 1、判断context是不是字符串
* 2、如果是,则把这个字符串累加给所有元素
* 3、如果不是,则先把context包装成jQ对象统一处理
* 4、外层遍历
* 5、内层遍历 4、5、6完全可以考虑复用appendTo
* 6、添加元素
* 7、返回this
**/
var $context = $( context );
// 如果是字符串,则累加给所有元素
if( jQuery.isString( context ) ){
for( var i = 0, len = this.length; i < len; i++ ){
this[ i ].innerHTML += context;
}
}
// 如果不是字符串,则把$context的每一项添加this的每一项中
else{
$context.appendTo( this );
}
return this;
},
// 给所有元素添加html内容,或者其他元素
_append: function( context ){
/*
* 实现思路:
* 1、判断context是不是字符串
* 2、如果是,则把这个字符串累加给所有元素
* 3、如果不是,则先把context包装成jQ对象统一处理
* 4、外层遍历
* 5、内层遍历 4、5、6完全可以考虑复用appendTo
* 6、添加元素
* 7、返回this
**/
var $context = $( context );
// 如果是字符串,则累加给所有元素
if( jQuery.isString( context ) ){
this.each(function(){
// 把得到的每一个元素进行innerHTML累加
this.innerHTML += context;
});
}
// 如果不是字符串,则把$context的每一项添加this的每一项中
else{
$context.appendTo( this );
}
return this;
},
// 给所有元素内容前添加html内容,或者其他元素
prepend: function( context ){
var $context = $( context );
// 如果是字符串,则累加给所有元素
if( jQuery.isString( context ) ){
this.each(function(){
// 把得到的每一个元素进行innerHTML累加
this.innerHTML = context + this.innerHTML;
});
}
// 如果不是字符串,则把$context的每一项添加this的每一项最前面
else{
$context.prependTo( this );
}
return this;
},
// 给所有元素内容前添加html内容,或者其他元素
_prepend: function( context ){
var $context = $( context );
// 如果是字符串,则累加给所有元素
if( jQuery.isString( context ) ){
this.each(function(){
// 把得到的每一个元素进行innerHTML累加
this.innerHTML = context + this.innerHTML;
});
}
// 如果不是字符串,则把$context的每一项添加this的每一项最前面
else{
$context.prependTo( this );
}
return this;
},
//给所有元素添加新的class
addClass: function( className ){
/*
* 实现思路:
* 1、遍历所有的元素
* 2、依次判断每一个元素有没有要添加的className
* 3、有则忽略,没有则添加( className += ' ' + classNames )
* 4、考虑trim一下
* 5、链式编程返回this
**/
for( var i = 0, len = this.length; i < len; i++ ){
if((' ' + this[ i ].className + ' ').indexOf(' ' + className + ' ') == -1){
this[ i ].className+=' '+ className;
}
}
//链式编程返回this
return this;
},
//给所有元素添加新的class
_addClass: function( className ){
/*
* 实现思路:
* 1、遍历所有的元素
* 2、依次判断每一个元素有没有要添加的className
* 3、有则忽略,没有则添加( className += ' ' + classNames )
* 4、考虑trim一下
* 5、链式编程返回this
**/
this.each(function(){
if((' ' + this.className + ' ').indexOf(' ' + className + ' ') == -1){
this.className+=' '+ className;
}
})
//链式编程返回this
return this;
},
//给所有元素添加新的class
__addClass: function( className ){
/*
* 实现思路:
* 1、遍历所有的元素
* 2、依次判断每一个元素有没有要添加的className
* 3、有则忽略,没有则添加( className += ' ' + classNames )
* 4、考虑trim一下
* 5、链式编程返回this
**/
this.each(function(){
if( !jQuery( this ).hasClass( className ) ){
this.className+=' '+ className;
}
})
//链式编程返回this
return this;
},
//删除所有元素指定的class
removeClass: function( className ){
/*
* 实现思路:
* 1、没有参数,遍历所有的元素, 设置他们的className为空
* 2、有参数,遍历所有的元素,删除指定的className(元素.className.replace())
* 把指定的className替换为空格,最后整体trim一下
* 3、链式编程返回this
**/
if( arguments.length === 0 ){
this.each( function(){
this.className = '';
});
}else{
this.each( function(){
this.className = (' ' + this.className + ' ').replace(' ' + className +' ', ' ');
});
}
},
//判断所有元素有没有指定的class
hasClass: function( className ){
/*
* 实现思路:
* 1、遍历所有的元素
* 2、依次获取每一个元素的className,为了方便判断,首尾加空格
* 3、利用处理过的className字符串的indexOf方法判断有没有指定的className(这个class类名)
* 4、如果有一个元素的判断结果不为-1,就返回true
* 5、否则返回false。
**/
for( var i = 0, len = this.length; i < len; i++ ){
if( (' ' + this[ i ].className + ' ').indexOf(' ' + className + ' ') > -1){
return true;
}
}
//如果所有的元素都没有 返回false
return false;
},
//判断所有元素有没有指定的class
_hasClass: function( className ){
/*
* 实现思路:
* 1、遍历所有的元素
* 2、依次获取每一个元素的className,为了方便判断,首尾加空格
* 3、利用处理过的className字符串的indexOf方法判断有没有指定的className(这个class类名)
* 4、如果有一个元素的判断结果不为-1,就返回true
* 5、否则返回false。
**/
//默认为所有的元素都没有指定的className
var has = false;
this.each( function(){
// 只要有一个元素存在指定的className,那么就修改has的变量值为true
if( (' ' + this.className + ' ').indexOf(' ' + className + ' ') > -1 ){
has = true;
}
});
//返回has
return has;
},
//判断所有元素有没有指定的class
__hasClass: function( className ){
/*
* 实现思路:
* 1、遍历所有的元素
* 2、依次获取每一个元素的className,为了方便判断,首尾加空格
* 3、利用处理过的className字符串的indexOf方法判断有没有指定的className(这个class类名)
* 4、如果有一个元素的判断结果不为-1,就返回true
* 5、否则返回false。
**/
//默认为所有的元素都没有指定的className
var has = false;
this.each( function(){
// 只要有一个元素存在指定的className,那么就修改has的变量值为true
if( (' ' + this.className + ' ').indexOf(' ' + className + ' ') > -1 ){
has = true;
// 中断遍历
return false;
}
});
//返回has
return has;
},
//有则删除,没有则添加
toggleClass: function( className ){
/*
* 实现思路:
* 1、遍历所有的元素
* 2、判断每一个元素有则删除,没有添加
**/
this.each( function(){
// 这里的this是遍历到每一个原生DOM
// 先包装成JQ对象,就可以复用之前写好的方法了
var $this = jQuery( this );
// 有则删除,没有则添加
if( $this.hasClass( className ) ){
$this.removeClass( className );
}else{
$this.addClass( className );
}
})
},
//设置或者获取元素属性节点值
attr: function( attr, val ){
/*
* 实现思路:
* 1、判断attr是不是字符串或者对象,不是直接return this
* 2、如果是字符串,那么继续判断arguments的length
* 3、length为1,则获取第一个元素指定的属性节点值返回
* 4、length>=2,遍历所有元素,分别给他们设置新的属性节点值( setAttribute )
* 5、如果不是字符串(是对象),那么遍历这个对象,得到所有的属性节点值,
* 然后遍历所有的元素,把所有的属性节点分别添加到这些元素中
* 6、return this
**/
// 如果不是字符串也不是对象
if( !jQuery.isString( attr ) && !jQuery.isObject( attr ) ){
return this;
}
// 如果是字符串
if( jQuery.isString( attr ) ){
// length为1,则获取第一个元素指定的属性节点值返回
if( arguments.length === 1 ){
return this.get( 0 ).getAttribute( attr );
}
// 如果length为多个(2及2个以上)
// 则遍历所有的元素,分别设置属性节点值
else{
for( var i = 0, len = this.length; i < len; i++){
this[ i ].setAttribute( attr, val );
}
}
}
// 如果是对象
// 遍历这个对象,和所有的元素, 分别添加遍历到的属性接节点值
else{
for( var key in attr ){
for( var i = 0, len = this.length; i < len; i++ ){
this[i].setAttribute( key, attr[key] )
}
}
}
return this;
},
//设置或者获取元素属性节点值
_attr: function( attr, val ){
/*
* 实现思路:
* 1、判断arguments.length
* 2、如果为1
* 3、继续判断attr是不是字符串,如果是获取第一个元素指定的属性节点值返回
* 4、如果不是继续判断是不是对象,如果是遍历这个对象,得到所有属性节点值,
* 然后遍历所有的元素,把所有的属性节点分别添加到这些元素中
* 5、如果为多个(2及以上),遍历所有元素分别设置属性节点值
* 6、return this
**/
// 把实例使用别称存储一下,为了在别的地方使用
var self = this;
if( arguments.length === 1 ){
// 如果是字符串,获取第一个元素指定的属性节点值返回
if( jQuery.isString( attr ) ){
return this[ 0 ].getAttribute( attr );
}
// 如果是对象,把对象中所有的属性节点添加到所有的元素中
else if( jQuery.isObject( attr) ){
// 使用jQ静态each方法遍历attr对象
jQuery.each( attr, function( key, val ){
// 这里便利到的val不是对象类型, 是基本数据类型
// 我们要使用的就是基本数据类型, 而this是基本数据类型的包装类型(),
// 所以这里不能使用this。
//遍历所有的元素
self.each(function(){
// 给遍历到的每一个元素分别设置外面遍历到的属性节点
this.setAttribute( key, val );
});
});
}
//如果参数个数为多个
}else if( arguments.length >= 2){
// 遍历所有元素分别设置对应的属性节点值
this.each(function(){
this.setAttribute( attr, val )
});
}
// 如果不是字符串也不是对象
if( !jQuery.isString( attr ) && !jQuery.isObject( attr ) ){
return this;
}
return this;
},
//设置或者获取元素的属性值
prop: function( attr, val ){
/*
* 实现思路:
* 1、判断attr是不是字符串或者对象,不是直接return this
* 2、如果是字符串,那么继续判断arguments的length
* 3、length为1,则获取第一个元素指定的属性节点值返回
* 4、length>=2,遍历所有元素,分别给他们设置新的属性节点值( setAttribute )
* 5、如果不是字符串(是对象),那么遍历这个对象,得到所有的属性节点值,
* 然后遍历所有的元素,把所有的属性节点分别添加到这些元素中
* 6、return this
**/
// 如果不是字符串也不是对象
if( !jQuery.isString( attr ) && !jQuery.isObject( attr ) ){
return this;
}
// 如果是字符串
if( jQuery.isString( attr ) ){
// 如果只有一个参数为字符串,那么返回第一个元素指定的属性值
if( arguments.length === 1 ){
return this[0][attr];
}
// 如果有多个参数, 那么给所有元素设置指定的属性值
else{
for( var i = 0, len = this.length; i < len; i++){
this[ i ][ attr ] = val;
}
}
}
// 如果是对象
// 遍历这个对象,和所有的元素, 分别添加遍历到的属性值
else{
for( var key in attr ){
for( var i = 0, len = this.length; i < len; i++ ){
// 给每一个元素设置属性
this[ i ][ key ] = attr[ key ];
}
}
}
return this;
},
//设置或者获取属性值
_prop: function( attr, val ){
/*
* 实现思路:
* 1、判断arguments.length为1
* 2、继续判断attr是不是字符串,如果是获取第一个元素指定的属性
* 3、如果不是继续判断是不是对象,如果是遍历这个对象,得到所有属性,
* 4、如果为多个(2及以上),遍历所有元素分别设置指定属性
* 5、链式编程return this
**/
// 把实例使用别称存储一下,为了在别的地方使用
var self = this;
if( arguments.length === 1 ){
// 如果是字符串,获取第一个元素指定的属性节点值返回
if( jQuery.isString( attr ) ){
return this[ 0 ][ attr ];
}
// 如果是对象,把对象中所有的属性添加到所有的元素中
else if( jQuery.isObject( attr) ){
// 使用jQ静态each方法遍历attr对象
jQuery.each( attr, function( key, val ){
// 这里便利到的val不是对象类型, 是基本数据类型
// 我们要使用的就是基本数据类型, 而this是基本数据类型的包装类型(),
// 所以这里不能使用this。
//遍历所有的元素
self.each(function(){
// 给遍历到的每一个元素分别设置外面遍历到的属性
this[ key ] = val;
});
});
}
//如果参数个数为多个
}else if( arguments.length >= 2){
// 遍历所有元素分别设置对应的属性值
this.each(function(){
this[ attr ] = val;
});
}
return this;
},
//获取或者设置元素的value的属性值
val: function( value ){
/*
* 实现思路:
* 1、如果arguments.length === 0,则直接返回第一个元素的value属性值
* 2、否则,遍历所有的元素,分别设置对应的value属性值
* 3、链式编程返回this
**/
// 没有传参,返回第一个元素的value属性值
if( arguments.length === 0 ){
return this[ 0 ].value;
}
// 否则给所有元素分别设置value值
else {
for( var i = 0, len = this.length; i < len; i++){
this[ i ].value = value;
}
}
//链式编程
return this;
},
//获取或者设置元素的value的属性值
_val: function( value ){
/*
* 实现思路:
* 1、如果arguments.length === 0,则直接返回第一个元素的value属性值
* 2、否则,遍历所有的元素,分别设置对应的value属性值
* 3、链式编程返回this
**/
// 没有传参,返回第一个元素的value属性值
if( arguments.length === 0 ){
return this[ 0 ].value;
}
// 否则给所有元素分别设置value值
else {
this.each( function(){
this.value = value;
});
}
//链式编程
return this;
},
//获取或者设置元素的value的属性值
__val: function( value ){
// 没有传参,借用prop获取第一个元素的value属性值
if( arguments.length === 0 ){
return this.prop( 'value' );
}
// 传参了,借用prop给所有元素设置新的value属性值
else {
return this.prop( 'value', value );
}
},
// 设置或者获取样式
css: function( styleName, style ){
/*
* 实现思路:
* 1、如果arguments.length为1
* 2、那么判断styleName是不是字符串, 是则获取第一个元素指定的样式
* 3、判断styleName是不是对象。是则遍历这个对象得到所有的样式分别设置给所有元素
* 4、如果arguments.length>=2,遍历所有元素分别设置指定的样式
* 5、链式变成返回this
**/
if( arguments.length === 1 ){
if( jQuery.isString( styleName ) ){
return jQuery.getStyle( this[ 0 ], styleName );
}
else if( jQuery.isObject( styleName ) ){
// 遍历styleName得到所有的样式
for( var key in styleName ){
// 遍历得到所有元素
for( var i = 0, len = this.length; i < len; i++ ){
// 给素有的元素设置遍历到所有样式
this[ i ][ 'style' ][ key ] = styleName[ key ];
}
}
}
}
else if( arguments.length >= 2 ){
//给所有元素设置指定样式
for( var i = 0, len = this.length; i <len; i++ ){
this[ i ][ 'style' ][ styleName ] = style;
}
}
return this;
}
} );
//重写所有事件
jQuery.each( ( "blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu" ).split(" "),
function( i, eventName ){
//事件设置
jQuery.fn[ eventName ] = function( fn ){
return this.on( eventName, fn );
}
} );
// 这是真正的构造函数,同时把构造函数放在了原型中
var init = jQuery.fn.init = function( selector ) {
// null、undefined、NaN、0、false、''
if ( !selector ) {
return this;
}
// function
if ( jQuery.isFunction( selector ) ) {
// 打包给ready静态方法处理
jQuery.ready( selector );
}
// string ==> ( html || selector )
else if( jQuery.isString( selector ) ) {
// 为了用户友好体验,先去掉首尾空白字符
selector = jQuery.trim( selector );
// html
if( jQuery.isHTML( selector ) ) {
// 利用一个临时的div来创建DOM,
// 然后把创建好的DOM依次push给实例。
var tempDiv = document.createElement( 'div' );
tempDiv.innerHTML = selector;
[].push.apply( this, tempDiv.childNodes );
}
// selector
else {
try {
[].push.apply( this, document.querySelectorAll( selector ) );
}catch(e) {
// 如果报错了,那么手动补一个length属性,代表没有获取到任何元素
this.length = 0;
}
}
}
// array || likeArray
else if( jQuery.isLikeArray( selector ) ) {
[].push.apply( this, [].slice.call( selector ) );
}
// 其它
else {
this[0] = selector;
this.length = 1;
}
};
// 替换构造函数的原型 为 jQuery工厂的原型
// 为了实现插件,让外界可以透过jQuery.fn访问到init原型
init.prototype = jQuery.fn;
// 暴露工厂和工厂的简称
w.jQuery = w.$ =jQuery;
}( window ));
- 本文作者: MrRetro博客
- 本文链接: http://mrretro.gitee.io/retroblog/retroblog/2018/02/10/jq1/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!