仿照jquery封装一个自己的js库(二)
本篇为完结篇。主要讲述如何造出轮子的高级特性。
一. css方法的高级操作
先看本文第一部分所讲的dQuery css方法
//css方法
dQuery.prototype.css=function(attr,value){
if(arguments.length==2){//当参数个数为2时,使用设置css的方法
var i=0;
for(i=0;i<this.elements.length;i++){
this.elements[i].style[attr]=value;
}
}else{//只有一个参数时获取样式
return getStyle(this.elements[0],attr);
}
};
在这个方法里,只能一次一行,且只能设置一个属性。效率还不够高。现在尝试通过设置类似$d('#div1').css({width:'200px',height:'200px',background:'red'})
这样的方式,向css方法传入一个json对象。允许一次性设置多个css样式。或者允许链式操作。
1. 传入json数据
传入json实质上是一个参数。所以只有一个参数时,不仅仅是获取,还可能是传入了json数据。需要对传入一个参数时,进行分类讨论
json与for-in循环
var json={width:'200px',height:'200px',background:'red'};
var i=0;
for(i in json){
alert(i+':'+json[i]);
}
这段程序可以依次弹出i(json属性):json数据的窗口。
同理,在dQuery的css方法中也可以这么做。
//css方法
dQuery.prototype.css=function(attr,value){
if(arguments.length==2){//当参数个数为2时,使用设置css的方法
var i=0;
for(i=0;i<this.elements.length;i++){
this.elements[i].style[attr]=value;
}
}else if(arguments.length==1){//只有一个参数时获取样式,或传入json
if(typeof attr=='string'){//attr为纯文字时,设置样式
return getStyle(this.elements[0],attr);
}else{//传入json,批量设置css样式
for(i=0;i<this.elements.length;i++){
var j='';
for(j in attr){
this.elements[i].style[j]=attr[j];
}
}
}
}
};
测试成功。
(2)链式操作
函数链式操作原理:返回函数本身。
function show(str){
alert(str);
return show;//关键
}
show('abc')('bcd');
这段代码将弹出两个框,abc,和bcd。只要你想,可以无限地写下去。
链式操作是jquery的主要特色。执行完一个方法之后,又返回到该对象。所以只需要在css函数的最后,补上return this
。就完成了。根据这个思想,可以给每个方法加上return this
,那么你的js库就可以实现完全的链式操作。
二. attr设置进一步——removeClass和addClass的实现
之前说到attr方法本质上和css方法一样的。所以attr也可以用类似的方法设置——
//attr方法和css方法类似。
dQuery.prototype.attr=function(attr,value){
if(arguments.length==2){//设置属性
var i=0;
for(i=0;i<this.elements.length;i++){
this.elements[i][attr]=value;
}
}else if(arguments.length==1){//获取属性
if(typeof attr=='string'){
return this.elements[0][attr];
}else{
for(i=0;i<this.elements.length;i++){
var j='';
for(j in attr){
this.elements[i][j]=attr[j];
}
}
}
}
return this;
}
这段attr方法已经能够实现链式操作和接收json数据。现在需要利用attr方法实现添加class和移除class的功能。
jquery中,addClass
和removeClass
的基本功能是:给addClass方法传入一个字符串,目标元素的class尾部追加这段字符串,给removeClass传入字符串,就把该字符串从目标元素的字符串中删除掉。
用文字描述之后,实现就简单不少了。
//addClass和removeClass的实现
dQuery.prototype.addClass=function(str){
var i=0;
for(i=0;i<this.elements.length;i++){
if(this.elements[i].className==''){
this.elements[i].className+=str;
}else{
this.elements[i].className+=' '+str;
}
}
return this;
}
dQuery.prototype.removeClass=function(str){
var i=0;
for(i=0;i<this.elements.length;i++){
var _className=this.elements[i].className
if(!str){//如果不传参,所有class都被清空。
this.elements[i].className='';
}else if(_className!=''){
var arr=_className.split(' ');
var j=0;
for(j=0;j<arr.length;j++){
if(arr[j]==str){
arr.splice(j,1);//从数组第j个起删除1个(移除arr[j])
this.elements[i].className=arr.join(' ');//把数组重新转化为字符串,并用空格分开。最后赋值给当下对象的className。
}
}
}
}
return this;
}
注意,此段代码有局限性,前提是操作者html操作正确时才能生效,类似$d('.div1').addClass('div1').removeClass('div1')
虽然也能尝试理解操作者意思并容错。但是<div class="div1 div1 div2">
这样错误的class标记使用removeClass会发生错误。
3.阻止冒泡及默认事件
右键菜单(contextmenu)为例——jquery阻止默认事件,冒泡的机制是
$(function(){
$(document).bind('contextmenu',function(){//对document绑定contextmenu事件,并执行函数。
return false;
})
})
return false在原生js中只处理阻止默认事件,但jquery可以两样都阻止。
首先,dQuery没有bind方法。加上去就行。所谓绑定事件的框架很简单,实际上和dQuery其它事件的框架是一样的。
//绑定事件的方法:
dQuery.prototype.bind=function(sEv,fn){
var i=0;
for(i=0;i<this.elements.length;i++){
myAddEvent(this.elements[i],sEv,fn);
}
}
只要你在页面上右键点击,就会触发函数。然而还不完善。
$d(function (){
$d(document).bind('contextmenu', function (){
return false;
});
});
这里添加了return false,但右击,无法阻止任何事件。
究其深层原因,可以发现,回调函数是通过myAddEvent()这个函数实现的。
这就搞笑了。在myAddEvent()中,无论return什么都没有意义。所以要实现,完整的myAddEvent()代码是:
//可重复调用的加载函数
function myAddEvent(obj,sEv,fn){
if(obj.attachEvent){
obj.attachEvent('on'+sEv,function(){
if(false==fn.call(obj)){//当调用return false的时候
event.cancelBubble=true;//阻止冒泡
return false;
}
});
}else{
obj.addEventListener(sEv,function(ev){
if(false==fn.call(obj)){
ev.cancelBubble=true;//阻止冒泡
ev.preventDefault();//火狐、chrome下用于阻止默认事件的语句
}
},false);
}
}
这样,在三大浏览器下,已经实现了事件的绑定调用return false无默认事件。并且阻止冒泡。
三. 插件接口
插件需要一个机制。可以通过它自定义方法名称,方法内容.
假设页面有三个class为div1的div,扩展s一个size
方法,获取某类页面元素的个数:
dQuery.prototype.extend=function (name, fn){
dQuery.prototype[name]=fn;
};
$d().extend('size',function(){
alert(this.element.length);
})
//调用插件里的方法和内部方法无差异
$d('.div1').size();
弹出结果就为3.
从这个角度说,插件机制编写方法似乎更加便捷了。
1. animate运动框架
jquery的运动形式为:xxx.animate('',目标)。目标是一个json数据。
在运动学教程中有一个比较完美的运动框架startMove。startMove的json传入的值为最终运动状态。注意:和jquery不同,数值不带单位,透明度的量度为100而不是1.
$d().extend('animate',function(json){
var i=0;
for(i=0;i<this.elements.length;i++){
console.log(this.elements);
startMove(this.elements[i],json,function(){alert('hehe')})
}
function getStyle(obj, attr)
{
if(obj.currentStyle)
{
return obj.currentStyle[attr];
}
else
{
return getComputedStyle(obj, false)[attr];
}
}
function startMove(obj,json,fn){
clearInterval(obj.timer);
obj.timer=setInterval(function(){
var bStop= true;//标志着所有运动都结束了
//遍历每个json属性
for(var attr in json){
//取当前的属性对象
var iCur=0;
if(attr=='opacity'){
iCur=parseInt(parseFloat(getStyle(obj,attr))*100);
}else{
iCur=parseInt(getStyle(obj,attr));
}
//定义速度值
var iSpeed=(json[attr]-iCur)/8;
iSpeed=iSpeed>0?Math.ceil(iSpeed):Math.floor(iSpeed);
//检测停止:如果我发现某个值不等于目标点bStop就不能为true。
if(iCur!==json[attr]){
bStop=false;
}
//计算
if(attr=='opacity'){
obj.style[attr]=(iCur+iSpeed)/100;
obj.style.filter='alpha(opacity:'+(iSpeed+iCur)+')';
}else{
obj.style[attr]=iCur+iSpeed+'px';
}
}
//检测是否停止,是的话关掉定时器
if(bStop==true){
if(iCur==json[attr]){
clearInterval(obj.timer);
if(fn){fn();};
}
}
},20)
}
});
那么这个插件就写完了。
【案例分析】上下滑动的幻灯片(选项卡)
基本思路和之前的选项卡案例差不多。但是切换过程是上下滑动的。因此所有图片不能隐藏,应该是一张借一张纵向连接。运动是由一个大容器带着一起运动。垂直运动距离由index()值决定同时,有了addClass和removeClass方法,可以设置一个class='active'的样式。
<div id="tab">
<ul class="list_group">
<li><a href="javascript:;">1</a></li>
<li><a href="javascript:;">2</a></li>
<li><a href="javascript:;">3</a></li>
<li><a href="javascript:;">4</a></li>
</ul>
<div class="box">
<div class="box2">
<div class="content">
<img alt="1" src="images/1.jpg">
</div>
<div class="content">
<img alt="2" src="images/2.jpg">
</div>
<div class="content">
<img alt="3" src="images/3.jpg">
</div>
<div class="content">
<img alt="4" src="images/4.jpg">
</div>
</div>
</div>
</div>
css
*{
margin:0;
padding: 0;
}
ul{
list-style: none;
}
a{text-decoration: none;}
#tab{
width: 400px;
margin:100px auto;
position: relative;
}
.list_group li{
float: left;
}
.list_group li a{
display: block;
width: 40px;
line-height: 30px;
text-align: center;
}
.box{
clear: both;
width: 402px;height: 302px;
overflow: hidden;
position: relative;
}
.box2{
position: absolute;
top:0;
}
.content{
width: 402px;
height: 302px;
}
.content img{
border: 1px solid black;
width: 400px;height: 300px;
}
.active{
background: #ccc;
}
javascript
$d(function(){
$d('li').click(function(){
var index=$d(this).index();
var moveLength=-302*index;
$d('.box2').animate({top:moveLength});
$d('li').removeClass('active');
$d(this).addClass('active');
})
})
效果:
效果还是差强人意,可以做自动播放:
$d().extend('size',function(){
return this.elements.length;
})//定义一个统计元素个数的dQuery插件
$d(function(){
var iNow=0;
var timer=null;
//定义一个函数指令,可以移动.box2 -302*iNow个单位。
function tab(){
var moveLength=-302*iNow;
$d('.box2').animate({top:moveLength});
$d('li').removeClass('active');
$d('li').eq(iNow).addClass('active');
}
//自动播放定义定时器内的函数
function timerInner(){
iNow++;
if(iNow==$d('li').size()){
iNow=0;
}
tab();
}
timer=setInterval(timerInner,1000);//调用定时器
//点击事件函数
$d('li').click(function(){
iNow=$d(this).index();
tab();
});
//鼠标移入选项卡范围,关掉定时器timer,移出时再打开
$d('#tab').hover(function(){
clearInterval(timer);
},function(){
timer=setInterval(timerInner,1000)
})
})
效果更加人性化了。
小结:与前一个版本的dQuery库相比,写法更进一步。
2.拖拽插件
加入页面上有一个绝对定位的div#div1
样式如下:
#div1{
width: 100px;height: 100px;
background: red;
position: absolute;
}
引入方法是:
$d().extend('drag', function (){
var i=0;
for(i=0;i<this.elements.length;i++){
drag(this.elements[i]);
}
function drag(oDiv){//拖拽函数
oDiv.onmousedown=function (ev){
var oEvent=ev||event;
var disX=oEvent.clientX-oDiv.offsetLeft;
var disY=oEvent.clientY-oDiv.offsetTop;
document.onmousemove=function (ev){
var oEvent=ev||event;
oDiv.style.left=oEvent.clientX-disX+'px';
oDiv.style.top=oEvent.clientY-disY+'px';
};
document.onmouseup=function (){
document.onmousemove=null;
document.onmouseup=null;
};
};
}
});
调用方法:
$d('#div1').drag();
一个简单到令人发指的效果就做好了。
附录
1.dQuery基本代码(dQuery.js)
//可重复调用的加载函数
function myAddEvent(obj,sEv,fn){
if(obj.attachEvent){
obj.attachEvent('on'+sEv,function(){
if(false==fn.call(obj)){//当调用return false的时候
event.cancelBubble=true;
return false;
}
});
}else{
obj.addEventListener(sEv,function(ev){
if(false==fn.call(obj)){
ev.cancelBubble=true;
ev.preventDefault();//火狐、chrome下用于阻止默认事件的语句
}
},false);
}
}
//class选择器调用函数
function getByClass(oParent,sClass){
var aEle=oParent.getElementsByTagName('*');//选择父元素的所有元素
var aResult=[];
var re=new RegExp('\\b'+sClass+'\\b','i');//正则边界
var i=0;
for(i=0;i<aEle.length;i++){
if(re.test(aEle[i].className)){
aResult.push(aEle[i]);
}
}
return aResult;
}
//获取计算后的样式
function getStyle(obj,attr){
//元素,样式
if(obj.currentStyle){//兼容ie9及以下
return obj.currentStyle[attr];
}else{
return getComputedStyle(obj,false)[attr];
}
}
//定义dQuery对象
function dQuery(vArg){//参数是变体变量
this.elements=[];//选择器选择的元素扔到这个数组中
switch(typeof vArg){
//如果参数是函数
case 'function':
myAddEvent(window,'load',vArg);
break;
//如果参数是字符串
case 'string':
switch(vArg.charAt(0)){
case '#'://id选择器参数应该为#号之后的字符段
var obj=document.getElementById(vArg.substring(1));
this.elements.push(obj);
break;
case '.'://class
this.elements=getByClass(document,vArg.substring(1));
break;
default://标签
this.elements=document.getElementsByTagName(vArg);
}
break;
//如果参数是对象。
case 'object':
this.elements.push(vArg);
}
}
//定义简写
function $d(vArg){
return new dQuery(vArg);
}
//对选择器函数绑定click事件
dQuery.prototype.click=function(fn){
var i=0;
//对于返回器数组的内容
for(i=0;i<this.elements.length;i++){
myAddEvent(this.elements[i],'click',fn);
}
return this;
}
//对选择器函数绑定show/hide事件
dQuery.prototype.show=function(){
var i=0;
//对于返回器数组的内容
for(i=0;i<this.elements.length;i++){
this.elements[i].style.display='block';
}
return this;
}
dQuery.prototype.hide=function(){
var i=0;
//对于返回器数组的内容
for(i=0;i<this.elements.length;i++){
this.elements[i].style.display='none';
}
return this;
};
//hover方法
dQuery.prototype.hover=function(fnover,fnout){
var i=0;
//对于返回器数组的内容
for(i=0;i<this.elements.length;i++){
//给这个对象一次性绑定两个事件
myAddEvent(this.elements[i],'mouseover',fnover);
myAddEvent(this.elements[i],'mouseout',fnout);
}
return this;
};
//css方法
dQuery.prototype.css=function(attr,value){
if(arguments.length==2){//当参数个数为2时,使用设置css的方法
var i=0;
for(i=0;i<this.elements.length;i++){
this.elements[i].style[attr]=value;
}
}else if(arguments.length==1){//只有一个参数时获取样式,或传入json
if(typeof attr=='string'){//attr为纯文字时,设置样式
return getStyle(this.elements[0],attr);
}else{//传入json,批量设置css样式
for(i=0;i<this.elements.length;i++){
var j='';
for(j in attr){
this.elements[i].style[j]=attr[j];
}
}
}
}
return this;
};
//toggle方法:
dQuery.prototype.toggle=function(){
var _arguments=arguments;//把toggle的arguments存起来,以便在其它函数中可以调用。
//私有计数器,计数器会被一组对象所享用。
function addToggle(obj){
var count=0;
myAddEvent(obj,'click',function(){
_arguments[count++%_arguments.length].call(obj);
})
}
var i=0;
for(i=0;i<this.elements.length;i++){
addToggle(this.elements[i]);
}
return this;
}
//attr方法和css方法类似。
dQuery.prototype.attr=function(attr,value){
if(arguments.length==2){//设置属性
var i=0;
for(i=0;i<this.elements.length;i++){
this.elements[i][attr]=value;
}
}else if(arguments.length==1){//获取属性
if(typeof attr=='string'){
return this.elements[0][attr];
}else{
for(i=0;i<this.elements.length;i++){
var j='';
for(j in attr){
this.elements[i][j]=attr[j];
}
}
}
}
return this;
}
//addClass和removeClass的实现
dQuery.prototype.addClass=function(str){
var i=0;
for(i=0;i<this.elements.length;i++){
if(this.elements[i].className==''){
this.elements[i].className+=str;
}else{
this.elements[i].className+=' '+str;
}
}
return this;
}
dQuery.prototype.removeClass=function(str){
var i=0;
for(i=0;i<this.elements.length;i++){
var _className=this.elements[i].className
if(!str){//如果不传参,所有class都被清空。
this.elements[i].className='';
}else if(_className!=''){
var arr=_className.split(' ');
var j=0;
for(j=0;j<arr.length;j++){
if(arr[j]==str){
arr.splice(j,1);//从数组第j个起删除1个(移除arr[j])
this.elements[i].className=arr.join(' ');//把数组重新转化为字符串,并用空格分开。最后赋值给当下对象的className。
}
}
}
}
return this;
}
//eq选择器
dQuery.prototype.eq=function(n){
return new dQuery(this.elements[n]);
}
//find选择器
//定义一个小函数,两个数组(元素集合),把两个类数组(html元素集合)合并在一块。
function appendArr(arr1, arr2){
var i=0;
for(i=0;i<arr2.length;i++){
arr1.push(arr2[i]);
}
}
dQuery.prototype.find=function(str){
var i=0;
var aResult=[];//存放临时数据
for(i=0;i<this.elements.length;i++){
switch(str.charAt(0)){
case '.'://class类
var aEle=getByClass(this.elements[i],str.substring(1));
aResult.concat(aEle);//桥接到aResult内。但是
break;
default://其它标签名(TagName)
var aEle=this.elements[i].getElementsByTagName(str);
appendArr(aResult,aEle);
}
}
var newdQuery=new dQuery();
newdQuery.elements=aResult;
return newdQuery;//保持可链。
}
//获取索引值函数
function getIndex(obj){
var aBrother=obj.parentNode.children;
var i=0;
for(i=0;i<aBrother.length;i++){
if(aBrother[i]==obj){
return i;
}
}
}
dQuery.prototype.index=function(){
return getIndex(this.elements[0]);
}
//绑定事件的方法:
dQuery.prototype.bind=function(sEv,fn){
var i=0;
for(i=0;i<this.elements.length;i++){
myAddEvent(this.elements[i],sEv,fn);
}
}
//插件机制
dQuery.prototype.extend=function (name, fn){
dQuery.prototype[name]=fn;
};
2.动画插件
注:动画插件可以设计类似jquery中fadeIn/Out,slideUp/Down 等等常见的动画效果。
$d().extend('animate',function(json){
var i=0;
for(i=0;i<this.elements.length;i++){
startMove(this.elements[i],json)
}
function getStyle(obj, attr)
{
if(obj.currentStyle)
{
return obj.currentStyle[attr];
}
else
{
return getComputedStyle(obj, false)[attr];
}
}
function startMove(obj,json,fn){
clearInterval(obj.timer);
obj.timer=setInterval(function(){
var bStop= true;//标志着所有运动都结束了
//遍历每个json属性
for(var attr in json){
//取当前的属性对象
var iCur=0;
if(attr=='opacity'){
iCur=parseInt(parseFloat(getStyle(obj,attr))*100);
}else{
iCur=parseInt(getStyle(obj,attr));
}
//定义速度值
var iSpeed=(json[attr]-iCur)/8;
iSpeed=iSpeed>0?Math.ceil(iSpeed):Math.floor(iSpeed);
//检测停止:如果我发现某个值不等于目标点bStop就不能为true。
if(iCur!==json[attr]){
bStop=false;
}
//计算
if(attr=='opacity'){
obj.style[attr]=(iCur+iSpeed)/100;
obj.style.filter='alpha(opacity:'+(iSpeed+iCur)+')';
}else{
obj.style[attr]=iCur+iSpeed+'px';
}
}
//检测是否停止,是的话关掉定时器
if(bStop==true){
if(iCur==json[attr]){
clearInterval(obj.timer);
if(fn){fn();};
}
}
},20)
}
});
3.拖拽插件
$d().extend('drag', function (){
var i=0;
for(i=0;i<this.elements.length;i++){
drag(this.elements[i]);
}
function drag(oDiv){//拖拽函数
oDiv.onmousedown=function (ev){
var oEvent=ev||event;
var disX=oEvent.clientX-oDiv.offsetLeft;
var disY=oEvent.clientY-oDiv.offsetTop;
document.onmousemove=function (ev){
var oEvent=ev||event;
oDiv.style.left=oEvent.clientX-disX+'px';
oDiv.style.top=oEvent.clientY-disY+'px';
};
document.onmouseup=function (){
document.onmousemove=null;
document.onmouseup=null;
};
};
}
});
仿照jquery封装一个自己的js库(二)的更多相关文章
- 仿照jquery封装一个自己的js库(一)
所谓造轮子的好处就是复习知识点,加深对原版jquery的理解. 本文系笔者学习jquery的笔记,记述一个名为"dQuery"的初级版和缩水版jquery库的实现.主要涉及知识点包 ...
- 仿照jquery封装一个自己的js库
所谓造轮子的好处就是复习知识点,加深对原版jquery的理解.本文系笔者学习jquery的笔记,记述一个名为"dQuery"的初级版和缩水版jquery库的实现.主要涉及知识点包括 ...
- Zepto——简化版jQuery,移动端首选js库
转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10826054.html 一:Zepto是什么 Zepto最初是为移动端开发的js库,是jQuery的轻量级替 ...
- C 封装一个简单二叉树基库
引文 今天分享一个喜欢佩服的伟人,应该算人类文明极大突破者.收藏过一张纸币类型如下 那我们继续科普一段关于他的简介 '高斯有些孤傲,但令人惊奇的是,他春风得意地度过了中产阶级的一生,而 没有遭受到冷 ...
- jQuery避免$符和其他JS库冲突的方法对比
1.如果jquery库在第三方库之后引用.这个时候jquery库会占用$. 解决办法:剔除$符号的使用权. <script type="text/javascript" sr ...
- 基于jQuery封装一个瀑布流插件
/*封装一个瀑布流插件*/ (function($){ $.fn.WaterFall = function(){ /*这是你初始化 调用这个方法的时候的 那个jquery选着到的dom对象 this* ...
- 仿照jQuery写一个关于选择器的框架(带了注释,请多多指教~)
var select = (function () { //这是一个异常与捕获的代码,它表示的意思是:如果push方法出现了错误那么就需要重写push方法 try { //这边是自己模拟一个场景,来使 ...
- Jquery,YUI这个著名js库名称作用的理解
看廖雪峰大神的教程,其中讲到变量作用域问题.在命名空间中,写到:因为全局变量绑到了window上,不同的js文件访问相同全局变量或者定义了相同名字的顶层函数,都会造成命名冲突,并且很难被发现. 减少冲 ...
- 【写一个自己的js库】 1.搭个架子先
最近在看<javascript dom 高级程序设计>,想着跟着里面的代码敲一遍吧,也算是做一下学习笔记吧,所以这不是重新发明轮子,只是个学习的过程. 1.先确定自己的命名空间,并且加入几 ...
随机推荐
- android部分开发摘要
Async 异步 不会阻塞当前线程sync 同步 数据库是应用软件|结构化数据存储 JDBC SQL ellipsis 省略 content provider URI thread--lo ...
- JS实时定位
<!DOCTYPE html><html lang="en" xmlns="http://www.w3.org/1999/xhtml"> ...
- [转] ServletContext 与application的异同
相同:其实servletContext和application 是一样的,就相当于一个类创建了两个不同名称的变量.在 servlet中ServletContext就是application对象.大家只 ...
- 数组的方法 Array.map();Array.every()和Array.some();数组的indexof();检测是否是数组isArray(obj);
数组的方法 Array.map(); 栗子: var a=[1,2,,3]; var b=a.map( function(value){return value*value} ); alert(b); ...
- Oracle数据库下sde用户系统表开放权限sql语句
--sde用户登陆执行以下语句 grant insert, update, delete on sde.table_registry to PUBLIC; grant insert, update, ...
- mysql-窗口多表连接视图view
create VIEW view_comment as (' as type FROM wei_comment_comment w) UNION ALL (' as type from review_ ...
- C# 后台获取WebApi 方法
以下是方法 返货json数据 代码都有注释 /// <summary> /// 请求webpapi /// </summary> // ...
- RunLoop的深入了解
RunLoop 是 iOS 和 OS X 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理.之后会介绍一下在 iOS 中,苹果是如何 ...
- 【poj1984】 Navigation Nightmare
http://poj.org/problem?id=1984 (题目链接) 题意 给出一棵树,这棵树是以平面直角坐标系为基准建立的,也就是每个节点最多只有上下左右4条边.现在动态建树,同时询问两点间的 ...
- Hash_bzoj1862: [Zjoi2006]GameZ游戏排名系统
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #i ...