I have set up drag and drop on a list of items so that the user can drag and drop an item into a preview pane and see its content. As expected the cursor changes to the expected "ghost" image and everything works as expected.
Previously, in the same application, I created custom scrollbars that work by nesting mousemove and mouseup within a mousedown on the scrollbar. As the mouse moves the page scrolls. This still works as expected.
However after scrolling, the preview system's drag and drop cursor is messed up: it no longer changes to the expected cursor(s) & attached ghost image.
I suspect that the act of scrolling (dragging the "scrubber" up and down the scrollbar's "track" is somehow triggering the html5 d&d system. I have tried putting e.preventDefault pretty much everywhere in the scrollbar to no effect.
I'm guessing that somehow the html5 d&d needs to be disabled while scrolling, or fooled into thinking that the scrollbar, while not a real drag and drop, has actually completed or reset whatever flags, or fulfilled whatever expectations, the d&d system has for a completed operation.
As a test I used the html5 d&d for the scrollbar (but due to the cursor changes on dragging just looks weird) and as expected the item preview d&d works correctly with all the expected cursor behaviors.
Any suggestions on how to reset so that the d&d cursor shows correctly would be much appreciated.
The code is in an Edge Animate framework, here are the key fragments:
//scrollbar code
Symbol.bindElementAction(compId, symbolName, "${scrubber}", "mouseenter", function(sym, e) {
sym.$("scrubber").attr("draggable", "false");
return false; ///
});
Symbol.bindElementAction(compId, symbolName, "${scrubber}", "mousedown", function(sym, e) {
e.preventDefault();
e.stopPropagation();
var scrubber = sym.$("scrubber");
var mouseButton = e.button;
if(mouseButton == 2){
return false;
}
var doMoveAtEnd = false;
canDrag = true;
sym.$("Hithilite").show();
var barProp = voodoo.scrollbarCalc("", "vertical", "verticalscroll.scrubber.init");
getStage().getSymbol("Domtop").$("Domtopreduced").mousemove(function(e){
e.preventDefault();
e.stopPropagation();
if(mouseButton !== 2 && canDrag){
isDrag = true;
var pos = 0;
var currOffsetY = scrubber.offset().top;
var possibleY = e.pageY;
if(possibleY > currOffsetY){
scrollDir = "up";
}
else if(possibleY < currOffsetY){
scrollDir = "down";
}
pos = pos + possibleY;
if(pos !== 0){
scrollProp = voodoo.scrollbarCalc(e, "vertical", "verticalscroll.scrubber.2");
voodoo.viewScroll(e, "mousedrag", scrollDir, scrollProp[7]);
}
pos = 0;
}
}
return false;
});
getStage().getSymbol("Domtop").$("Domtopreduced").mouseup(function(e){
e.preventDefault();
e.stopPropagation();
var mouseButton = e.button;
if(mouseButton !== 2){
isDrag = false;
canDrag = false;
setVar("scrubber", "");
}
return false;
});
return false;
});
//Preview drag & drop
//drag source
sym.$("hotspotfocus").attr("draggable", "true");
Symbol.bindElementAction(compId, symbolName, "${hotspotfocus}", "dragstart", function(sym, e) {
if(getVar("hardpreview") == "off"){
return false;
}
setVar("dragDropItem", e.target.id);
setVar("isDrag", true);
});
//drag target
Symbol.bindElementAction(compId, symbolName, "${hpvslot1}", "dragover", function(sym, e) {
e.preventDefault();
e.stopPropagation();
if(getVar("hpvslot1") === ""){
sym.$("hpvslot1BG").fadeTo(0, 0.5);
}
else{
sym.$("hpvslot1BG").show();
}
return false; ///
});
Symbol.bindElementAction(compId, symbolName, "${hpvslot1}", "drop", function(sym, e) {
e.preventDefault();
e.stopPropagation();
sym.$("hpvslot1BG").fadeTo(0, 1);
voodoo.hpvDrop("hpvslot1");
return false; ///
});
Symbol.bindElementAction(compId, symbolName, "${hpvslot1}", "dragleave", function(sym, e) {
e.stopPropagation();
e.preventDefault();
if(getVar("hpvslot1") !== ""){
sym.$("hpvslot1BG").hide();
}
else{
sym.$("hpvslot1BG").fadeTo(0, 1);
}
});
Symbol.bindElementAction(compId, symbolName, "${hpvslot1}", "dragend", function(sym, e) {
e.preventDefault();
sym.$("hpvslot1BG").hide();
});
Stumbled on the answer by accident.
It turns out that once the scrollbar's mousemove is activated by the scrubber (ie the first time you scroll by dragging) the scrollbar's mousemove function remains activated and scrolling anywhere in the view triggers mousemove.
This unintentional triggering is defeating the correct cursor response in the html5 drag&drop.
By stopping propagation at a lower level this is prevented (the event is being triggered in the scrollbar even though the mouse button is not "down" on the scrubber).
Unexpected!
In essence the issue here is that even though the offending mousemove handler is nested in a situation where you wouldn't think it would be fired unless the enclosing mousedown is triggered, it is, after being triggered for the first time, always triggered, regardless of whether the mouse is down or not.
The answer is have only one mousemove handler connected to the element...
Related
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";
}
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 was asked to implement ctrl+mousewheel event for our page site in order to change image offset on user zoom in or zoom out. I found this old answer Override browsers CTRL+(WHEEL)SCROLL with javascript and I`ve tried to do the same.
I downloaded the jQuery Mouse Wheel Plugin for the implementation and here is my code:
var isCtrl = false;
$(document).on('keydown keyup', function(e) {
if (e.which === 17) {
isCtrl = e.type === 'keydown' ? true : false;
}
}).on('mousewheel', function(e, delta) { // `delta` will be the distance that the page would have scrolled;
// might be useful for increasing the SVG size, might not
if (isCtrl) {
e.preventDefault();
alert('wheel');
}
});
The events works fine separately, but if I hold the CTRL button and wheel the mouse the wheel event does not fire.
Does any one have better solution for this or may be I did something wrong?
Fiddle, In order for it to work you have to click in the result box first before trying.
$(window).bind('mousewheel DOMMouseScroll', function(event)
{
if(event.ctrlKey == true)
{
event.preventDefault();
if(event.originalEvent.detail > 0) {
console.log('Down');
}else {
console.log('Up');
}
}
});
To check if the ctrl key is clicked, the event already provides a way to do that. Try this:
.on('mousewheel', function(e) {
if (e.ctrlKey) {
e.preventDefault();
alert('wheel');
}
});
This also works for e.shiftKey, e.altKey etc. I would only listen for the scroll event and there I would check if the ctrlKey is down.
This can be achieved with the wheel event, which is the standard wheel event interface to use.
document.getElementById('id_of_element')
.addEventListener('wheel', (e) => {
if(e.ctrlKey)
alert("Control + mouse wheel detected!");
})
I'm trying to write mechanism on site which prevents users to scroll normally. When user scrolls down or up the site is smoothscrolling to next or previous slide (depends on scrolling direction) and stops there (like when you click on a navbar). See live preview: CLICK HERE
But there's an annoying problem. It works almost good in FF (no jumping), but breaks in another browsers (Chrome, Safari, IE)- it jumps. How can I prevent this?Here are snippets from my code.
I have a ScrollControl object where I prevent scrolling:
scrollControl = {
keys : [32, 37, 38, 39, 40],
scrollTimer : 0,
lastScrollFireTime : 0,
preventDefault : function(e){
e = e || window.event;
if (e.preventDefault)
e.preventDefault();
e.returnValue = false;
},
keydown : function(e){
for (var i = scrollControl.keys.length; i--;) {
if (e.keyCode === scrollControl.keys[i]) {
scrollControl.preventDefault(e);
return;
}
}
},
wheel : function(e){
scrollControl.preventDefault(e);
},
disableScroll : function(){
if (window.addEventListener) {
window.addEventListener('DOMMouseScroll', scrollControl.wheel, false);
}
window.onmousewheel = document.onmousewheel = scrollControl.wheel;
document.onkeydown = scrollControl.keydown;
},
enableScroll : function(){
if (window.removeEventListener) {
window.removeEventListener('DOMMouseScroll', scrollControl.wheel, false);
}
window.onmousewheel = document.onmousewheel = document.onkeydown = null;
}
}
Then I'm listening if mousewheel occurs and trying to execute function only once (I'm using this plugin to detect mousewheel PLUGIN )
$(window).mousewheel(function(objEvent, intDelta){
var minScrollTime = 1000;
var now = new Date().getTime();
function processScroll() {
console.log("scrolling");
if(intDelta>0){
$.smoothScroll({
speed:med.effectDuration,
easing:med.scrollEase,
scrollTarget:med.prevPage,
afterScroll: function(){
med.currentPage = med.prevPage;
med.setActiveNav();
med.setSlides();
med.runAnimations();
}});
}else if(intDelta<0){
//scrollControl.disableScroll();
$.smoothScroll({
speed:med.effectDuration,
easing:med.scrollEase,
scrollTarget:med.nextPage,
afterScroll: function(){
med.currentPage = med.nextPage;
med.setActiveNav();
med.setSlides();
med.runAnimations();
}});
}
}
if (!scrollControl.scrollTimer) {
if (now - scrollControl.lastScrollFireTime > (3 * minScrollTime)) {
processScroll(); // fire immediately on first scroll
scrollControl.lastScrollFireTime = now;
}
scrollTimer = setTimeout(function() {
scrollControl.scrollTimer = null;
scrollControl.lastScrollFireTime = new Date().getTime();
processScroll();
}, minScrollTime);
}
});
I'm executing scrollControl.disableScroll function on DOM ready event when users starts website. And actually scrolling once prevention doesn't works prefectly and sometimes it triggers smoothscrolling twice. What am I doing wrong? Thanks in advance for any suggestions!
I had the same issue the Mouse Wheel Event was fired Twice.
function wheelDisabled(event){
event.preventDefault();
event.stopImmediatePropagation();
return false;
}
Also you might use both of these Events.
window.addEventListener('DOMMouseScroll', wheel, false);
window.addEventListener('mousewheel', wheel, false);
Instead of trying to prevent scrolling with Javascript, I would try a different approach. This approach includes CSS and Javascript to make sure the website is never bigger then the viewport (hence no scrollbars!).
Use CSS to force the main wrapping div (a div that wraps all the content on the site) to have overflow: hidden. Then use Javascript to dynamically ensure that the height and width of this div is always equal to the viewport's height and width.
In this scenario, if you want to implement scrolling in a predefined way you choose you can dynamically add negative margin-top (or negative margin-left for horizontal scrolling) to the parent wrapping div to give it the appearance that it is scrolling.
Is there a way to check if the space bar and at the same time track what direction the mouse is moving and how far etc.
Point of this is that I want to replicate how Photoshop scrolls when you hold the space bar, left mouse button and you move the mouse, but without having to hold down the left mouse button.
You can use keydown() and keyup() to track if the space bar is pressed or not and look at that state in your mousemove() event handler. For example:
var space = false;
$(function() {
$(document).keyup(function(evt) {
if (evt.keyCode == 32) {
space = false;
}
}).keydown(function(evt) {
if (evt.keyCode == 32) {
space = true;
console.log('space')
}
});
});
And then your mousemove() handler can see if it's pressed or not.
you will probably have to be watching for the keydown event, check to see that it's the spacebar, set a variable saying it's down, unset it when the keyup event is seen.
so, then you would look for mouse movements when that variable was set indicating the spacebar was pressed.
This is my solution:
var allowed = true;
$(document).ready(
function () {
$(document).bind('keydown', 'space', function () {
if (!allowed) return;
allowed = false;
$('#viewport').
dragscrollable();
});
$(document).bind('keyup', 'space', function () {
allowed = true;
$('#base').off('mousedown');
return false;
});
});
Works with jQuery and the Dragscrollable Plugin.