How to make a whole website scrollable by dragging - javascript

I'm using the script below (adapted from https://htmldom.dev/drag-to-scroll/) which enables a website to be scrolled by dragging it. Additionally, I remove the default draggability for links and images via css such that the page can be dragged when the cursor is currently positioned on these elements.
document.addEventListener('DOMContentLoaded', () => {
let dragging; const pos = {};
const onClick = (e) => {
if(!dragging)
return;
e.preventDefault(); // somehow does not work: why?
e.stopImmediatePropagation();
};
const onMouseMove = (e) => {
dragging = true;
window.scroll(0, pos.top - (e.clientY - pos.y));
};
const onMouseUp = (e) => {
dragging = false;
e.target.removeEventListener('mousemove', onMouseMove);
e.target.removeEventListener('mouseup', onMouseUp);
e.target.removeEventListener('click', onClick);
};
const onMouseDown = (e) => {
pos.top = window.scrollY;
pos.y = e.clientY;
e.target.addEventListener('mousemove', onMouseMove);
e.target.addEventListener('mouseup', onMouseUp);
e.target.addEventListener('click', onClick);
};
document.addEventListener('mousedown', onMouseDown);
});
a, img {
user-drag: none;
-webkit-user-drag: none;
}
<div style="height:200vh; background:lightblue; padding:1em;">
<h1>Drag to scroll me</h1>
Dragging this link should NOT open the website (but does)
</div>
Problem
Preventing the default action (opening the link) does not work somehow. How can it be canceled in case of dragging?

The solution was a bit more complex than I thought, perhaps someone else has a better one. It requires an additional global function preventClick which prevents the event default and propagation.
The rest of the code looks like this now:
$(() => {
/*
require 'preventClick()';
*/
//------------------------------------------------------------------------------------
// $
//------------------------------------------------------------------------------------
const
$body = $('body'),
$head = $('head')
;
//------------------------------------------------------------------------------------
// var
//------------------------------------------------------------------------------------
const
pos = {}
;
let
clone
;
//------------------------------------------------------------------------------------
// ()
//------------------------------------------------------------------------------------
const
//------------------------------------------
onMouseMove = (e) => {
//------------------------------------------
window.scroll(0, pos.top - (e.originalEvent.clientY - pos.y));
},
//------------------------------------------
onMouseUp = function(e){
//------------------------------------------
$body.css('cursor', 'grab');
$(this)
.off('mousemove', null, onMouseMove)
.off('mouseup', null, onMouseUp)
;
}
;
//------------------------------------------------------------------------------------
// Events
//------------------------------------------------------------------------------------
$(document)
//------------------------------------------
.mousedown(function(e){
//------------------------------------------
$body.css('cursor', 'grabbing');
pos.top = window.scrollY;
pos.y = e.originalEvent.clientY;
$(this)
.one('dragstart', function(e){
clone = $(e.target).clone(true);
$(e.target)
.attr('onclick', 'preventClick(event)')
.one('click', function(e){
$(this).attr('onclick', clone.attr('onclick') || '');
})
;
})
.mousemove(onMouseMove)
.mouseup(onMouseUp)
;
})
;
$body
//------------------------------------------
.css('cursor', 'grab')
//------------------------------------------
;
//------------------------------------------------------------------------------------
// >>
//------------------------------------------------------------------------------------
$head.append(`<style>
a, img {
user-drag: none;
-webkit-user-drag: none;
}
</style>`);
});

Related

drag and drop soccer score incrementor Javascript

I haven't been able to correctly build logic. After dropping soccer ball into the goal, the score does not increment. As stated in my JS script code, I attempted to add score++ to scoreDisplay function (in bold).
Thank you for looking at this issue.
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<h3>Score:<span id="score">0</span></h3>
<button id="start-button">Start/Pause</button>
<p>Drag the ball.</p>
<img src="https://en.js.cx/clipart/soccer-gate.svg" id="gate" class="droppable">
<img src="https://picsum.photos/150/150" id="ball">
<script>
const scoreDisplay = document.querySelector('#score')
let score = 0
let currentDroppable = null;
ball.onmousedown = function(event) {
let shiftX = event.clientX - ball.getBoundingClientRect().left;
let shiftY = event.clientY - ball.getBoundingClientRect().top;
ball.style.position = 'absolute';
ball.style.zIndex = 1000;
document.body.append(ball);
moveAt(event.pageX, event.pageY);
function moveAt(pageX, pageY) {
ball.style.left = pageX - shiftX + 'px';
ball.style.top = pageY - shiftY + 'px';
}
function onMouseMove(event) {
moveAt(event.pageX, event.pageY);
ball.hidden = true;
let elemBelow = document.elementFromPoint(event.clientX, event.clientY);
ball.hidden = false;
if (!elemBelow) return;
let droppableBelow = elemBelow.closest('.droppable');
if (currentDroppable != droppableBelow) {
if (currentDroppable) { // null when we were not over a droppable before this event
leaveDroppable(currentDroppable);
}
currentDroppable = droppableBelow;
if (currentDroppable) { // null if we're not coming over a droppable now
// (maybe just left the droppable)
enterDroppable(currentDroppable);
}
//add score
**function scoreIncrement() {
score++;
document.getElementById("score").innerText="Score: " + score;**
}​
}
}
document.addEventListener('mousemove', onMouseMove);
ball.onmouseup = function() {
document.removeEventListener('mousemove', onMouseMove);
ball.onmouseup = null;
};
};
function enterDroppable(elem) {
elem.style.background = 'pink';
}
function leaveDroppable(elem) {
elem.style.background = '';
}
ball.ondragstart = function() {
return false;
};
</script>
</body>
</html>
Thanks again for for taking the time to look this over
you were almost there!. You were missing what is called a "flag". That is a variable (usually true/false) that holds some information that allows you to make decisions in the right event (which you had).
In this case a flag could tell me if I am over or not the goal, so that when I drop the ball I can add a point to the score.
As you can see in the code below, when you enterDroppable the flag is set to true and when you leaveDroppable its set back to false; then when the event mouseUp is triggered, the flag is checked. If the flag was true, then the score is incremented (and its value is updated on the screen)
Note that I changed the style of the ball so it looks more like a ball. Also note that its not a good practice to subscribe and remove events inside other events. Its better to have the events subscribed once and have more flags: for example, set a flag in the mouseDown to know that the ball was "grabed", then the mouseMove event would check that flag to see if it had to move the ball or not. I leave it as is, so not to change your code much but keep it in mind.
See the working code below (scroll all the way down and click on "Run the snipped code" to see it in action). Also if this solved your question dont forget to mark it as such using the green check left to the answer.
const scoreDisplay = document.querySelector('#score')
let score = 0
let currentDroppable = null;
let ballIsOverDroppable = false;
ball.onmousedown = function(event) {
let shiftX = event.clientX - ball.getBoundingClientRect().left;
let shiftY = event.clientY - ball.getBoundingClientRect().top;
ball.style.position = 'absolute';
ball.style.zIndex = 1000;
document.body.append(ball);
moveAt(event.pageX, event.pageY);
function moveAt(pageX, pageY) {
ball.style.left = pageX - shiftX + 'px';
ball.style.top = pageY - shiftY + 'px';
}
function onMouseMove(event) {
moveAt(event.pageX, event.pageY);
ball.hidden = true;
let elemBelow = document.elementFromPoint(event.clientX, event.clientY);
ball.hidden = false;
if (!elemBelow) return;
let droppableBelow = elemBelow.closest('.droppable');
if (currentDroppable != droppableBelow) {
if (currentDroppable) { // null when we were not over a droppable before this event
leaveDroppable(currentDroppable);
}
currentDroppable = droppableBelow;
if (currentDroppable) { // null if we're not coming over a droppable now
// (maybe just left the droppable)
enterDroppable(currentDroppable);
}
}
}
document.addEventListener('mousemove', onMouseMove);
ball.onmouseup = function() {
document.removeEventListener('mousemove', onMouseMove);
ball.onmouseup = null;
if (ballIsOverDroppable) {
score++;
scoreDisplay.textContent = `${score}`;
}
};
};
function enterDroppable(elem) {
elem.style.background = 'pink';
ballIsOverDroppable = true;
}
function leaveDroppable(elem) {
elem.style.background = '';
ballIsOverDroppable = false;
}
ball.ondragstart = function() {
return false;
};
<h3>Score:<span id="score">0</span></h3>
<button id="start-button">Start/Pause</button>
<p>Drag the ball.</p>
<img src="https://en.js.cx/clipart/soccer-gate.svg" id="gate" class="droppable">
<img src="https://picsum.photos/20/20" id="ball" style="border-radius: 20px; border: 2px solid black;">

Detecting mouseenter event while dragging html element

I'm trying to make a sort of drag and drop puzzle website using uniform square images, and I've written the drag code to drag individual images by their <img> element.
However I noticed that while dragging an <img> element over another element with addEventListener("mousenter", function () {//code};) attached, the mouseenter event doesn't fire. If I move my mouse over the element without dragging the image, the event fires as normal.
Does anyone have any ideas on how to fix this problem? My only guess atm is that the <img> element is blocking the listener from seeing the mouse.
var dragImg = document.querySelector(".images img");
dragImg.addEventListener("mousedown", function () {
console.log("mousedown detected " + dragImg.clientHeight);
dragImg.setAttribute("id", "drag");
dragging = true;
});
dragImg.addEventListener("mousemove", function (event) {
if (dragging) {
dragImg.style.top = (event.clientY - dragImg.clientHeight/2) + "px";
dragImg.style.left = (event.clientX - dragImg.clientWidth/2) + "px";
}
});
dragImg.addEventListener("mouseup", function () {
console.log("mouseup detected");
dragging = false;
//reset position if not in clipboard
dragImg.removeAttribute("id");
dragImg.style.top = "";
dragImg.style.left = "";
});
//Clipboard behavior - This listener doesn't fire when dragging an img?
var clipboard = document.querySelector(".clipboard");
clipboard.addEventListener("mouseover", function () {
if (dragging) {
console.log("mouse entered clipboard!");
clipboard.style.backgroundColor = "red";
}
});
#drag {
position: absolute;
}
The problem is that the mouse never hover the .clipboard because the image always below the pointer.
The simple solution is to add pointer-events:none; (not supported on older browsers).
var dragImg = document.querySelector(".images img");
var dragging = false;
dragImg.addEventListener("mousedown", function (event) {
event.stopPropagation();
console.log("mousedown detected " + dragImg.clientHeight);
dragImg.setAttribute("id", "drag");
dragging = true;
document.addEventListener('mousedown', function documentMouseDown(){
if (dragging) {
dragImg.removeAttribute("id");
dragging = false;
document.removeEventListener('mousedown', documentMouseDown);
}
});
});
document.addEventListener("mousemove", function (event) {
if (dragging) {
dragImg.style.top = (event.clientY - dragImg.clientHeight/2) + "px";
dragImg.style.left = (event.clientX - dragImg.clientWidth/2) + "px";
}
});
dragImg.addEventListener("mouseup", function () {
console.log("mouseup detected");
dragging = false;
//reset position if not in clipboard
dragImg.removeAttribute("id");
dragImg.style.top = "";
dragImg.style.left = "";
});
//Clipboard behavior - This listener doesn't fire when dragging an img?
var clipboard = document.querySelector(".clipboard");
clipboard.addEventListener("mouseover", function () {
if (dragging) {
console.log("mouse entered clipboard!");
clipboard.style.backgroundColor = "red";
}
});
#drag {
position: absolute;
pointer-events:none;
}
.clipboard {
width:200px;
height:200px;
background:green;
}
img {
-webkit-user-select:none;
}
<div class="clipboard"></div>
<div class="images">
<img src="http://i.stack.imgur.com/NghNQ.jpg" />
</div>
http://jsbin.com/hugewi/edit?html,css,js

Enabling and disabling Events in javascript

So basically I have an event that occurs at page load. it causes an image to follow your cursor, but on a click event I would like to disable this event, and then on a second click re-enable the mouse follow event
I tried just creating a toggle variable but it just seems to be freezing my image.
Would .on() and .off() be appropriate here? I read the documentation but I am confused on how to implement them
I am confused as to how I would turn off an event i guess.
Jscript
var enabled = true;
$(document).ready(function() {
$(document).mousemove(function() {
if (enabled) {
$("#rocket").stop().animate({left:e.pageX, top:e.pageY});
}
});
$(document).click(function() {
enabled == !enabled;
});
});
Demo
LIVE DEMO
var enabled = true;
$(function () {
var $rocket = $('#rocket');
$(document).mousemove(function (e) {
if (enabled) {
$rocket.css({left: e.pageX, top: e.pageY});
}
}).click(function () {
enabled ^= 1;
});
});
Instead of animate() use .css()
If you really want to add a sleek animation to your catching spacecraft:
LIVE DEMO with 'animation'
$(function () {
var $rocket = $('#rocket'),
enabled = true,
mX =0, mY =0,
posX =0, posY =0,
lazy = 20; // Smooth move
$(document).mousemove(function(e){
mX = e.pageX;
mY = e.pageY;
}).click(function(){
enabled^=1;
});
intv = setInterval(function(){
if(enabled){
posX += (mX-posX) / lazy; // zeno's paradox equation "catching delay"
posY += (mY-posY) / lazy;
$rocket.css({left: posX, top: posY});
}
}, 10);
});
I might try to register and remove the handler like
$(document).ready(function () {
function handler(e) {
$("#rocket").css({
left: e.pageX,
top: e.pageY
});
}
$(document).on('mousemove.cursor', handler);
var enabled = true;
$(document).click(function () {
enabled = !enabled;
if (enabled) {
$(document).on('mousemove.cursor', handler);
} else {
$(document).off('mousemove.cursor');
}
});
});
Demo: Fiddle
Here are minor fixes to make your code work:
var enabled = true;
$(document).ready(function() {
$(document).mousemove(function(e) {
if (enabled) {
$("#rocket").stop().animate({left:e.pageX, top:e.pageY});
}
});
$(document).click(function() {
enabled = !enabled;
});
});
The fiddle is here http://jsfiddle.net/bmzyK/8/
Just added param e in the mousemove event and added JQuery to the JSFiddle. Also fixed the '==' typo.

Draggable without jQuery UI object by Id error

I am trying to make a div draggable using jQuery, but without jQuery UI, I can't figure out why I get an error if when I use the e.target.id, the value in this field at some point is empty or null.
If I hard code the Id in the function it works pretty well, what am I missing here?
I appreciate your help.
This is my code:
<div id="Div1" style="width: 100px; background-color: red">test</div>
$(document).ready(function () {
dragObject.init("Div1");
});
jQdrag.js
var dragObject = (function () {
var init = function (dragObj) {
addListeners(dragObj);
};
var addListeners = function (dragObj) {
document.getElementById(dragObj).addEventListener('mousedown', mouseDown, false);
window.addEventListener('mouseup', mouseUp, false);
};
mouseDown = function (e) {
window.addEventListener('mousemove', divMove, true);
};
mouseUp = function () {
window.removeEventListener('mousemove', divMove, true);
};
divMove = function (e) {
debugger
//var div = document.getElementById("Div1");
var div = document.getElementById(e.target.id); //ERROR
div.style.position = 'absolute';
div.style.top = e.clientY + 'px';
div.style.left = e.clientX + 'px';
};
return {
init: function (dragObj)
{
init(dragObj);
}
};
})();

Not able to input text in html text field

I have a script that makes me able to drag some content in a div.
But because you can drag around, no matter where you place the cursor in the div, I am not able to input some text in a text field.
Is it possible to allow that?
I can eaily click links in the div container. But not input text in a html input field.
This is the javascript:
$(document).ready(function () {
$('#roadmap').mousedown(function (event) {
$(this)
.data('down', true)
.data('x', event.clientX)
.data('scrollLeft', this.scrollLeft);
return false;
}).mouseup(function (event) {
$(this).data('down', false);
}).mousemove(function (event) {
if ($(this).data('down') == true) {
this.scrollLeft = $(this).data('scrollLeft') + $(this).data('x') - event.clientX;
}
}).mousewheel(function (event, delta) {
this.scrollLeft -= (delta * 30);
}).css({
'overflow': 'hidden',
'cursor': 'col-resize'
});
});
$(window).mouseout(function (event) {
if ($('#roadmap').data('down')) {
try {
if (event.originalTarget.nodeName == 'BODY' || event.originalTarget.nodeName == 'HTML') {
$('#roadmap').data('down', false);
}
} catch (e) { }
}
});
This is my extended mousewheel function (if needed)
(function ($) {
$.event.special.mousewheel = {
setup: function () {
var handler = $.event.special.mousewheel.handler;
// Fix pageX, pageY, clientX and clientY for mozilla
if ($.browser.mozilla)
$(this).bind('mousemove.mousewheel', function (event) {
$.data(this, 'mwcursorposdata', {
pageX: event.pageX,
pageY: event.pageY,
clientX: event.clientX,
clientY: event.clientY
});
});
if (this.addEventListener)
this.addEventListener(($.browser.mozilla ? 'DOMMouseScroll' : 'mousewheel'), handler, false);
else
this.onmousewheel = handler;
},
teardown: function () {
var handler = $.event.special.mousewheel.handler;
$(this).unbind('mousemove.mousewheel');
if (this.removeEventListener)
this.removeEventListener(($.browser.mozilla ? 'DOMMouseScroll' : 'mousewheel'), handler, false);
else
this.onmousewheel = function () { };
$.removeData(this, 'mwcursorposdata');
},
handler: function (event) {
var args = Array.prototype.slice.call(arguments, 1);
event = $.event.fix(event || window.event);
// Get correct pageX, pageY, clientX and clientY for mozilla
$.extend(event, $.data(this, 'mwcursorposdata') || {});
var delta = 0, returnValue = true;
if (event.wheelDelta) delta = event.wheelDelta / 120;
if (event.detail) delta = -event.detail / 3;
if ($.browser.opera) delta = -event.wheelDelta;
event.data = event.data || {};
event.type = "mousewheel";
// Add delta to the front of the arguments
args.unshift(delta);
// Add event to the front of the arguments
args.unshift(event);
return $.event.handle.apply(this, args);
}
};
$.fn.extend({
mousewheel: function (fn) {
return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
},
unmousewheel: function (fn) {
return this.unbind("mousewheel", fn);
}
});
})(jQuery);
And here goes the HTML
<div id="roadmap">
<ul>
<li class="roadmap_description">
<h2>Welcome</h2>
<p>Description</p>
</li>
<li><h3 class="milestone_name">v. 1.0.2.3</h3>
<ul>
<li class="milestone_item">Title description</li>
</ul>
<div class="milestone_item_add">
<input class="field" name="milestone_item_name" type="text" /><img src="/Intranet/admin/Intranet/css/images/icons/wand.png" alt="Add new" />
</div>
</li>
</ul>
</div>
Thank you.
The return false in mousedown prevents the mouse button press from having its default action, which is to focus the child <input>.
You could test event.target in the mousedown function to see if it's the <input> element, and if so just stop trying to handle it (return or return true straight away).
If you wanted to still allow the element to be dragged when the mouse is in the <input>, you could still return false but manually call focus() on the input in mousedown. The disadvantage of that would be that the user couldn't use drags to select part of the text, as inputs would normally allow.
Why dont you add a mouseover event to the textfield that sets this: $(this).data('down', false); ?

Categories

Resources