亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

使用原生js編寫一個簡單的框選功能方法

2024-05-06 16:50:49
字體:
來源:轉載
供稿:網友

今天我們來聊一下怎么使用原生javascript編寫一個簡單的框選功能。

需求描述

  • 鼠標左鍵按下不放,移動鼠標出現矩形選框;
  • 鼠標左鍵松開,根據上邊出現的矩形選框統計選框范圍內的DOM元素;

嗯...上邊的功能描述看著是挺簡單的,但實現起來也還是會有些地方需要斟酌思考的。比如,如果我們的框選范圍不是document.body,而是某一個div里邊進行框選呢?而現實開發過程中,我們會遇上的應該就是第二種情況。

點擊查看完整的源碼 

怎么實現

二話不說,咱們動手寫代碼吧!因為更好的兼容性,這里就避免了一些ES6的語法,如果是用的其他框架來寫的話,代碼上相應的也要做一些調整。

<head><style>.fileDiv { display: inline-block; width: 100px; height: 100px; margin: 24px; background-color: blue;}</style></head><body> <div class="fileDiv"></div> <div class="fileDiv"></div> <div class="fileDiv"></div> <div class="fileDiv"></div> <div class="fileDiv"></div> <div class="fileDiv"></div> <div class="fileDiv"></div> <div class="fileDiv"></div></body>

添加鼠標事件監聽

由于js自身并沒有帶有鼠標點擊按住不放這樣子的事件,這里我們不僅需要檢測鼠標左鍵點擊按下,還要加一個定時器來檢測鼠標是否按住不放了。

<script> (function () {  // 定時器id  var mouseStopId;  // 是否開啟框選功能  var mouseOn = false;  // 用來存放鼠標點擊初始位置  var startX = 0;  var startY = 0;  // 添加鼠標按下監聽事件  document.body.addEventListener('mousedown', function (e) {   // 阻止事件冒泡   clearEventBubble(e);   // 判斷是否為鼠標左鍵被按下   if (e.buttons !== 1 || e.which !== 1) return;   mouseStopId = setTimeout(function () {    mouseOn = true;    startX = e.clientX;    startY = e.clientY;   }, 300); // 間隔300毫秒后執行,判定這時候鼠標左鍵被按住不放  });  // 添加鼠標移動事件監聽  document.body.addEventListener('mousemove', function (e) {   // 如果并非框選開啟,退出   if (!mouseOn) return;   // 阻止事件冒泡   clearEventBubble(e);   // 處理鼠標移動   // codes  });  // 添加鼠標點擊松開事件監聽  document.body.addEventListener('mouseup', function (e) {   // 阻止事件冒泡   clearEventBubble(e);   // 處理鼠標點擊松開   // codes  });  function clearEventBubble (e) {   if (e.stopPropagation) e.stopPropagation();   else e.cancelBubble = true;   if (e.preventDefault) e.preventDefault();   else e.returnValue = false;  } })();</script>

添加框選可視化元素

js,框選

框選可視化元素示意圖

我們有了事件監聽還不夠,為了更好的交互效果,我們需要一個隨時跟隨著鼠標移動的框選框元素,用于讓用戶隨時感知框選范圍。

<script> (function () {  var mouseStopId;  var mouseOn = false;  var startX = 0;  var startY = 0;  document.body.addEventListener('mousedown', function (e) {   clearEventBubble(e);   if (e.buttons !== 1 || e.which !== 1) return;   mouseStopId = setTimeout(function () {    mouseOn = true;    startX = e.clientX;    startY = e.clientY;    // 創建一個框選元素    var selDiv = document.createElement('div');    // 給框選元素添加css樣式,這里使用絕對定位    selDiv.style.cssText = 'position:absolute;width:0;height:0;margin:0;padding:0;border:1px dashed #eee;background-color:#aaa;z-index:1000;opacity:0.6;display:none;';    // 添加id    selDiv.id = 'selectDiv';    document.body.appendChild(selDiv);    // 根據起始位置,添加定位    selDiv.style.left = startX + 'px';    selDiv.style.top = startY + 'px';   }, 300);  });  document.body.addEventListener('mousemove', function (e) {   if (!mouseOn) return;   clearEventBubble(e);   // 獲取當前坐標   var _x = e.clientX;   var _y = e.clientY;   // 根據坐標給選框修改樣式   var selDiv = document.getElementById('selectDiv');   selDiv.style.display = 'block';   selDiv.style.left = Math.min(_x, startX) + 'px';   selDiv.style.top = Math.min(_y, startY) + 'px';   selDiv.style.width = Math.abs(_x - startX) + 'px';   selDiv.style.height = Math.abs(_y - startY) + 'px';   // 如果需要更直觀一點的話,我們還可以在這里進行對框選元素覆蓋到的元素進行修改被框選樣式的修改。  });  document.body.addEventListener('mouseup', function (e) {   clearEventBubble(e);  });  function clearEventBubble (e) {   if (e.stopPropagation) e.stopPropagation();   else e.cancelBubble = true;   if (e.preventDefault) e.preventDefault();   else e.returnValue = false;  } })();</script>

添加鼠標松開事件監聽

js,框選

元素是否被選中示意圖

我們沒有在鼠標移動的時候去實時統計被框選到的DOM元素,如果需要實時統計或者實時修改被選擇的DOM元素的樣式,以便更準確的讓用戶感知到被框選的內容的話,可以選擇在mousemove事件里邊去實現以下代碼:

<script> (function () {  var mouseStopId;  var mouseOn = false;  var startX = 0;  var startY = 0;  document.onmousedown = function (e) {   clearEventBubble(e);   if (e.buttons !== 1 || e.which !== 1) return;   mouseStopId = setTimeout(function () {    mouseOn = true;    startX = e.clientX;    startY = e.clientY;    var selDiv = document.createElement('div');    selDiv.style.cssText = 'position:absolute;width:0;height:0;margin:0;padding:0;border:1px dashed #eee;background-color:#aaa;z-index:1000;opacity:0.6;display:none;';    selDiv.id = 'selectDiv';    document.body.appendChild(selDiv);    selDiv.style.left = startX + 'px';    selDiv.style.top = startY + 'px';   }, 300);  }  document.onmousemove = function (e) {   if (!mouseOn) return;   clearEventBubble(e);   var _x = e.clientX;   var _y = e.clientY;   var selDiv = document.getElementById('selectDiv');   selDiv.style.display = 'block';   selDiv.style.left = Math.min(_x, startX) + 'px';   selDiv.style.top = Math.min(_y, startY) + 'px';   selDiv.style.width = Math.abs(_x - startX) + 'px';   selDiv.style.height = Math.abs(_y - startY) + 'px';  };  // 添加鼠標松開事件監聽  document.onmouseup = function (e) {   if (!mouseOn) return;   clearEventBubble(e);   var selDiv = document.getElementById('selectDiv');   var fileDivs = document.getElementsByClassName('fileDiv');   var selectedEls = [];   // 獲取參數   var l = selDiv.offsetLeft;   var t = selDiv.offsetTop;   var w = selDiv.offsetWidth;   var h = selDiv.offsetHeight;   for (var i = 0; i < fileDivs.length; i++) {    var sl = fileDivs[i].offsetWidth + fileDivs[i].offsetLeft;    var st = fileDivs[i].offsetHeight + fileDivs[i].offsetTop;    if (sl > l && st > t && fileDivs[i].offsetLeft < l + w && fileDivs[i].offsetTop < t + h) {     // 該DOM元素被選中,進行處理     selectedEls.push(fileDivs[i]);    }   }   // 打印被選中DOM元素   console.log(selectedEls);   // 恢復參數   selDiv.style.display = 'none';   mouseOn = false;  };  function clearEventBubble (e) {   if (e.stopPropagation) e.stopPropagation();   else e.cancelBubble = true;   if (e.preventDefault) e.preventDefault();   else e.returnValue = false;  } })();</script>

這里判斷一個元素是否被選中采用的判斷條件是:

  • 該DOM元素的最右邊(fileDiv[i].offsetLeft + fileDiv[i].offsetWidth)是否要比選框元素最左邊(selDiv.offsetLeft)的位置要?。?/li>
  • 該DOM元素的最下邊(fileDiv[i].offsetTop + fileDiv[i].offsetHeight)是否要比選框元素的最上邊(selDiv.offsetTop)的位置要大;
  • 該DOM元素的最左邊(fileDiv[i].offsetLeft)是否要比選框元素的最后邊(selDiv.offsetLeft + selDiv.offsetWidth)的位置數值要小;
  • 該DOM元素的最上邊(fileDiv[i].offsetTop)是否要比選框元素的最下邊(selDiv.offsetTop + selDiv.offsetHeight)的位置數值要?。?/li>

滿足了以上四個條件,即可判別為該DOM元素被選中了。

實際應用

上邊的例子,舉得有些過于簡單了。實際的開發當中,框選的范圍往往不可能是整個document.body,而是某一個具體的有特定寬度跟高度限制的元素。這個時候,就還需要考慮這個框選容器元素造成的定位偏差,以及容器內元素過多,出現滾動條的情況了。

乍一看,上邊的情況需要考慮的因素多了不少,比較容易亂。我這里采用的方法是修改坐標系的方式來實現上邊描述的功能。上文我們已經實現了在document.body整個頁面左上角頂點作為坐標原點來實現框選功能,這時候我們需要修改坐標原點為框選容器的左上角頂點作為坐標原點即可。

換言之,就是修改mousedown跟mousemove事件時,初始位置由原來的e.clientX跟e.clientY修改為e.clientX - selectContaienr.offsetLeft + selectContainer.scrollLeft跟e.clientY - selectContainer.offsetTop + selectContainer.scrollTop。

js,框選

坐標更改shi'yi'tu

<html> <head>  <title>region</title>  <style>   body {    margin: 0;    padding: 0;   }   #selectContainer {    position: relative;    width: 400px; /* 演示寬高與位置 */    height: 400px;    top: 200px;    left: 200px;    border: 1px solid #eee;    overflow: hidden;    overflow-y: auto;   }   .fileDiv {    display: inline-block;    width: 100px;    height: 100px;    margin: 24px;    background-color: #0082CC;   }  </style> </head> <body>  <div id="selectContainer">   <div class="fileDiv"></div>   <div class="fileDiv"></div>   <div class="fileDiv"></div>   <div class="fileDiv"></div>   <div class="fileDiv"></div>   <div class="fileDiv"></div>   <div class="fileDiv"></div>   <div class="fileDiv"></div>   <div class="fileDiv"></div>   <div class="fileDiv"></div>   <div class="fileDiv"></div>   <div class="fileDiv"></div>   <div class="fileDiv"></div>   <div class="fileDiv"></div>  </div> </body></html>
<script> (function () {  var mouseStopId;  var mouseOn = false;  var startX = 0;  var startY = 0;  document.onmousedown = function (e) {   clearEventBubble(e);   if (e.buttons !== 1 || e.which !== 1) return;   mouseStopId = setTimeout(function () {    mouseOn = true;    // 獲取容器元素    var selectContainer = document.getElementById('selectContainer');    // 調整坐標原點為容器左上角    startX = e.clientX - selectContainer.offsetLeft + selectContainer.scrollLeft;    startY = e.clientY - selectContainer.offsetTop + selectContainer.scrollTop;    var selDiv = document.createElement('div');    selDiv.style.cssText = 'position:absolute;width:0;height:0;margin:0;padding:0;border:1px dashed #eee;background-color:#aaa;z-index:1000;opacity:0.6;display:none;';    selDiv.id = 'selectDiv';    // 添加框選元素到容器內    document.getElementById('selectContainer').appendChild(selDiv);    selDiv.style.left = startX + 'px';    selDiv.style.top = startY + 'px';   }, 300);  }  document.onmousemove = function (e) {   if (!mouseOn) return;   clearEventBubble(e);   var selectContainer = document.getElementById('selectContainer');   var _x = e.clientX - selectContainer.offsetLeft + selectContainer.scrollLeft;   var _y = e.clientY - selectContainer.offsetTop + selectContainer.scrollTop;   var _H = selectContainer.clientHeight;   // 鼠標移動超出容器內部,進行相應的處理   // 向下拖拽   if (_y >= _H && selectContainer.scrollTop <= _H) {    selectContainer.scrollTop += _y - _H;   }   // 向上拖拽   if (e.clientY <= selectContainer.offsetTop && selectContainer.scrollTop > 0) {    selectContainer.scrollTop = Math.abs(e.clientY - selectContainer.offsetTop);   }   var selDiv = document.getElementById('selectDiv');   selDiv.style.display = 'block';   selDiv.style.left = Math.min(_x, startX) + 'px';   selDiv.style.top = Math.min(_y, startY) + 'px';   selDiv.style.width = Math.abs(_x - startX) + 'px';   selDiv.style.height = Math.abs(_y - startY) + 'px';  };  document.onmouseup = function (e) {   if (!mouseOn) return;   clearEventBubble(e);   var selDiv = document.getElementById('selectDiv');   var fileDivs = document.getElementsByClassName('fileDiv');   var selectedEls = [];   var l = selDiv.offsetLeft;   var t = selDiv.offsetTop;   var w = selDiv.offsetWidth;   var h = selDiv.offsetHeight;   for (var i = 0; i < fileDivs.length; i++) {    var sl = fileDivs[i].offsetWidth + fileDivs[i].offsetLeft;    var st = fileDivs[i].offsetHeight + fileDivs[i].offsetTop;    if (sl > l && st > t && fileDivs[i].offsetLeft < l + w && fileDivs[i].offsetTop < t + h) {     selectedEls.push(fileDivs[i]);    }   }   console.log(selectedEls);   selDiv.style.display = 'none';   mouseOn = false;  };  function clearEventBubble (e) {   if (e.stopPropagation) e.stopPropagation();   else e.cancelBubble = true;   if (e.preventDefault) e.preventDefault();   else e.returnValue = false;  } })();</script>

使用前端框架

上邊的代碼,我們只是在一個html文件里邊實現了框選的功能。很多時候,我們會使用一些前端框架來編寫框選的功能(例如vue.js,angular,react,polymer之類的前端框架)。這個時候,我們可以利用框架自身的生命周期的函數,添加對應的監聽事件,然后在mouseup事件里移除掉上邊這些事件監聽,以減少不必要的資源消耗。而且,很多時候,組件化的使用,使得被框選的元素,往往也是一個可重復利用的小組件,也是需要根據相應的框架的對應的途徑獲取到對應的DOM元素來獲取其屬性。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
不卡中文字幕av| 8090成年在线看片午夜| 美女福利精品视频| xxxx欧美18另类的高清| 日本国产高清不卡| 久久免费精品日本久久中文字幕| 国产日韩欧美另类| 在线观看日韩www视频免费| 亚洲精品自拍第一页| 国产97在线|亚洲| 国产美女精品视频免费观看| 午夜剧场成人观在线视频免费观看| 日韩电影免费在线观看中文字幕| 成人福利视频在线观看| 成人天堂噜噜噜| 国产日韩在线一区| 欧美日韩国产123| 国产精品久久久999| 国产精品福利在线观看网址| 亚洲色图欧美制服丝袜另类第一页| 日韩一区二区精品视频| 日韩电视剧免费观看网站| 欧美在线观看视频| 91精品国产综合久久久久久蜜臀| 欧美成人免费小视频| 国产精品av电影| 亚洲老头同性xxxxx| 欧美激情图片区| 日韩欧美中文第一页| 欧美日韩加勒比精品一区| 国产精品国模在线| 亚洲日本中文字幕| 欧美在线国产精品| 国产精品尤物福利片在线观看| 国产精品久久久av久久久| 精品无人国产偷自产在线| 久久99视频免费| 中文字幕日韩av电影| 国产日韩在线一区| 国产亚洲欧美日韩美女| 成人中文字幕+乱码+中文字幕| 全色精品综合影院| 欧美疯狂性受xxxxx另类| 91精品视频在线看| 最近中文字幕2019免费| 亚洲自拍偷拍视频| 中文字幕欧美日韩在线| 日韩有码在线视频| 成人国内精品久久久久一区| 日本久久久久亚洲中字幕| 66m—66摸成人免费视频| 久久久久日韩精品久久久男男| 成人激情视频小说免费下载| 国产精品视频成人| 日本亚洲精品在线观看| 色妞一区二区三区| 亚洲天堂网站在线观看视频| 成人xxxxx| 性欧美xxxx视频在线观看| 亚洲免费视频一区二区| 成人情趣片在线观看免费| 疯狂蹂躏欧美一区二区精品| 亚洲精美色品网站| 日韩欧美国产骚| 久久久久久有精品国产| 国产一区二区三区精品久久久| 欧美日韩电影在线观看| 亚洲精品999| 亚洲国产精品女人久久久| 成人黄色在线播放| 国产一区二区欧美日韩| 疯狂欧美牲乱大交777| 国产视频精品xxxx| 欧美性视频精品| 欧美精品电影免费在线观看| 日韩美女中文字幕| 日韩精品视频在线播放| 日韩午夜在线视频| 亚洲免费伊人电影在线观看av| 欧美在线视频观看| 最新中文字幕亚洲| 成人欧美一区二区三区黑人| 国产日本欧美在线观看| 美女扒开尿口让男人操亚洲视频网站| 欧美高清一级大片| 欧美在线播放视频| 国产丝袜高跟一区| 日韩欧美亚洲范冰冰与中字| 国产精品极品美女在线观看免费| 中文精品99久久国产香蕉| 美女久久久久久久| 亚洲跨种族黑人xxx| 亚洲国产三级网| 日韩美女写真福利在线观看| 国产欧美日韩精品专区| 亚洲成年人在线| 亚洲精品美女久久久久| 中文字幕日韩欧美在线| 国产亚洲欧洲高清一区| 国产中文欧美精品| 91精品国产高清久久久久久| 91精品国产综合久久香蕉| 国产经典一区二区| 久久久精品视频成人| 国产精品三级在线| 国产a级全部精品| 欧美日韩精品二区| 亚洲第一页在线| 国产www精品| 欧美一区二区视频97| 亚洲一区二区免费| 精品亚洲va在线va天堂资源站| 亚洲综合最新在线| 夜夜嗨av一区二区三区免费区| 欧美成年人在线观看| 久久影视电视剧凤归四时歌| 久久精品国产一区二区三区| 美日韩精品视频免费看| 欧美激情一级欧美精品| 亚洲一区免费网站| 国产日韩欧美电影在线观看| 亚洲精品色婷婷福利天堂| 亚洲免费视频一区二区| 精品一区二区三区三区| 欧美夫妻性生活视频| 美乳少妇欧美精品| 久久久久久久电影一区| 亚洲精品按摩视频| 欧美夫妻性视频| 久久九九全国免费精品观看| 日本亚洲欧美成人| 91午夜在线播放| 亚洲成人网av| 精品香蕉一区二区三区| 亚洲欧美三级在线| 久久人体大胆视频| 55夜色66夜色国产精品视频| 欧美成人精品影院| 日韩欧美成人免费视频| 欧美日韩国产va另类| 国产美女91呻吟求| 91精品视频免费观看| 日韩美女激情视频| 久久手机精品视频| 色婷婷**av毛片一区| 欧美色videos| 久久免费在线观看| 久久久久久综合网天天| 亚洲综合大片69999| 九九精品在线视频| 精品久久久国产精品999| 亚洲自拍偷拍福利| 日韩网站在线观看| 国产成人午夜视频网址| 久久久999精品免费| 亚洲人午夜精品免费| 日韩一区视频在线| 成人免费视频xnxx.com| 色偷偷偷综合中文字幕;dd| 欧美黑人一区二区三区| 中文字幕在线国产精品| 亚洲成人av资源网| 色综合久久88色综合天天看泰| 精品女同一区二区三区在线播放|