用原生js编写一个简单的选框函数方法
今天,我们将讨论如何使用本机javascript编写一个简单的框选择函数。
需求描述
当按下鼠标左键时,鼠标移动时会出现一个矩形框;松开鼠标左键,根据上面出现的矩形选中框,统计选中框内的DOM元素;嗯(表示踌躇等).上面的功能描述看起来相当简单,但是在实现的时候还是有一些地方需要考虑。例如,如果我们的框选择范围不是document.body,而是div呢?在实际开发过程中,我们应该会遇到第二种情况。
单击查看完整的源代码
如何实现
什么都不说,让我们写代码!由于兼容性更好,这里避免了一些ES6语法。如果是用其他框架写的,代码要做相应的调整。
head style . FileDiv { display : }内联块;宽度: 100 px;高度: 100像素;margin: 24px背景-颜色:蓝色;}/style/head dy Div class=' fileDiv '/Div class=' fileDiv '/Div class=' fileDiv '/Div class=' fileDiv '/Div class=' fileDiv '/Div class=' file Div '/Div class=' file Div '/Div class=' file Div '/Div/body Add鼠标事件监控
由于js本身没有鼠标点击和保持的事件,这里我们不仅需要检测鼠标左键点击和保持,还需要添加一个计时器来检测鼠标是否被保持。
script(function(){//timer id var MouseStOpid;//是否开启选框功能var mouseOn=false//用于存储初始鼠标点击位置var startX=0;var startY=0;//添加鼠标按下监听事件document . body . addevent listener(' mouse down ',函数(e){//防止事件冒泡clearevent table(e);//判断鼠标左键是否被按下如果(e.buttons!==1 || e .哪个!==1)返回;mouseStopId=setTimeout(函数(){ mouseOn=truestartX=e.clientXstartY=e.clientY}, 300);//间隔300毫秒后执行,判断此时按住鼠标左键});//添加鼠标移动事件来监听document . body . addeventlistener(' mouse move ',函数(e){//如果没有打开框,退出if(!mouseOn)返回;//防止事件冒泡clearevent table(e);//处理鼠标移动//codes });//添加鼠标点击释放事件监听document . body . addeventlistener(' mouse up ',函数(e){//防止事件冒泡cleareventtable(e);//处理鼠标点击释放//codes });函数clearEventBubble(e){ if(e . stopperpagation)e . stopperpagation();else e.cancelBubble=trueif(e . preventdefault)e . preventdefault();else e.returnValue=false} })();/script添加框选择可视元素
框选中的视觉元素示意图
我们仅有事件监控是不够的。为了更好的交互效果,我们需要一个随时随鼠标移动的选框元素,让用户可以随时感受到选框范围。
脚本(函数(){ var mouseStopIdvar mouseOn=false var startX=0;var startY=0;文件。尸体。addeventlistener('鼠标向下'),函数{ clearEventBubble(e);if(e . button!==1 || e。哪个!==1)返回;mouseStopId=setTimeout(函数(){ MouseOn=TrueStartX=e . Clientxstarty=e.clientY//创建一个框选元素var selDiv=文档。创建元素(' div ');//给框选元素添加钢性铸铁样式,这里使用绝对定位塞尔维。风格。CSS文本='位置:绝对;宽度:0;高度:0;余量:0划水:0;border:1px虚线# eee背景-颜色: # AAAz指数:1000;opa city 33600.6 display : none ';//添加id selDiv.id=' selectDivdocument。尸体。append child(SelDiV);//根据起始位置,添加定位塞尔维。风格。left=startX ' px塞尔维。风格。top=startY ' px}, 300);});文件。尸体。addeventlistener('鼠标移动',函数(e) { if(!鼠标打开)返回;clearEventBubble(e);//获取当前坐标var _ x=e . clientxvar _ y=e.clientY//根据坐标给选框修改样式var selDiv=文档。getelementbyid(' SelectDiv ');塞尔维。风格。display=' blockselDiv.style.left=Math.min(_x,startX)' px ';selDiv.style.top=Math.min(_y,startY)' px ';塞尔维。风格。宽度=数学。ABS(_ x-StartX)' px ';塞尔维。风格。身高=数学。ABS(_ y-StartY)' px ';//如果需要更直观一点的话,我们还可以在这里进行对框选元素覆盖到的元素进行修改被框选样式的修改。 });文件。尸体。addeventlistener('鼠标向上'),函数{ clearEventBubble(e);});函数clearEventBubble(e){ if(e . stopperpagation)e . stopperpagation();else . cancelubble=true if(e . prevent default)e . prevent default();else . return value=false } }));/script添加鼠标松开事件监听
元素是否被选中示意图
我们没有在鼠标移动的时候去实时统计被框选到的数字正射影像图元素,如果需要实时统计或者实时修改被选择的数字正射影像图元素的样式,以便更准确的让用户感知到被框选的内容的话,可以选择在鼠标移动事件里边去实现以下代码:
脚本(函数(){ var mouseStopIdvar mouseOn=false var startX=0;var startY=0;文件。onmousedown=function(e){ clearEventBubble(e);if(e . button!==1 || e。哪个!==1)返回;mouseStopId=setTimeout(函数(){ MouseOn=TrueStartX=e . Clientxstarty=e . ClientyVar selDiv=document。创建元素(' div ');塞尔维。风格。CSS文本='位置:绝对;宽度:0;高度:0;余量:0划水:0;border:1px虚线# eee背景-颜色: # AAAz指数:1000;opa city 33600.6 display : none ';selDiv.id=' selectDivdocument。尸体。append child(SelDiV);塞尔维。风格。left=startX ' px塞尔维。风格。top=startY ' px}, 300);}文档。onmousemove=函数(e){ if(!鼠标打开)返回;clearEventBubble(e);var _ x=e . clientxvar _ y=e . clientyvar selDiv=document。getelementbyid(' SelectDiv ');塞尔维。风格。display=' blockselDiv.style.left=Math.min(_x,startX)' px ';selDiv.style.top=Math.min(_y,startY)' px ';塞尔维。风格。宽度=数学。ABS(_ x-StartX)' px ';塞尔维。风格。身高=数学。ABS(_ y-StartY)' px ';};//添加鼠标松开事件监听文件。onmouseup=函数(e){ if(!鼠标打开)返回;clearEventBubble(e);var selDiv=文档。getelementbyid(' SelectDiv ');var fileDivs=文档。getelementsbyclassname(' FileDiv ');var selected els=[];//获取参数var l=seldiv . offsetleftvar t=seldiv . offsettopvar w=seldiv . offsetidthvar h=seldiv . offsethethightfor(var I=0;I fileDivs . lengthi){ var sl=fileDivs[I].与文件div[I]的偏移量.offsetLeftvar st=fileDivs[i].偏右文件间距[i].偏移如果(第一个文件[i].offsetLeft l w fileDivs[i].偏移顶部t(h){//该数字正射影像图元素被选中,进行处理选择一个。推送(FileDivs[I]);} } //打印被选中数字正射影像图元素控制台。日志(选择edels//恢复参数塞尔维。风格。显示='无';mouse on=false };函数clearEventBubble(e){ if(e . stopperpagation)e . stopperpagation();else . cancelubble=true if(e . prevent default)e . prevent default();else . return value=false } }));/script这里判断一个元素是否被选中采用的判断条件是:
该数字正射影像图元素的最右边(文件格式[i].offsetLeft fileDiv[i].偏移)是否要比选框元素最左边(selDiv.offsetLeft)的位置要小;该数字正射影像图元素的最下边(文件格式[i].offsetTop文件分区[i].右偏)是否要比选框元素的最上边(selDiv.offsetTop)的位置要大;该数字正射影像图元素的最左边(文件格式[i].offsetLeft)是否要比选框元素的最后边(seldiv。offsetleft seldiv。偏移)的位置数值要小;该数字正射影像图元素的最上边(文件格式[i].offsetTop)是否要比选框元素的最下边(seldiv。偏移顶部seldiv。偏移高度)的位置数值要小;满足了以上四个条件,即可判别为该数字正射影像图元素被选中了。
实际应用
上边的例子,举得有些过于简单了。实际的开发当中,框选的范围往往不可能是整个文档。正文,而是某一个具体的有特定宽度跟高度限制的元素。这个时候,就还需要考虑这个框选容器元素造成的定位偏差,以及容器内元素过多,出现滚动条的情况了。
乍一看,上边的情况需要考虑的因素多了不少,比较容易乱。我这里采用的方法是修改坐标系的方式来实现上边描述的功能。上文我们已经实现了在文档。正文整个页面左上角顶点作为坐标原点来实现框选功能,这时候我们需要修改坐标原点为框选容器的左上角顶点作为坐标原点即可。
换言之,就是修改鼠标放下跟鼠标移动事件时,初始位置由原来的e.clientX跟e.clientY修改为e . clientx-selectcontainer。offsetleft selectcontainer。偷窃跟e . client-select容器。偏移顶部选择容器。滚动顶部。
坐标更改十一图
超文本标记语言标题标题区域/标题样式正文{ margin : 0;padd : 0;} # SelectContainer {位置:相对;宽度: 400像素;/* 演示宽高与位置*/高度: 400 pxtop : 200 pxleft : 200 pxborder : 1px固体# eee飞越:隐藏;溢出-y:自动;} .fileDiv { display:内联块;宽度: 100像素;高度: 100像素;margin: 24px背景色-: # 0082 cc;}/style/head body div id=' selectContainer ' div class=' fileDiv '/div class=' fileDiv '/div class=' fileDiv '/div class=' fileDiv '/div class=' fileDiv '/div class=' fileDiv '/div class=' fileDiv '/div class=' fileDiv '/div class=' fileDiv '/div class=' fileDiv '/div class=' fileDiv ' var ' mouseOn=false var startX=0var startY=0;文件。onmousedown=function(e){ clearEventBubble(e);if(e . button!==1 || e。哪个!==1)返回;mouseStopId=setTimeout(函数(){ mouseOn=true//获取容器元素var selectContainer=文档。getelementbyid(' SelectContainer ');//调整坐标原点为容器左上角startX=e . client x-select container。offsetleft selectcontainer。被偷窃;startY=e . client y-select container。顶部偏移选择容器。滚动顶部;var selDiv=文档。创建元素(' div ');塞尔维。风格。CSS文本='位置:绝对;宽度:0;高度:0;余量:0划水:0;border:1px虚线# eee背景-颜色: # AAAz指数:1000;opa city 33600.6 display : none ';selDiv.id=' selectDiv//添加框选元素到容器内文件。getelementbyid(' selectContainer ').appendChild(SelDiV);塞尔维。风格。left=startX ' px塞尔维。风格。top=startY ' px}, 300);}文档。onmousemove=函数(e){ if(!鼠标打开)返回;clearEventBubble(e);var selectContainer=文档。getelementbyid(' SelectContainer ');var _ x=e . client x-select container。offsetleft selectcontainer。被偷窃;var _ y=e . client y-select container。offsettop selectcontainer。滚动顶部;var _ H=选择容器。客户身高;//鼠标移动超出容器内部,进行相应的处理//向下拖拽if(_ y=_ H选择容器。滚动顶部=_ H){ selectcontainer。滚动顶部=_ y-_ H;} //向上拖拽if(e . clienty=selectcontainer。offsettop selectcontainer。滚动顶部0){ selectcontainer。滚动顶部=数学。ABS(例如clienty-selectcontainer。offset top);} var selDiv=document。getelementbyid(' SelectDiv ');塞尔维。风格。display=' blockselDiv.style.left=Math.min(_x,startX)' px ';selDiv.style.top=Math.min(_y,startY)' px ';塞尔维。风格。宽度=数学。ABS(_ x-StartX)' px ';塞尔维。风格。身高=数学。ABS(_ y-StartY)' px ';};文件。onmouseup=函数(e){ if(!鼠标打开)返回;clearEventBubble(e);var selDiv=文档。getelementbyid(' SelectDiv ');var fileDivs=文档。getelementsbyclassname(' FileDiv ');var selected els=[];var l=seldiv . offsetleftvar t=seldiv . offsettopvar w=seldiv . offsetidthvar h=seldiv . offsethethightfor(var I=0;I fileDivs . lengthi){ var sl=fileDivs[I].与文件div[I]的偏移量.offsetLeftvar st=fileDivs[i].偏右文件间距[i].偏移如果(第一个文件[i].offsetLeft l w fileDivs[i].偏移顶部t(h){ selected els。推送(文件div[I]);} }控制台。日志(选择条目);塞尔维。风格。显示='无';mouse on=false };函数clearEventBubble(e){ if(e . stopperpagation)e . stopperpagation();else . cancelubble=true if(e . prevent default)e . prevent default();else . return value=false } }));/script使用前端框架
在上面的代码中,我们只是在一个html文件中实现了选择框的功能。很多时候我们会用一些前端框架来编写选框的功能(比如vue.js、angular、react、polymer等前端框架)。这时候我们可以利用框架本身的生命周期功能,添加相应的监控事件,然后在mouseup事件中去掉上面的事件监控,减少不必要的资源消耗。而且很多时候,组件化的使用使得选中的元素往往是一个可重用的小组件,也需要根据对应框架的对应方式获取对应的DOM元素来获取其属性。
以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。
版权声明:用原生js编写一个简单的选框函数方法是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。

















