I am trying to catch when user press left button on mouse while hovering over cells in a html table using vanilla javascript. The purpose is to paint a cell in black when user is clicking with mouse while dragging (drawing like in MsPaint, when you draw a line for example)
I added an "over" event listener on each td of my table and used buttons property to check if left button is pressed or not:
celle = document.getElementsByTagName("td");
for (i=0;i<celle.length;i++)
celle[i].addEventListener("mouseover", function(e){
if(e.buttons == 1 ){
e.target.style.backgroundColor="black";
}
})
This code works but not always and not perfectly. First it starts setting the background color of the next element, not the one on which I pressed the mouse. Moreover, sometimes it doesn't set any color at all (there is a small icon like "accessed denied" in Chrome's window). It appears to work quite randomly and unpredicatably.
I tried also with jQuery, but I found similar problems. Anyone can help me?
Thanks a lot
Split the problem into several parts. I would add a mousedown and mouseup eventlistener to the whole window and set a global state if you're currently drawing:
var drawState=false
window.addEventListener("mousedown",function(e){
if(e.button===1){
drawState = true;
}});
window.addEventListener("mouseup",function(e){
if(e.button===1){
drawState = false;
}});
You can improve the window listeners with some checks, if the mouse is over a cell.
After this you can add a mouseenter listener to all your cells. Mouseenter is only fired once you enter a cell and not on every move inside the element:
celle[i].addEventListener("mouseenter", function(e){
if(drawState){
e.target.style.backgroundColor="black";
}
})
Instead of tracking mouseover, track three events:
mousemove - to constantly get the mouse position
mousedown - to set the mouse state as currently clicked down
mouseup - to set the mouse state as currently released
It works this way:
handleMousemove constantly updates the mouse position and check mouse state
When the mouse is clicked down, handleMousedown is fired
handleMousedown set the state as 'down'
When handleMousemove sees that mouse state is 'down', it fires click event at the current mouse position
When the mouse is released, handleMouseup is fired
handleMouseup set the state as 'released' and everything returns to normal
Repeat
var mouseIsDown = false;
var mousePosition = { x:-1, y:-1 };
let handleMousemove = (event) => {
// get the mouse position
mousePosition.x = event.x;
mousePosition.y = event.y;
if(mouseIsDown) // if mouse state is currently down, fire click at mouse position
{
let elem = document.elementFromPoint(mousePosition.x, mousePosition.y);
// you can add some conditions before clicking
if(something)
{
elem.click();
}
}
};
let handleMousedown = (event) => {
mouseIsDown = true;
// set the mouse state as 'down'
};
let handleMouseup = (event) => {
mouseIsDown = false;
// set the mouse state as 'release'
};
document.addEventListener('mousemove', handleMousemove);
document.addEventListener('mousedown', handleMousedown);
document.addEventListener('mouseup', handleMouseup);
Working fiddle: https://jsfiddle.net/Black3800/9wvh8bzg/5/
Thanks to everybody for your kind answers. Proposed codes work almost ok. The only problem is that sometimes browser shows the NO SYMBOL cursor. Unfortunately I can't post an image but you can find it here:
NO Symbol
and the only way to keep on drawing is clicking outside the table and then clicking again inside.
This is my code:
var mouseIsDown = false;
var mousePosition = { x:-1, y:-1 };
let handleMousemove = (event) => {
// get the mouse position
mousePosition.x = event.x;
mousePosition.y = event.y;
if(mouseIsDown) // if mouse state is currently down, fire click at mouse position
{
let elem = document.elementFromPoint(mousePosition.x, mousePosition.y);
// you can add some conditions before clicking
if (event.buttons==1)
{
elem.click();
}
}
};
let handleMousedown = (event) => {
mouseIsDown = true;
// set the mouse state as 'down'
};
let handleMouseup = (event) => {
mouseIsDown = false;
// set the mouse state as 'release'
};
document.addEventListener('mousemove', handleMousemove);
document.addEventListener('mousedown', handleMousedown);
document.addEventListener('mouseup', handleMouseup);
celle = document.getElementsByTagName("td");
for (i=0;i<celle.length;i++)
celle[i].addEventListener("click", function(e){
e.target.style.backgroundColor="black";
}
)
Isn't it easier to just add a listener for "click" ? If the element is clicked it also over the cell.
celle[i].addEventListener("click", function(e){
e.target.style.backgroundColor="black";
}
Related
I want to get and keep only the original target element of a mousedown event -- but as the user holds the mouse down, the event.target changes depending on what's under the pointer.
There is a property event.originalTarget which does exactly what I want, but it's only supported by Firefox (reference: https://developer.mozilla.org/en-US/docs/Web/API/Event/originalTarget ).
How can I replicate this behavior using standard event.target?
Figured it out: you need to create flags outside the event listener.
let el = false
let mousedown = false;
document.addEventListener('mousedown', ev => {
if (!mousedown) {
el = ev.target;
mousedown = true;
}
});
document.addEventListener('mouseup', ev => {
mousedown = false;
el = false;
});
So el will be set as the original target for the duration of the mousedown, but on mouseup, it's reset back to nothing.
I'm writing a desktop app using nwjs, and I want to use the right mouse button for some UI functions. This is working pretty ok right now; I am able to disable the context menu when the right click was for a UI function.
However, I am having an awful time figuring out how to not only stop right click events from opening a context menu, but also to stop them from selecting the text under the cursor.
Here is an example of what is happening (that I do not want to happen) - I am left-click dragging a handle to resize a UI view, and then while the left mouse is held down I am right clicking to cancel the resize. When the right click ends over any text, the text is selected. (Normally, a context menu would also appear.)
When handling the right mouse down event and context menu event, I am calling event.preventDefault() and returning false.
What the actual event handler code looks like (appearing in the same order as the events are spawned and handled)...
this.windowMouseDownListener = event => {
if(this.draggingResize &&
event.button === 2 && !event.ctrlKey
){
for(let view of this.area.views){
view.size = view.sizeBeforeDrag;
}
this.area.updateElementSizes();
this.draggingResize = false;
this.recentDraggingResize = true;
event.preventDefault();
event.stopPropagation();
return false;
}
};
this.windowContextMenuListener = event => {
if(this.recentDraggingResize){
event.preventDefault();
return false;
}
};
this.windowMouseUpListener = event => {
this.sizeBeforeDrag = this.size;
if(this.size <= 0.0001){
this.area.removeView(this);
}
if(this.draggingResize || this.recentDraggingResize){
this.recentDraggingResize = false;
this.draggingResize = false;
event.preventDefault();
event.stopPropagation();
return false;
}
};
How can I fix this behavior?
I have a field that when you leave focus on it, it changes the layout of the page. I also have buttons on the page that submit my form.
If I go into my field and type a value, then click the button, the button click event never fires. This seems to happen because the layout is changing before the click event gets fired, which means the button changes places. By the time the click event fires, it's firing on an empty area, not the button.
Here is a jsfiddle of the issue: http://jsfiddle.net/xM88p/
I figured out a way to solve this for IE but after extensive research I can't find/access the same object in FF/Chrome:
//only works in IE
if(event.originalEvent.toElement){
$("#"+event.originalEvent.toElement.id).click();
}
http://jsfiddle.net/xM88p/2/
Use mousedown instead of click:
$("#btn_test").on('mousedown', function (event){
alert("clicked!");
});
$('#test').focusout(function (event){
$('<p>Test</p>').insertAfter(this);
});
Edit
Okay, I got a little more creative with the event handlers. The new solution keeps track of mousedown/mouseup events as well as the position of the click. It uses these values to check whether mouse up should execute an alert.
var testClicked = false;
var lastX, lastY;
$(document).on('mouseup', function (event) {
if (testClicked === true && lastX === event.clientX && lastY === event.clientY) {
alert("clicked!");
}
testClicked = false;
lastX = null;
lastY = null;
});
$("#btn_test").on('mousedown', function (event){
testClicked = true;
lastX = event.clientX;
lastY = event.clientY;
});
$('#test').focusout(function (event){
$('<p>Test</p>').insertAfter(this);
});
I been playing around with html5 canvas and ran into a problem.
canvas.onmousedown = function(e){
dragOffset.x = e.x - mainLayer.trans.x;
dragOffset.y = e.y - mainLayer.trans.y;
canvas.onmousemove = mouseMove;
}
canvas.onmouseup = function(e){
canvas.onmousemove = null;
}
canvas.onmouseclick = mouseClick;
function mouseMove(e){
mainLayer.trans.x = e.x - dragOffset.x;
mainLayer.trans.y = e.y - dragOffset.y;
return false;
}
function mouseClick(e){
// click action
}
In this code, I make my mouse click+drag pan the canvas view by translating by the drag offset. But I also have a click event. Right now, whenever I drag my mouse and let go, it runs both onmouseup AND onclick.
Are there any techniques to make them unique?
A click event happens after a successful mousedown and mouseup on an element. Is there any particular reason you are using click on top of the mouse events? You should be fine with just mousedown/mouseup, click is a convenience event that saves you a little bit of coding.
I would generally do it this way:
var mouseIsDown = false;
canvas.onmousedown = function(e){
dragOffset.x = e.x - mainLayer.trans.x;
dragOffset.y = e.y - mainLayer.trans.y;
mouseIsDown = true;
}
canvas.onmouseup = function(e){
if(mouseIsDown) mouseClick(e);
mouseIsDown = false;
}
canvas.onmousemove = function(e){
if(!mouseIsDown) return;
mainLayer.trans.x = e.x - dragOffset.x;
mainLayer.trans.y = e.y - dragOffset.y;
return false;
}
function mouseClick(e){
// click action
}
in the function mouse move set a boolean to say that a move occured, encompase all the code in mouseup and click with an if statement to check that a drag did not occur. After these if statements and before the end of the functions set the dragging boolean to false.
Try declaring a variable such as clickStatus = 0; and at the start of each function check to ensure the correct value.
if (clickstatus == 0) {
clickstatus =1;
...//function code
};
I need to do the following. As soon as the user clicks on a div, i want to save the mouse coordinations while the user is moving the cursor over the div and is holding the left mouse button. When the user leaves the div or releases the left button, i want to stop recording the coordinates. I've got the following code:
$(document).ready(function() {
var coordhdl = new coordinateHandler();
$("#test").mousedown(function(e) {
$("#test").mousemove(function(ee) {
$("#test").mouseup(function(e) {
stopIt = true;
});
if(stopIt == false)
{
coordhdl.addCords(ee.pageX - this.offsetLeft, ee.pageY - this.offsetTop);
}
});
});
});
The problems with this code are:
It records coordinate even when the user only clicked the div without pressing the left button.
It doesn't stop recording the coordinates once it has been clicked.
I am new to Javascript/jQuery, so I don't know very much about it.
Something like this should work. It sets a flag to true/false when the mouse is pressed/released respectively. When the mouse moves, if the flag is set, the coordinates are added:
$(document).ready(function() {
var isDown = false,
coordhdl = new coordinateHandler();
$("#test").mousedown(function() {
isDown = true;
}).mouseup(function() {
isDown = false;
}).mousemove(function(e) {
if(isDown) {
coordhdl.addCords(ee.pageX - this.offsetLeft, ee.pageY - this.offsetTop);
}
});
});
Here's a demo of something similar in action (it simply writes the coordinates to a p element instead of using your coordinateHandler object).
Don't attach the event handlers inside the event handlers. On every mouse move you attach a new mouseup event handler. They don't get overridden, they get appended.
Use a "global" flag instead:
$(document).ready(function() {
var coordhdl = new coordinateHandler(),
recording = false;
$("#test").mousedown(function(e) {
recording = true;
}).mousemove(function(e) {
if(recording) {
coordhdl.addCords(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
}
}).mouseup(function(e) {
recording = false;
});
});
Every time there is a mousedown event, you add a mousemove handler, and every time the mouse moves, you add another mouseup handler. I can't see where the stopIt variable is declared so the scope of this variable may also be an issue. You don't need to nest the handlers, so try it this way.
$(document).ready(function() {
var coordhdl = new coordinateHandler();
var isRecording = false;
$("#test").mousedown(function(e) { isRecording = true })
.mouseup(function(e) { isRecording = false })
.mousemove(function(ee) {
if(isRecording)
{
coordhdl.addCords(ee.pageX - this.offsetLeft, ee.pageY - this.offsetTop);
}
});
});