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;
});
Related
Let's imagine an example of some content that dynamically inserted upon data fetch from server. The purpose is to display it like a bunch of items in a scroll-snapped container.
The problem is browser somehow randomly tries to scroll to some element but I want the container to stay at the first element no matter how many elements are inserted. The only exception is user that manually scrolls the container.
It works fine unless I add scroll-snap property.
Here is the code snippet
const startingIdx = 3;
const batchSize = 10;
const delay = 500;
const root = document.querySelector('.pills');
const appendBatch = (from) => {
for(let i = 0; i < batchSize; i++){
const element = document.createElement('div');
element.classList.add('pill');
element.innerText = String(from + i);
root.appendChild(element);
}
}
for(let i = 0; i < 3; i++){
setTimeout(() => appendBatch(startingIdx + i*batchSize), i*delay);
}
.pills {
display: flex;
gap: 20px;
width: 100%;
overflow: auto;
scroll-snap-type: x mandatory;
}
.pill {
width: 120px;
height: 40px;
background: rgba(92, 138, 255, 0.15);
display: flex;
align-items: center;
justify-content: center;
border-radius: 20px;
flex-shrink: 0;
scroll-snap-align: start;
}
<main>
<div class="pills">
<div class="pill">1</div>
<div class="pill">2</div>
</div>
</main>
And the demo where you can reproduce it yourself: https://977940.playcode.io/
This is what I'm talking about
Just add that in your script. When page is loaded it scrolls to the left side.
window.addEventListener("DOMContentLoaded", () => {
root.scrollLeft = 0;
});
Seems like adding scroller.scrollBy(0,0); before every batch insertion would save the day as it forces browser to snap to existing element
Source: https://web.dev/snap-after-layout/#interoperability
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>
Hello I'm new to javascript, I'm using es6 to my code.
Basically, I have an issue with IE for addEventListener, the idea, when we click the image, pop up appears, it's working on chrome, but it doesn't work in IE. I know there's related topic already about this, ex: addEventListener in Internet Explorer
I tried to implement this, but it doesn't seem to work, I think I need to understand more how to implement it related to my code, if anyone could help, I'll really appreciate this.
const toggleButton = document.querySelector('.jsModalToggle');
const container = document.querySelector('.modal-yt-container');
toggleButton.addEventListener('click', _ => {
document.body.classList.add('modal-yt-is-open')
})
container.addEventListener('click', e => {
if (!e.target.closest('.modal-yt-video')) {
document.body.classList.remove('modal-yt-is-open')
}
})
.installation-video-callout-text-container {
padding: 20px;
}
.installation-video-callout-text p{
font-size: 1em;
line-height: 16px;
}
.installation-video-callout-text .green_btn{
margin: 20px 0 0px 0;
}
.installation-video-callout-text h2{
line-height: 45px;
font-size: 32px;
}
.installation-video-callout-img iframe{
height: 300px;
}
.modal-yt-container {
position: fixed;
display: flex;
justify-content: center;
align-items: center;
top: 0;
right: 0;
bottom: 0;
left: 0;
opacity: 0;
z-index: -1;
background-color: rgba(0, 0, 0, 0.78);
}
.modal-yt-is-open .modal-yt-container {
z-index: 9999;
opacity: 1;
}
.modal-yt-video{
display: flex;
justify-content: center;
align-items: center;
width: 45%;
}
<img class="jsModalToggle installation-video-callout-img" src="image" style="cursor:pointer">image click</img>
<div class="modal-yt-container installation-video-callout-img">
<div class="modal-yt-video">
<iframe type="text/html"
width="100%"
height="500px"
src="https://www.youtube.com/embed/TA6blZJ6nVw"
frameborder="0">
</iframe>
</div>
</div>
No version of Internet Explorer supports arrow functions. The same is true for all ES6 Javascript features except const and let.
So this will not work in any IE:
toggleButton.addEventListener('click', _ => {
document.body.classList.add('modal-yt-is-open')
})
container.addEventListener('click', e => {
if (!e.target.closest('.modal-yt-video')) {
document.body.classList.remove('modal-yt-is-open')
}
})
Instead, use ES5 functions:
toggleButton.addEventListener('click', function(_) {
document.body.classList.add('modal-yt-is-open')
})
container.addEventListener('click', function(e) {
if (!e.target.closest('.modal-yt-video')) {
document.body.classList.remove('modal-yt-is-open')
}
})
The next problem is that no version of Internet Explorer supports HTMLElement.prototype.closest() either. If you want to use it, you need to polyfill it:
if (!Element.prototype.matches) {
Element.prototype.matches = Element.prototype.msMatchesSelector ||
Element.prototype.webkitMatchesSelector;
}
if (!Element.prototype.closest) {
Element.prototype.closest = function(s) {
var el = this;
do {
if (Element.prototype.matches.call(el, s)) return el;
el = el.parentElement || el.parentNode;
} while (el !== null && el.nodeType === 1);
return null;
};
}
It asks you 3 things event, element, function
var event = "click"
var element = document.querySelector('.jsModalToggle');
var myFunc = function(e){
if (!e.target.closest('.modal-yt-video')) {
document.body.classList.remove('modal-yt-is-open')
}
}
then you pass everything to the function
addEvent(event, element, myFunc);
I am making a website that has multiple layers to it, which is brought back and forth by manipulating the z-index in Javascript through event reactions (like onclick). I have a navigation bar which has a large z-index value compared to every other element as I would like it to be at the very front regardless of anything. However, when run on Safari, the nav bar disappears from the get go, while it works fine on Google Chrome and FireFox.
I have included the css code and javascript code that dictates this role:
JAVASCRIPT:
//Global variables representing DOM elements
var introTitleElem = document.getElementById('introduction-title');
var resumeElem = document.getElementById('resume-container');
var introElem = document.getElementById('intro-content');
var eduElem = document.getElementById('edu-content');
//Layer tracker (for transition effect)
var prev = introElem;
var prevButton = "";
//Function that actually changes the layers
function changeLayer(layer, button) {
if (layer === prev) return;
introTitleElem.style.opacity = "0";
prev.style.zIndex = "40";
layer.style.zIndex = "50";
layer.style.cssText = "opacity: 1; transition: opacity 0.5s";
prev.style.zIndex = "5";
prev.style.opacity = "0";
prev = layer;
if (prevButton !== "") prevButton.style.textDecoration = "none";
button.style.textDecoration = "underline";
prevButton = button;
}
//Manages events triggered by name button toggle
function revealResume() {
introTitleElem.style.zIndex = "0";
resumeElem.style.zIndex = "10";
resumeElem.style.opacity = "1";
introElem.style.opacity = "1";
resumeElem.style.transition = "opacity 0.7s";
introElem.style.transition = "opacity 0.7s";
}
document.getElementById("name-title").addEventListener("click", revealResume);
//Manage z-index of different components of the resume and reveal them accordingly
$('#nav-education').click(function () {
onEducation = true;
changeLayer(eduElem, this);
});
CSS (SASS):
/*NAVIGATION STYLING*/
#fixed-nav {
align-self: flex-start;
overflow-x: hidden;
float: right;
z-index: 9999 !important;
display: flex;
margin: 1em;
li {
margin: 0.6em;
font: {
family: $font-plex-sans-condensed;
size: 0.8em;
}
text-align: center;
color: $lightest-grey;
transition: color 0.3s;
&:hover {
cursor: pointer;
color: $dark-grey;
transition: color 0.3s;
}
}
}
/*OVERALL DIV FORMATTING*/
.format-div {
width: 100vw;
height: 100vh;
display: flex;
position: absolute;
justify-content: center;
align-items: center;
flex-direction: column;
opacity: 0;
}
/*EDUCATION CONTENT STYLING*/
#edu-content {
background-color: $red-1;
}
HTML:
<div id="resume-container">
<ul id="fixed-nav">
<li id="nav-education">education.</li>
<li id="nav-experiences">experiences.</li>
<li id="nav-skills">skills.</li>
<li id="nav-projects">projects.</li>
<li id="nav-additional">additional.</li>
<li id="nav-contact">contact.</li>
</ul>
<div id="intro-content" class="format-div">
<h1 class="type-effect">
<h1>I'm Daniel <b>(Sang Don)</b> Joo</h1>
<span class="blinking-cursor">|</span>
</h1>
</div>
<div id="edu-content" class="format-div">
<h1>education</h1>
</div>
Sorry about the large amount of code but I'm really unsure of where this problem is rooted. Cheers!
it has to be the position feature of the elements so that it can work
wrong example ( not working )
.className { z-index: 99999 !important; }
correct example ( it work )
.className { position: 'relative'; z-index: 99999 !important; }
.className { position: 'absolute'; z-index: 99999 !important; }
etc..
good luck :)
I need to display the list and focus first list item in the list when user selects key 'm' on the keyboard. I tried calling focus() on the list item but it is not getting focus or it is not showing any effect. Below is the code.
HTML:
<!DOCTYPE html>
<html>
<head>
<title>VOD</title>
<script src='js/index.js'>
</script>
<style>
html, body {
height:100%
}
#mid {
display: flex;
height: 100%;
width: 100%;
justify-content: stretch;
flex-flow: row nowrap;
z-index: 2;
}
#mid.hidden {
display: none;
}
#mid1, #mid2 {
display: flex;
flex: 1;
align-items: center;
}
#mid1 {
justify-content: center;
background-color: rgba(255, 255, 255, 0.5);
}
#mid2 {
background-color: rgba(255, 255, 255, 0.6);
}
#ulid {
list-style-type: none;
width: 100%;
margin: 0;
border: 0;
padding: 0;
}
.list-item {
width: 100%;
height: 150px;
border-style: solid;
border-width: 1px;
display:flex;
align-items: center;
justify-content: flex-start;
}
li:focus, li:active {
background-color: green;
}
</style>
</head>
<body>
<video id='vid' src='textMotion.mp4' autoplay loop></video>
<div id='mid' class='hidden'>
<div id='mid1'>
<h1>TEXT</h1>
</div>
<div id='mid2'>
<ul id='ulid'></ul>
</div>
</div>
</body>
</html>
JavaScript:
function displayMenu() {
var mid = document.getElementById('mid');
if(mid.classList.contains('hidden') == false) {
mid.classList.toggle("hidden");
let ulid = document.getElementById('ulid');
while(ulid.firstChild) {
ulid.removeChild(ulid.firstChild);
}
return;
}
var ulid = document.getElementById('ulid');
for(let index = 0; index < 3; index ++) {
let lItem = document.createElement('li');
lItem.classList.add('list-item');
lItem.setAttribute('id', 'li' + index);
var lineBreak = document.createElement('br');
lItem.appendChild(document.createTextNode('TEXT'));
lItem.appendChild(lineBreak);
lItem.appendChild(document.createTextNode('TEXT'));
ulid.appendChild(lItem);
}
mid.classList.toggle("hidden");
document.getElementById('li0').focus();
}
function changeChannel(e) {
console.log('received keyEvent : ' + e.keyCode);
let keyCode = e.keyCode;
if(keyCode == 77) {
displayMenu();
}
}
document.addEventListener('keydown', changeChannel);
Even though list is getting displayed when user presses 'm', list item is not getting focused.
below is the jsfiddle link
https://jsfiddle.net/t75gojd7/
Can anyone please help me to focus the list element.
It looks like adding a tabindex might work. Add this to the attributes.
lItem.setAttribute('tabindex', index);
Got the idea from here. Worked for some people not all.
What is happening here is that the element you are trying to focus is a div which doesn't actually remain in focus after click unlike a button or a input text field.
You can observe this behavior by pressing down the pointer on the div and not releasing it. Your CSS stylings will be applied.
I would suggest instead on :focus CSS class, you use some other pseudo CSS class. OR you can do event.preventDefault() on div click.
Quoting from MDN
If you call HTMLElement.focus() from a mousedown event handler, you must call event.preventDefault() to keep the focus from leaving the HTMLElement.
Also check out this ans Is it possible to focus on a <div> using JavaScript focus() function? which suggests adding tabindex attribute.