19
I wanted to know what is the name of this 'effect' ('-') that serves to select folders and files in Windows Explorer.
And if possible wanted to know how to do this with pure Javascript.
19
I wanted to know what is the name of this 'effect' ('-') that serves to select folders and files in Windows Explorer.
And if possible wanted to know how to do this with pure Javascript.
35
The main characteristic of this "effect" is to have a rectangle created during the selection process to demarcate the area being selected. It has one vertex at the starting point of the click and the other that accompanies the cursor, and is only visible while holding the mouse.
First we create a <div>
invisible and apply some CSS to give the desired look:
<div id="selection"></div>
#selection {
display: none;
position: absolute;
background: lightblue;
border-color: blue;
border-width: 1px;
border-style: solid;
opacity: .5;
}
Having this we will base ourselves on events onmousedown
, onmousemove
and onmouseup
to create the behavior. Follow the code:
(function() {
var beginX, beginY; // a posição do vértice fixo
var active; // se a seleção está ativa (visível)
var selection = document.getElementById("selection"); // o elemento
window.onmousedown = function (e) {
beginX = e.clientX;
beginY = e.clientY;
active = true;
selection.style.display = "block"; // deixar a div visível
window.onmousemove(e); // forçar a atualização de posição (função abaixo)
};
window.onmousemove = function (e) {
if (active) {
// cx,cy = a posição do segundo vértice
var cx = e.clientX;
var cy = e.clientY;
// x,y,w,h = o retângulo entre os vértices
var x = Math.min(beginX, cx);
var y = Math.min(beginY, cy);
var w = Math.abs(beginX - cx);
var h = Math.abs(beginY - cy);
// aplicar a posição e o tamanho
selection.style.left = x+"px";
selection.style.top = y+"px";
selection.style.width = w+"px";
selection.style.height = h+"px";
}
};
window.onmouseup = function (e) {
active = false; // desligar
selection.style.display = "none"; // e ocultar
};
})();
To keep the cursor stable as an arrow and avoid changing to others during selection (try clicking and dragging on an empty page, it changes), I will use this CSS. It’s not exactly pretty, but it works:
* {
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
cursor: default;
}
Here is the result so far: Jsfiddle.
Now we have a rectangle as desirable. But it has no function at all for now. We have to create selectable things. Let’s create some:
<div class="selectable">A</div>
<div class="selectable">B</div>
<div class="selectable">C</div>
<div class="selectable">D</div>
<div class="selectable">E</div>
<div class="selectable">F</div>
<div class="selectable">G</div>
<div class="selectable">H</div>
.selectable {
font-size: 3em;
background-color: lightgray;
border-radius: 10px;
display: inline-block;
width: 100px;
line-height: 100px;
margin: 10px;
text-align: center;
}
This should create a lot of comic books with letters. Our targets. To make the selection interact with them just that every time the mouse is moved check which items the selection rectangle overlaps. All that needs to be done is a rectangle collision check inside a loop. To get the rectangle of each item is used the function getBoundingClientRect()
. At the end of the function onmousemove
:
// procurar elementos selecionados
var list = document.getElementsByClassName("selectable");
for (var i = 0; i < list.length; ++i) {
var rect = list[i].getBoundingClientRect();
if (rect.bottom > y && rect.top < y+h && rect.right > x && rect.left < x+w) {
list[i].classList.add("mark");
}
else {
list[i].classList.remove("mark");
}
}
And in the onmouseup
unmark what was left marked:
// desmarcar tudo.
// aqui você pode fazer algo diferente manter marcado
var list = document.getElementsByClassName("selectable");
for (var i = 0; i < list.length; ++i) {
list[i].classList.remove("mark");
}
This is the point where you can do something with the selection, like keep the items marked for example.
Of course, the class .mark
so that we can see the effect:
.selectable.mark {
background-color: yellow;
}
Once again: Jsfiddle.
Now maybe we don’t want the whole page to be a selection area, but limit all of that to a single element. Let’s define a container:
<div id="container">
<div id="selection"></div>
<div class="selectable">A</div>
<div class="selectable">B</div>
<div class="selectable">C</div>
<div class="selectable">D</div>
<div class="selectable">E</div>
<div class="selectable">F</div>
<div class="selectable">G</div>
<div class="selectable">H</div>
</div>
#container {
margin: 40px;
padding: 10px;
border-style: solid;
}
For this to work we will use again the getBoundingClientRect()
and take a limit rectangle.
var limit = document.getElementById("container").getBoundingClientRect();
First, add to the beginning of onmousedown
so that nothing happens if the click was out of limit:
// se o clique foi fora do limite, não continuar
if (e.clientX > limit.right || e.clientX < limit.left ||
e.clientY > limit.bottom || e.clientY < limit.top) {
return;
}
As a last modification, make the second vertex of the selection (the one that is mobile and follows the cursor) never leave the limit. No onmousemove
:
var cx = Math.max(Math.min(e.clientX, limit.right), limit.left);
var cy = Math.max(Math.min(e.clientY, limit.bottom), limit.top);
The result so far: Jsfiddle.
The problem so far is that you put enough items to cause an overflow and create scroll, it will not be taken into account and the selection will happen in the wrong position.
This happens because the mouse position is returned relative to the screen and not to the point 0,0 of the page. Also the getBoundingClientRect()
refers to the screen. So we do not need to touch the check of the onmousedown
, but we need to set correct values in the beginX/Y
. So:
beginX = e.clientX + document.body.scrollLeft;
beginY = e.clientY + document.body.scrollTop;
Already in the onmousemove
we have some changes to make. First create variables for the actual mouse position, referring to the body
and not to the canvas:
var sx = document.body.scrollLeft;
var sy = document.body.scrollTop;
var mx = e.clientX + sx;
var my = e.clientY + sy;
And use these variables in the vertex calculation:
var cx = Math.max(Math.min(mx, limit.right), limit.left);
var cy = Math.max(Math.min(my, limit.bottom), limit.top);
Now x,y,w,h
will be correct, but the comparison that checks which items are selected will fail because getBoundingClientRect()
still refers to the screen. Just correct the measures on the conditional:
var rect = list[i].getBoundingClientRect();
if (rect.bottom+sy > y && rect.top+sy < y+h && rect.right+sx > x && rect.left+sx < x+w) {
list[i].classList.add("mark");
}
else {
list[i].classList.remove("mark");
}
The final result: Jsfiddle.
5
I got the answer from @Guilherme Bernal (more precisely the example of selecting div’s) and incremented so that they remain selected in the event mouseup.
Example: Jsfiddle
The changes were:
window.onmousedown = function (e) {
[...]
selecteds = [];
};
window.onmousemove = function (e) {
[if->for]
var rect = list[i].getBoundingClientRect();
if (rect.bottom > y && rect.top < y+h &&
rect.right > x && rect.left < x+w) {
list[i].style.backgroundColor = "red";
selecteds.push(list[i]);
} else {
list[i].style.backgroundColor = "lightgreen";
}
[/if->/for]
};
window.onmouseup = function (e) {
[...]
for (var i = 0; i < list.length; ++i) {
if(selecteds.length > 0){
index = list.indexOf(i);
if (index > -1) {
array.splice(index, 1);
}
}
if(indexOf.call(selecteds, list[i])){return;}
list[i].style.backgroundColor = "lightgreen";
}
};
Browser other questions tagged javascript
You are not signed in. Login or sign up in order to post.
=The/ if possible I wanted the 'Selection' to select Ivs, as shown in the image there, if possible :D
– Iago Bruno
Now you have the selection rectangle and the rectangle of each div. Just calculate a collision of rectangles within the
onmousemove
for each "selectable" div. The basic idea is this.– Guilherme Bernal
ah right, vlw the/
– Iago Bruno
Could you update your example to select Divs? rsrsrs failed to do =/
– Iago Bruno
@Iagobruno added an example for this too
– Guilherme Bernal
Thank you @Guilhermebernal Õ/
– Iago Bruno
Just one thing, I want you to only select elements within a tag, in case I start selecting outside the tag, it won’t work, I tried it but it didn’t work very well, I used the method e.target to check, but it checks the element clicked or if I click on some element inside the tag it will send the tag clicked not the tag I want '-' I think you understand kkkkk
– Iago Bruno
@Iagobruno added this too. Take a look.
– Guilherme Bernal
@Guilhermebernal opened the 3 Jsfiddle’s and none of them worked I’m using
Mozilla Firefox 27.0
– Paulo Roberto Rosa
@Pauloroberto I updated all three. The problem is that firefox does not define the
e.x
, different from Chrome. I usede.clientX
, that both browsers define.– Guilherme Bernal
dude! turned out really good ! : ) @Guilhermebernal I didn’t know I had how to do it so easy I thought it was extremely more complex ! :)
– Paulo Roberto Rosa
@Guilhermebernal Your example was the best I found on the internet, but since the day you answered the question I’ve been trying to make it work with scroll in other =/positions as well. If it wasn’t too much to ask =p I wanted you to adapt so that the scroll bar wouldn’t affect the functionality. It would have like?
– Iago Bruno
@Iagobruno I finished rewriting everything (the answer ended up having enough visibility). See now :)
– Guilherme Bernal
Thanks, but the scroll went wrong =/
– Iago Bruno
@Iago Can you give more details of "didn’t work"? Which browser?
– Guilherme Bernal
I was able to fix it, you just replaced Scrollleft with Top and Top with left. http://jsfiddle.net/qnN55/2/
– Iago Bruno