I would like to have my own cursor only displayed over certain images with a certain class.
I've already written a few lines for that.
Here is my code:
function registerCursorHoverEffect() {
if (!isTouch()) {
const el = document.body;
var cursorDiv = document.createElement("div");
cursorDiv.setAttribute("id", "cursor");
cursorDiv.setAttribute("class", "light-spot light-spot--cursor light-spot--center-center light-spot--color-red light-spot--filled light-spot--outside");
el.before(cursorDiv);
document.getElementById("cursor").innerHTML = '<svg class="cursor-main" xmlns="http://www.w3.org/2000/svg" width="70" height="70" viewport="0 0 100 100" style="stroke: white; fill:white;font-size:300px; z-index: 9999999;position: absolute; top: 40px; right: 40px; bottom: 0;"><path d="M59.71,31.29l-10-10a1,1,0,0,0-1.42,1.42L56.59,31H5a1,1,0,0,0,0,2H56.59l-8.3,8.29a1,1,0,0,0,0,1.42,1,1,0,0,0,1.42,0l10-10A1,1,0,0,0,59.71,31.29Z"/></svg>';
var cursorDivAppend = document.createElement("div");
cursorDivAppend.setAttribute("id", "cursor-append");
cursorDivAppend.setAttribute("class", "cursor-append");
el.before(cursorDivAppend);
// Mousemove
$(document).on('mousemove', function (event) {
if($('.col-3:hover').length != 0) {
const appendScale = 1
var destinationX = event.pageX;
var destinationY = event.pageY;
var transformScale = `translate(calc(${destinationX}px - 50%), calc(${destinationY}px - 50%)) scale(${appendScale})`
$('#cursor').css('transform', transformScale);
}
})
}
}
The following screenshot shows what is generated when you hover over an image with a certain class:
The critical part of it all is here:
// Mousemove
$(document).on('mousemove', function (event) {
if($('.col-3:hover').length != 0) {
const appendScale = 1
var destinationX = event.pageX;
var destinationY = event.pageY;
var transformScale = `translate(calc(${destinationX}px - 50%), calc(${destinationY}px - 50%)) scale(${appendScale})`
$('#cursor').css('transform', transformScale);
}
})
event.pageX should always give me the current mouse position of the $(document) and not of the event itself. Exactly the same for event.pageY. But I always get the current position of the event and I don't know why.
Can somebody help me please?
UPDATE:
However, the width of the cursor element hinders me because I get the coordinates of the $ (documents). The event is only triggered when I am outside the cursor.
Here is a screenshot and to clarify the problem:
I would like to have my own cursor only displayed over certain images with a certain class.
You can do that only with css,
.cursors {
display: flex;
justify-content: flex-start;
align-items: stretch;
height: 100vh;
}
.cursors > div {
display: flex;
justify-content: center;
align-items: center;
flex-grow: 1;
box-sizing: border-box;
padding: 10px 2px;
text-align: center;
}
.cursors > div:nth-child(odd) {
background: #eee;
}
.cursors > div:hover {
opacity: 0.25;
}
.png {
cursor: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/9632/heart.png"), auto;
}
.gif {
cursor: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/9632/happy.png"), auto;
}
.rotated {
transform: rotate(45deg); /* Equal to rotateZ(45deg) */
background-color: pink;
}
<div class="cursors">
<div><img class="png rotated"src="https://uxwing.com/wp-content/themes/uxwing/images/patreon_btn.png"/> </div>
<div class="gif">GIF</div>
</div>
Related
Intentionally posting this question on stackoverflow instead of posting it on salesforce.stackexchange.com as this problem is specific to JS/CSS
I have below LWC playground where it is not working as expected
https://app.lwc.studio/edit/CTHEQCGrn18rKmQQ0zs1 (unfortunately you might have to login to webcomponents.dev to access this link)
but on the below code pen the same is working as expected not sure what am I doing wrong.
https://codepen.io/gs650x/pen/qByPQKP
I have below HTML
<section class="resizeable-container" >
<div class="resizeable-item">
DIV1
</div>
<div class="resizer-x" onmousedown={handleOnMouseDown} onmouseup={handleOnMouseUp}></div>
<div class="resizeable-item">
DIV2
</div>
</section>
CSS
.resizeable-container {
display: flex;
min-height: 80vh;
}
.resizeable-item {
flex: 50%;
overflow: auto;
}
.resizer-x {
position: relative;
display: flex;
justify-content: center;
align-items: center;
background: hsl(212, 100%, 17%);
padding: 4px;
}
.resizer-x {
z-index: 2;
cursor: col-resize;
}
.resizer-x::before,
.resizer-x::after {
content: "";
width: 2px;
height: 16px;
margin: 2px;
background: lightgray;
}
Below is the javascript
renderedCallback() {
if (!resizer)
resizer = this.template.querySelector(".resizer-x")
//In case mouse up event occurs outside the resizer element
document.addEventListener("mouseup", this.handleOnMouseUp)
}
handleOnMouseDown(event) {
event.preventDefault();
document.addEventListener("mousemove", this.handleOnMouseMove)
document.addEventListener("mouseup", this.handleOnMouseUp)
}
handleOnMouseMove(event) {
event.preventDefault();
const clientX = event.clientX;
const deltaX = clientX - (resizer._clientX || clientX);
resizer._clientX = clientX;
const { previousElementSibling, nextElementSibling } = resizer
// LEFT
if (deltaX < 0) {
const width = Math.round(parseInt(window.getComputedStyle(previousElementSibling).width) + deltaX)
previousElementSibling.style.flex = `0 ${clientX < 10 ? 0 : clientX}px`
nextElementSibling.style.flex = "1 0"
}
// RIGHT
if (deltaX > 0) {
const width = Math.round(parseInt(window.getComputedStyle(nextElementSibling).width) - deltaX)
nextElementSibling.style.flex = `0 ${width < 10 ? 0 : width}px`
previousElementSibling.style.flex = "1 0"
}
}
handleOnMouseUp(event) {
event.preventDefault();
document.removeEventListener("mousemove", this.handleOnMouseMove)
document.removeEventListener("mouseup", this.handleOnMouseUp)
delete event._clientX
}
This works well on my 32 inches monitor but it is not working on my 14 inch laptop
expectation is to move the cursor and resizer-x at the same time but resizer-x moves first and then the cursor moves and mouse up only works if it is coming from the resizer-x anywhere else it is not working. I have added eventListener to document not on a particular object but still removeEventListener to stop handleMouseMove function doesn't stop.
Below is the code pen where it is working as expected but in LWC it doesn't behave in the same manner
https://codepen.io/gs650x/pen/qByPQKP
I have created a marquee slider. After a few iterations the marquee lacks.
I want to have the marquee to have full view-width. As I can see the marquee is lacking less on smaller devices. I have created other similar marquees before with different elements and never had that problem. The empty list elements I have implemented to have a spacing between the text elements, but the icons should be next to the text as they are currently.
(function marquee() {
var marqueeWrapper = $('.js-marquee');
var FPS = (60/100); // 60fps
var SLIDESPEED = 1000; // default | lower is faster
marqueeWrapper.each(function (index, element) {
var _marqueeWrapper = $(element);
var _marqueeTracks = $('>ul', _marqueeWrapper);
var _marqueeSlides = $('>ul>li', _marqueeWrapper);
var _marqueeWidth = parseFloat(_marqueeSlides.last().position().left + _marqueeSlides.last().outerWidth(true));
var shifted = _marqueeWrapper.attr('data-marquee-shift') || false;
var SPEED = (_marqueeWrapper.attr('data-marquee-speed') * _marqueeSlides.length) || (SLIDESPEED * _marqueeSlides.length);
var frames = SPEED * FPS;
var steps = _marqueeWidth / frames; // distance elems will move each frames
var posX = 0;
var tempSteps;
function _clone() {
var times = Math.ceil(_marqueeWrapper.outerWidth(true) / _marqueeWidth) + 1;
_marqueeTracks.each(function () {
$('>ul', _marqueeWrapper).empty();
var sliders = _marqueeSlides;
for (i = 1; i <= times; i++) {
sliders.clone().appendTo(($(this)));
}
})
}
function _animated() {
posX += -steps;
_marqueeTracks.css({
transform: 'translate3d(' + posX + 'px, 0, 0)'
});
if (Math.abs(posX) >= _marqueeWidth) {
posX = 0;
}
window.requestAnimationFrame(_animated);
}
function _pause() {
tempSteps = steps;
return steps = 0;
}
function _resume() {
return steps = tempSteps;
}
function _shiftPosition() {
if(shifted) return posX = -(_marqueeSlides.first().outerWidth(true)) / 2 ;
}
/*
function _registerEvents() {
_marqueeTracks.on('mouseenter', _pause);
_marqueeTracks.on('mouseleave', _resume);
$(window).on('resize', debounce(_clone, 300))
}*/
function init() {
_shiftPosition()
_clone();
_animated();
/*_registerEvents();*/
}
function debounce(func, wait, immediate) {
var timeout;
return function () {
var context = this,
args = arguments;
var later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};// debounce
init();
})
})();
.marquee {
background: black;
width: 100%;
overflow: hidden;
}
.marquee__track {
display: flex;
padding: 0;
margin: 0;
list-style: none;
}
.marquee__item {
display: flex;
justify-content: center;
align-items: center;
color: white;
height: 100px;
margin-right: 10px;
padding-bottom: 10px;
}
.marquee__item {
height: 80px;
}
.marquee__item_vegan {
flex: 0 0 120px;
font-size: 50px;
font-family: Gothic821;
}
.marquee__item_gluten {
flex: 0 0 160px;
font-size: 50px;
font-family: Gothic821;
}
.marquee__item_natural {
flex: 0 0 130px;
font-size: 50px;
font-family: Gothic821;
}
.marquee__item_empty {
flex: 0 0 180px;
}
.marquee__item_small {
flex: 0 0 80px;
}
.marquee__item_icon {
width: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div data-marquee-speed="100" data-marquee-shift="false" class="marquee js-marquee">
<ul class="marquee__track">
<li class="marquee__item marquee__item_small"><img class="marquee__item_icon" src="{{ 'Haken_weiss.svg' | asset_url }}"/></li>
<li class="marquee__item marquee__item_vegan">VEGAN</li>
<li class="marquee__item marquee__item_empty"></li>
<li class="marquee__item marquee__item_small"><img class="marquee__item_icon" src="{{ 'Haken_weiss.svg' | asset_url }}"/></li>
<li class="marquee__item marquee__item_gluten">GLUTENFREI</li>
<li class="marquee__item marquee__item_empty"></li>
<li class="marquee__item marquee__item_small"><img class="marquee__item_icon" src="{{ 'Haken_weiss.svg' | asset_url }}"/></li>
<li class="marquee__item marquee__item_natural">NATURAL</li>
<li class="marquee__item marquee__item_empty"></li>
</ul>
</div>
The given code seems rather complex for a simple marquee.
While you will need to invoke a little JS at the start (on a load or a resize) to make sure the timing is right for the speed required (it varies depending on viewport width) there seems little to be gained from using JS for the actual animation.
And there may be some loss in using JS which does not guarantee real-time timing (your system may be busy doing other things) and that can introduce lag. Using a CSS method can help to smooth things as there is less need to move back and forth between CPU and GPU.
The given code also performs the animation in steps, moving position, which can be jerkier than translating.
This snippet is stripped down to demonstrate the basic ideas in using HTML and CSS without JS.
As in the code in the question, a second copy of the items is made. This allows the marquee to appear continuous, as the first item disappears to the left it can be seen coming in from the right.
The units used are related to the viewport width so the whole thing is responsive. A CSS animation is used to get the movement, moving the whole 50% of its width, then when the copy comes in it is overwritten by the first part so the movement looks continuous (this is the same technique as used in the given code but with pure CSS rather than JS/CSS).
.marquee,
.marquee * {
margin: 0;
padding: 0;
border-width: 0;
}
.marquee {
background: black;
overflow: hidden;
}
.marquee>ul {
display: flex;
width: 200vw;
justify-content: space-around;
list-style: none;
animation: move 5s linear infinite;
white-space: nowrap;
}
.marquee>ul li {
font-size: 4vw;
color: white;
margin: 5vw;
position: relative;
}
.marquee__item_vegan,
.marquee__item_gluten,
.marquee__item_natural {
--bg: linear-gradient(white, white);
/* just for demo */
}
.marquee>ul li::before {
content: '';
width: 4vw;
height: 4vw;
left: -4vw;
display: inline-block;
position: absolute;
background-image: var(--bg);
background-repeat: no-repeat;
background-size: 80% 80%;
background-position: bottom left;
}
#keyframes move {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
<div class="marquee">
<ul>
<li class="marquee__item_vegan">VEGAN</li>
<li class="marquee__item_gluten">GLUTENFREI</li>
<li class="marquee__item_natural">NATURAL</li>
<li class="marquee__item_vegan">VEGAN</li>
<li class="marquee__item_gluten">GLUTENFREI</li>
<li class="marquee__item_natural">NATURAL</li>
</ul>
</div>
Notes: you may want to reinstate some JS to calculate the timing - depends on your use case.
The spurious list items have been removed. The imgs which are really just visual clues are added as before pseudo elements. The empty items are not needed. (It's best to keep formatting and content separate, especially for those using assistive technology such as screen readers).
I am trying to create a little mouse moving animation. When cursor comes near to the button I call it borderline - Certain distance from the button, the button moves to the cursor direction.
Here I have shown two steps dashed borderline with css just for clarity and understanding.
I have created the nearest borderline from code by calculating the center point of button and decreasing and adding width and height of the button for both x and y axis.
And I want to solve this in the same process I'm working not by adding other event-listener tothe parent-elements of the button.
Here is what I have tried..
const button = document.querySelector(".button");
let { width, height, x: buttonX, y: buttonY } = button.getBoundingClientRect(); // gives you width, height, left-X,top-y of the button
buttonX = buttonX + width / 2; // center point of button on x-axis
buttonY = buttonY + height / 2; // center point of button on y-axis
/*************** Functions ***************/
let distance = width;
let mouseHasEntered = true;
let mouseIsInButtonTerritory;
function mouseMove(e) {
const x = e.x; // current x of cursor
const y = e.y; // current y of cursor
const leftBorderLine = buttonX - distance;
const rightBorderLine = buttonX + distance;
const topBorderLine = buttonY - distance;
const bottomBorderline = buttonY + distance;
const xWalk = (x - buttonX) / 2; // the distance to move the button when mouse moves on X axis
const yWalk = (y - buttonY) / 2; // the distance to move the button when mouse moves on Y axis
mouseIsInButtonTerritory =
x > leftBorderLine &&
x < rightBorderLine &&
y > topBorderLine &&
y < bottomBorderline; // becomes true if mouse is inside all of these border-line
if (mouseIsInButtonTerritory) {
if (mouseHasEntered) {
// this must happen only once to create outside borderline
//creating another level borderline by incresing distance;
// while cursor is returing the button comes out of nearest border-line and return from this borderline
distance = distance + distance;
mouseHasEntered = false;
}
catchCursor(xWalk, yWalk); // call the function when mouse in in the button's territory
} else {
resetPositon();
}
}
function catchCursor(xWalk, yWalk) {
// translates the button in the direction where cursor is.
button.style.transform = `translate(${xWalk}px, ${yWalk}px)`;
}
function resetPositon() {
// resets the postion of the button as it was initial.
button.style.transform = `translate(${0}px, ${0}px)`;
mouseHasEntered = true;
// when button is return to it's position (mouseHasEntered = true) lets to increase the initial borderline of button for the next time
}
/*************** Event-handler ***************/
window.addEventListener("mousemove", mouseMove);
window.addEventListener("mouseout", resetPositon);
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--gutter-lg: 4rem;
--gutter-md: 3rem;
--gutter-sm: 1rem;
--gutter-xm: 1rem;
--color-white: #fff;
--color-black: #000;
}
body {
background: var(--color-black);
font: 16px verdana;
color: var(--color-white);
}
.banner {
display: flex;
height: 100vh;
}
.button {
margin: auto;
cursor: pointer;
transition: all 0.2s ease-out;
}
.button-wrap-wrapper {
width: 192px;
height: 192px;
border: 1px dashed #fff;
margin: auto;
display: flex;
}
.button-wrap {
width: 128px;
height: 128px;
margin: auto;
/* background: orange; */
display: flex;
justify-content: center;
align-items: center;
border: 1px dashed #fff;
}
.button__like-text {
display: block;
color: var(--color-black);
background: var(--color-white);
width: var(--gutter-lg);
height: var(--gutter-lg);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
<section class="banner">
<div class="button-wrap-wrapper">
<div class="button-wrap">
<div class="button">
<span class="button__like-text">
Like
</span>
</div>
</div>
</div>
</section>
What is not working as expected is: mouseIsInButtonTerritory becomes true and I am trying to increase the borderline here
if (mouseHasEntered) {
// this must happen only once to create outside borderline
//creating another level borderline by incresing distance;
// while cursor is returing the button comes out of nearest border-line and return from this borderline
distance = distance + distance;
}
the button keep following the cursor all the time.
What I am trying to solve is if the cursor is coming out of the both borderlines the button must cross the first borderline and come near to the last borderline and go back where it was in initial phase.
I am not getting where I am doing wrong. is there a anything that is missing ?
you should reset the distance when mouse leave. (I'm not sure, just guess this is what you want, since you write mouseHasEntered = true when reset)
since you handle mouse leave yourself (the else mouseIsInButtonTerritory part) do not listen to window.
const button = document.querySelector(".button");
let { width, height, x: buttonX, y: buttonY } = button.getBoundingClientRect(); // gives you width, height, left-X,top-y of the button
buttonX = buttonX + width / 2; // center point of button on x-axis
buttonY = buttonY + height / 2; // center point of button on y-axis
/*************** Functions ***************/
let distance = width;
let mouseHasEntered = true;
let mouseIsInButtonTerritory;
function mouseMove(e) {
const x = e.x; // current x of cursor
const y = e.y; // current y of cursor
const leftBorderLine = buttonX - distance;
const rightBorderLine = buttonX + distance;
const topBorderLine = buttonY - distance;
const bottomBorderline = buttonY + distance;
const xWalk = (x - buttonX) / 2; // the distance to move the button when mouse moves on X axis
const yWalk = (y - buttonY) / 2; // the distance to move the button when mouse moves on Y axis
mouseIsInButtonTerritory =
x > leftBorderLine &&
x < rightBorderLine &&
y > topBorderLine &&
y < bottomBorderline; // becomes true if mouse is inside all of these border-line
if (mouseIsInButtonTerritory) {
if (mouseHasEntered) {
// this must happen only once to create outside borderline
//creating another level borderline by incresing distance;
// while cursor is returing the button comes out of nearest border-line and return from this borderline
distance = distance + distance;
mouseHasEntered = false;
}
catchCursor(xWalk, yWalk); // call the function when mouse in in the button's territory
} else {
resetPositon();
}
}
function catchCursor(xWalk, yWalk) {
// translates the button in the direction where cursor is.
button.style.transform = `translate(${xWalk}px, ${yWalk}px)`;
}
function resetPositon() {
// resets the postion of the button as it was initial.
button.style.transform = `translate(${0}px, ${0}px)`;
if(!mouseHasEntered)distance/=2;
mouseHasEntered = true;
// when button is return to it's position (mouseHasEntered = true) lets to increase the initial borderline of button for the next time
}
/*************** Event-handler ***************/
window.addEventListener("mousemove", mouseMove);
//window.addEventListener("mouseout", resetPositon);
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--gutter-lg: 4rem;
--gutter-md: 3rem;
--gutter-sm: 1rem;
--gutter-xm: 1rem;
--color-white: #fff;
--color-black: #000;
}
body {
background: var(--color-black);
font: 16px verdana;
color: var(--color-white);
}
.banner {
display: flex;
height: 100vh;
}
.button {
margin: auto;
cursor: pointer;
transition: all 0.2s ease-out;
}
.button-wrap-wrapper {
width: 192px;
height: 192px;
border: 1px dashed #fff;
margin: auto;
display: flex;
}
.button-wrap {
width: 128px;
height: 128px;
margin: auto;
/* background: orange; */
display: flex;
justify-content: center;
align-items: center;
border: 1px dashed #fff;
}
.button__like-text {
display: block;
color: var(--color-black);
background: var(--color-white);
width: var(--gutter-lg);
height: var(--gutter-lg);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
<section class="banner">
<div class="button-wrap-wrapper">
<div class="button-wrap">
<div class="button">
<span class="button__like-text">
Like
</span>
</div>
</div>
</div>
</section>
If I understood your intention correctly, I think you need to:
add buttonWrapWrapper selector
const button = document.querySelector(".button");
const buttonWrapWrapper = document.querySelector(".button-wrap-wrapper");
attach the event listeners to buttonWrapWrapper instead of window:
buttonWrapWrapper.addEventListener("mousemove", mouseMove);
buttonWrapWrapper.addEventListener("mouseout", resetPositon);
I wanted a vertical dragBar for resizing two divs. I have created an example for the same but I am facing an issue.
Actual : As and when I resize the the upper div and move the slider down, the area of parent div increases and hence a scroll bar is given.
Expected: When Resizing, if the slider is moved down, it should only show the data contained in the upper div and when slider is moved up, it should show the content of lower div and should not increase the over all length of the parent div.
var handler = document.querySelector('.handler');
var wrapper = handler.closest('.wrapper');
var boxA = wrapper.querySelector('.box1');
var boxB = wrapper.querySelector('.box2');
var isHandlerDragging = false;
document.addEventListener('mousedown', function(e) {
// If mousedown event is fired from .handler, toggle flag to true
if (e.target === handler) {
isHandlerDragging = true;
}
});
document.addEventListener('mousemove', function(e) {
// Don't do anything if dragging flag is false
if (!isHandlerDragging) {
return false;
}
// Get offset
var containerOffsetTop= wrapper.offsetTop;
var containerOffsetBottom= wrapper.offsetBottom;
// Get x-coordinate of pointer relative to container
var pointerRelativeXpos = e.clientY - containerOffsetTop;
var pointerRelativeXpos2 = e.clientY - e.offsetTop + e.offsetHeight;
var boxAminWidth = 30;
boxA.style.height = (Math.max(boxAminWidth, pointerRelativeXpos - 2)) + 'px';
boxA.style.flexGrow = 0;
boxB.style.height = (Math.max(boxAminWidth, pointerRelativeXpos2 - 8)) + 'px';
boxB.style.flexGrow = 0;
});
document.addEventListener('mouseup', function(e) {
// Turn off dragging flag when user mouse is up
isHandlerDragging = false;
});
body {
margin: 40px;
}
.wrapper {
background-color: #fff;
color: #444;
/* Use flexbox */
}
.box1, .box2 {
background-color: #444;
color: #fff;
border-radius: 5px;
padding: 20px;
font-size: 150%;
margin-top:2%;
/* Use box-sizing so that element's outerwidth will match width property */
box-sizing: border-box;
/* Allow box to grow and shrink, and ensure they are all equally sized */
}
.handler {
width: 20px;
height:7px;
padding: 0;
cursor: ns-resize;
}
.handler::before {
content: '';
display: block;
width: 100px;
height: 100%;
background: red;
margin: 0 auto;
}
<div class="wrapper">
<div class="box1">A</div>
<div class="handler"></div>
<div class="box2">B</div>
</div>
Hope I was clear in explaining the issue I am facing in my project. Any help is appreciated.
It looks like your on the right track. You just need to make the wrapper a flexbox with the flex direction column and assign it a height. Also box 2 needs to have a flex of 1 so it can grow and shrink as needed. Finally I needed to remove the code that set the flex grow to 0 in the JavaScript. Here is the result.
var handler = document.querySelector('.handler');
var wrapper = handler.closest('.wrapper');
var boxA = wrapper.querySelector('.box1');
var boxB = wrapper.querySelector('.box2');
var isHandlerDragging = false;
document.addEventListener('mousedown', function(e) {
// If mousedown event is fired from .handler, toggle flag to true
if (e.target === handler) {
isHandlerDragging = true;
}
});
document.addEventListener('mousemove', function(e) {
// Don't do anything if dragging flag is false
if (!isHandlerDragging) {
return false;
}
e.preventDefault();
// Get offset
var containerOffsetTop= wrapper.offsetTop;
var containerOffsetBottom= wrapper.offsetBottom;
// Get x-coordinate of pointer relative to container
var pointerRelativeXpos = e.clientY - containerOffsetTop;
var pointerRelativeXpos2 = e.clientY - e.offsetTop + e.offsetHeight;
var boxAminWidth = 30;
boxA.style.height = (Math.max(boxAminWidth, pointerRelativeXpos - 2)) + 'px';
boxB.style.height = (Math.max(boxAminWidth, pointerRelativeXpos2 - 8)) + 'px';
});
document.addEventListener('mouseup', function(e) {
// Turn off dragging flag when user mouse is up
isHandlerDragging = false;
});
body {
margin: 40px;
}
.wrapper {
background-color: #fff;
color: #444;
/* Use flexbox */
display: flex;
flex-direction: column;
height: 200px;
}
.box1, .box2 {
background-color: #444;
color: #fff;
border-radius: 5px;
padding: 20px;
font-size: 150%;
margin-top:2%;
/* Use box-sizing so that element's outerwidth will match width property */
box-sizing: border-box;
/* Allow box to grow and shrink, and ensure they are all equally sized */
}
.box2 {
flex: 1;
}
.handler {
width: 20px;
height:7px;
padding: 0;
cursor: ns-resize;
}
.handler::before {
content: '';
display: block;
width: 100px;
height: 100%;
background: red;
margin: 0 auto;
}
<div class="wrapper">
<div class="box1">A</div>
<div class="handler"></div>
<div class="box2">B</div>
</div>
I'm creating a website that utilizes the HTML5 Drag and Drop API.
However, to increase the user experience, I'd like to prevent ghost images when a user drags non-draggable elements. Is this even possible?
Further, almost every element seems " draggable " by default. One can click and then quickly drag pretty much any element in a browser, which creates a ghost image along with a no-drop cursor. Is there any way to prevent this behaviour?
draggable="false" doesn't work.
user-select: none doesn't work.
pointer-events: none doesn't work.
event.preventDefault() on mousedown doesn't work.
event.preventDefault() on dragstart doesn't work either.
I'm out of ideas and so far it's proven incredibly difficult to find information about this online. I have found the following thread, but, again, draggable="false" doesn't seem to work in my case.
Below is a screenshot that demonstrates it doesn't work; of course you can't see my cursor in the screenshot, but you can see how I've dragged the numbers to the left despite that.
I believe the issue might have something to do with its parent having dragover and drop events associated with it. I'm still dumbfounded nonetheless.
HTML
...
<body>
...
<div id="backgammon-board-container">
<div class="column" id="left-column">
<div class="quadrant" id="third-quadrant">
<div class="point odd top-point" id="point-13-12"><text>13</text>
<div class="checker player-one-checker" id="checker-03" draggable="true"></div>
</div>
</div>
</div>
...
</div>
</body>
</html>
CSS
#backgammon-board-container {
height: 100vh;
width: 60vw;
position: absolute;
right: 0;
display: flex;
}
.column {
height: 100%;
display: flex;
flex-direction: column; /* column-reverse for player two perspective */
}
#left-column {
flex: 6;
}
.quadrant {
flex: 1;
display: flex;
}
.point {
flex: 1;
padding: 10px 0;
display: flex;
flex-direction: column;
align-items: center;
}
.checker {
z-index: 1;
width: 48px;
height: 48px;
border-radius: 50%;
}
text {
position: fixed;
font-family: impact;
font-size: 24px;
color: white;
display: flex;
justify-content: center;
align-items: center;
user-select: none;
pointer-events: none;
}
JS
const p1checkers = document.getElementsByClassName('player-one-checker');
const p2checkers = document.getElementsByClassName('player-two-checker');
const pointClass = document.getElementsByClassName('point');
function setTurn(player) {
if (player === 'p1') {
allowCheckerMovement = p1checkers;
disallowCheckerMovement = p2checkers;
} else {
allowCheckerMovement = p2checkers;
disallowCheckerMovement = p1checkers;
}
// enable checker control for player
for (var i = 0; i < allowCheckerMovement.length; i++) {
allowCheckerMovement[i].style.cursor = 'pointer';
allowCheckerMovement[i].setAttribute('draggable', true);
allowCheckerMovement[i].addEventListener('dragstart', start); // for drag-and-drop.js
allowCheckerMovement[i].addEventListener('dragend', stop); // for drag-and-drop.js
}
// disable checker control for player
for (var i = 0; i < disallowCheckerMovement.length; i++) {
disallowCheckerMovement[i].style.cursor = 'default';
disallowCheckerMovement[i].setAttribute('draggable', false);
disallowCheckerMovement[i].removeEventListener('dragstart', start); // for drag-and-drop.js
disallowCheckerMovement[i].removeEventListener('dragend', stop); // for drag-and-drop.js
}
// allow drag and drop
for (var i = 0; i < pointClass.length; i++) {
pointClass[i].addEventListener('dragover', allowDrop); // for drag-and-drop.js
pointClass[i].addEventListener('drop', droppedOn); // for drag-and-drop.js
}
}
function start(event) {
var checker = event.target;
event.dataTransfer.setData('text/plain', checker.id);
event.dataTransfer.effectAllowed = 'move';
window.requestAnimationFrame(function(){
checker.style.visibility = 'hidden';
});
}
function allowDrop(event) {
event.preventDefault();
}
function droppedOn(event) {
event.preventDefault();
var data = event.dataTransfer.getData('text/plain');
event.target.appendChild(document.getElementById(data));
}
function stop(event){
var element = event.srcElement;
window.requestAnimationFrame(function(){
element.style.visibility = 'visible';
});
}
This is the solution you're looking for. ;)
For anything that DOES need to be draggable, just add the 'enable-drag' CSS class.
$('*:not(".enable-drag")').on('dragstart', function (e) {
e.preventDefault();
return false;
});