z-index not working on Safari when manipulating it using Javascript - javascript

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 :)

Related

Marquee Slider lacks after few iterations

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).

Replacing HTML contents with AJAX

I'm relatively new to Javascript, so I've pieced together the code I have by looking through the forums on here. However, I cannot get this to work, and I am needing help.
The desired end result I am trying to achieve is that whenever a user calls the moreInfo(ID) function, a modal pops up on the screen with the contents of the modal being populated from an external file that is built using PHP.
Right now, whenever I call the function, the modal pops up but does not display the external file. Instead, the modal displays the current page (??). A live version can be found here: http://classcolonies.com/app/test.php/
What am I doing wrong? How do I need to go about doing this instead? An explanation along with a solution would be ideal so I can learn and grow in my journey to understand javascript.
Launch Page (used to launch the modal)
<h1>Test Screen</h1><button onclick='moreInfo("12");'>Test</button>
<div id="infoModal" class="modal">
<div class="modal-window">
<span id="moreInfo"></span>
</div>
</div>
<script> /* AJAX name selector */
var infoModal = document.getElementById("infoModal");
function moreInfo(str){
if (window.XMLHttpRequest) {xmlhttp=new XMLHttpRequest();}
xmlhttp.onreadystatechange=function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
document.getElementById("moreInfo").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET","moreinfo.tem.php?assID=" + str, true);
xmlhttp.send();
infoModal.style.display = "block"; /* After fetching request, make modal appear */
}
window.onclick = function(event) { /* Make modal disappear when you click "X" */
if (event.target == infoModal) {infoModal.style.display = "none";}
}
</script>
<style>
.modal {
display: none;
position: fixed;
z-index: 20;
right: 0; top: 0;
width: 100%; height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.4);
-webkit-animation-name: fadeIn;
-webkit-animation-duration: 0.4s;
animation-name: fadeIn;
animation-duration: 0.4s}
/* Customized part listed below */
.modal-window{
display: grid;
position: fixed;
padding: 10px;
width: 600px; height: 350px;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
transition: height 0.5s;
grid-template-rows: 90px 1fr 60px;
grid-template-areas:
"top"
"content"
"controls";}
/* --------[TOP] -------- */
.modal-top {
display: grid;
grid-area: top;
border-bottom: 2px solid #5B7042;
grid-template-columns: 100px 1fr 80px;}
.pic{
display: inline-block;
width: 65px;
clip-path: circle();
margin-left: 10px;}
.modal-top .title {
display: flex;
align-items: center;
font-weight: 800;
font-size: 26px}
.due {
display: flex;
align-items: center;
font-size: 18px;
color: gray;}
/* --------[CONTENT] -------- */
.modal-content {
display: block;
grid-area: content;
overflow-y: scroll;
padding: 12px;}
.directions {
font-size: 18px;
line-height: 1.7}
textarea {
display: none;
width: 100%; height: 100px;
box-sizing: border-box;
font-size: 18px !important;
margin-top: 20px;}
/* --------[CONTROLS] -------- */
.modal-controls {
display: flex;
align-items: center;
grid-area: controls}
#askforhelp {margin-right: 10px;}
#sendmsg {display: none; margin-right: 10px}
#cancelmsg {display: none}
</style>
External file, used to replace the <span id="moreInfo"> with actual content
<div class='modal-top'>
<img class='pic' src='../resources/pics/1.png'>
<span class='title'> Reading Homework </span>
<span class='due'> Due 3d </span>
</div>
<div class="modal-content">
<div class='directions'>
<b>Directions:</b> You must complete the assignment to continue to the next section. Please type complete sentences and capitalization. Let me know if you need help.
</div>
<textarea placeholder='Type Question..'></textarea>
</div>
<div class="modal-controls">
<button id='askforhelp' class='button green-btn' onclick='askHelp("showform")'>Ask for Help</button>
<button id='markdone' class='button green-btn'>Mark as Done</button>
<button id='sendmsg' class='button green-btn'>Send Message</button>
<button id='cancelmsg' class='button grey-btn' onclick='askHelp("hideform")'>Cancel Message</button>
</div>
<script>
function askHelp(arg) {
var window = document.getElementsByClassName('modal-window')[0];
var textbox = document.getElementsByTagName("textarea")[0];
var helpBtn = document.getElementById('askforhelp');
var doneBtn = document.getElementById('markdone');
var sendBtn = document.getElementById('sendmsg');
var cancelBtn = document.getElementById('cancelmsg');
if (arg == "showform") {
window.style.height = '400px';
textbox.style.display = 'block';
helpBtn.style.display = 'none';
doneBtn.style.display = 'none';
sendBtn.style.display = 'block';
cancelBtn.style.display = 'block';
}
if (arg == "hideform") {
window.style.height = '350px';
textbox.style.display = 'none';
helpBtn.style.display = 'block';
doneBtn.style.display = 'block';
sendBtn.style.display = 'none';
cancelBtn.style.display = 'none';
}
}
</script>
Based on the answer by #Gil, Update your moreInfo function as below:
function moreInfo(str){
fetch("moreinfo.tem.php?assID=" + str).then((res) => res.text()).then(response=>{
document.getElementById("moreInfo").innerHTML=response;
infoModal.style.display = "block";
});
}
fetch returns a promise. From that promise, return the evaluated text from the response. This yields another promise which would contain the html or whatever.
It would be worth mentioning that the script in the returned html won't execute, so your askHelp function won't be defined. You can parse the html response and inject any script contents into the page as follow:
function moreInfo(str){
infoModal.style.display = "block";
fetch("moreinfo.tem.php?assID=" + str).then((response) =>response.text()).then((text) => {
var parser = new DOMParser();
var doc = parser.parseFromString(text, "text/html");
var ele = doc.documentElement;
var scripts = ele.getElementsByTagName('script');
for(var script of scripts){
var head = document.getElementsByTagName('head')[0];
var scriptElement = document.createElement('script');
scriptElement.setAttribute('type', 'text/javascript');
scriptElement.innerText = script.innerText;
head.appendChild(scriptElement);
head.removeChild(scriptElement);
}
document.getElementById("moreInfo").innerHTML=text;
});
}
fetch('xxx/com/api')
.then(responese=>responese.json())
.then(data=>{ do something..}
remember to add json() within first .then
Try using fetch instead.
Something like:
function moreInfo(str){
fetch("moreinfo.tem.php?assID=" + str).then((response) => {
response.text().then((text) => {
document.getElementById("moreInfo").innerHTML=text;
infoModal.style.display = "block";
});
})
}
Some explanation about the syntax here:
fetch makes an HTTP request to the URL provided (default is GET request, unless specified otherwise)
.then means, do something after the request is done.
(response) => {} is an arrow notation in JavaScript.
it's the same as writing function(response) {...}

Changing images with right or left arrow key

I've built this gallery
https://jsfiddle.net/ramamamagagaulala/do4yLxcz/
let images = document.querySelectorAll('.work-item');
let best = document.querySelector('.work-modal');
let main = document.querySelector('.work-modal__item');
console.log(images)
let closeButton = document.getElementById("closee");
images.forEach(function(ref) {
ref.addEventListener('click', function(){
let newImage = this.getElementsByTagName('img')[0].src;
best.classList.add('work-modal--show');
main.style.backgroundImage = `url( ${newImage} )`;
})
})
closeButton.addEventListener('click', function() {
best.classList.remove('work-modal--show');
});
basically, it works like this:
you click an item.
JavaScript checks what IMG this item contains.
a modal window opens up.
then the IMG that is associated with the item, is going to be displayed as the background image of this modal.
So far so good, however, I would like to build a function so I can press the arrow keys on my keyboard and the next image is going to be displayed.
What I've tried is to select the IMG of the nextSibling while clicking. Then I have used this variable to set up the background image of the modal window. But this only worked once.
Any ideas what to try next?
I would suggest have list of images urls in an array in .js file, and then you show one modal, click right/left and just change img src value to next/previous array element, untill get to either end of array.
There are three things we need to do for this problem
Storing the image source in an array
Keep track of the position of the image index
Add an event listener to track the keypress for next & prev button on your keyboard
let images = document.querySelectorAll('.work-item');
let best = document.querySelector('.work-modal');
let main = document.querySelector('.work-modal__item');
let closeButton = document.getElementById("closee");
let currentIndex = -1;
let imgSrc = [];
images.forEach(function(ref,index) {
imgSrc.push(ref.children[0].getAttribute("src"));
ref.addEventListener('click', function(){
let newImage = this.getElementsByTagName('img')[0].src;
best.classList.add('work-modal--show');
main.style.backgroundImage = `url( ${newImage} )`;
currentIndex = index
});
})
closeButton.addEventListener('click', function() {
best.classList.remove('work-modal--show');
});
let doc = document.getElementById("work");
window.addEventListener("keydown", event => {
if(event.keyCode === 39){
// next event
if(currentIndex < imgSrc.length -1 ){
main.style.backgroundImage = `url( ${imgSrc[currentIndex+1]} )`;
currentIndex=currentIndex+1;
} else {
alert("Reached last image")
}
} else if(event.keyCode === 37){
// prev event
if(currentIndex > 0){
main.style.backgroundImage = `url( ${imgSrc[currentIndex-1]} )`;
currentIndex=currentIndex-1;
} else {
alert("Reached first image")
}
}
});
.work-container{
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-gap: 1rem;
}
img {
width: 250px;
}
.work-item__img{
width: 100%;
height: 100%;
display: block;
transition: all 1s linear;
opacity: 1;
object-fit: cover;
transform: scale(1.1);
}
/* modal */
.work-modal{
display: none;
}
.work-modal--show{
position: fixed;
background: rgba(0,0,0,0.5);
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 999;
display: grid;
justify-content: center;
align-items: center;
}
.work-modal__item{
height: 70vh;
width: 80vw;
border:0.5rem solid var(--yellow);
border-radius: 0.4rem;
}
#media screen and (min-width:768px){
.work-modal__item{
height: 80vh;
width: 60vw;
}
}
.work-modal__close{
position: fixed;
font-size: 3rem;
color: var(--brightYellow);
bottom: 5%;
right: 5%;
transition: color 0.5s linear;
cursor: pointer;
text-decoration: none;
display: inline-block;
}
.work-modal__close:hover{
color: red;
}
<section class="work section-padding" id="work">
<div class="work-container">
<div class="work-item item-1">
<img src="https://images.pexels.com/photos/2683138/pexels-photo-2683138.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" alt="" class="work-item__img">
</div>
<div class="work-item item-2">
<img src="https://images.pexels.com/photos/2736220/pexels-photo-2736220.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" alt="" class="work-item__img">
</div>
<div class="work-item item-3">
<img src="https://images.pexels.com/photos/2928178/pexels-photo-2928178.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500" alt="" class="work-item__img">
</div>
</div>
</section>
<div class="work-modal">
<div class="work-modal__item"></div>
<div class="work-modal__close">
<i id="closee" class="fas fa-window-close">close</i>
</div>
</div>
JS Fiddle
https://jsfiddle.net/aamin89/b5wp3kez/1/

Weird mobile nav animation bug

CodePen of the nav
On the first interaction with the mobile nav, it will open and close as expected but anything after that and there's a bug. It will begin to open and close instantly or the links will appear is weird orders.
What I need is for the mobile nav to first open from right to left, have each of the links to cascade into the view, starting from About all the way to Blog, and then I would like it to reverse when leaving the view.
Right now I don't have the logic implemented for the reverse but I need to work out this bug before I get to that.
SNIPPET
const bars = document.querySelector('.fa-bars');
bars.addEventListener('click', () => {
const navItemsContainer = document.querySelector('.navbar__links-container');
const navItems = document.querySelectorAll('.navbar__links-container__item');
const sleep = ms => {
return new Promise(resolve => {
setTimeout(() => {
return resolve();
}, ms);
});
};
const startNavAnimation = async () => {
let count = 0;
for (let item of navItems) {
if (item.classList.contains('navbar__links-container__item--show')) {
setTimeout(() => {
item.style.transitionDelay = `${ count }s`
item.classList.remove('navbar__links-container__item--show');
count += .15;
}, count);
}
else {
item.style.transitionDelay = `${ count }s`
item.classList.add('navbar__links-container__item--show');
count += .15;
}
}
};
if (navItemsContainer.classList.contains('navbar__links-container--open')) {
navItems[ navItems.length - 1 ].addEventListener('transitionend', () => {
navItemsContainer.classList.remove('navbar__links-container--open');
});
}
else {
navItemsContainer.classList.add('navbar__links-container--open');
}
startNavAnimation();
});
body {
margin: 0;
}
.navbar {
background: #f2f2f2;
display: flex;
flex-wrap: wrap;
}
.navbar__mobile-container {
display: flex;
justify-content: space-around;
width: 100%;
}
.fa-bars {
cursor: pointer;
}
.navbar__links-container {
background: inherit;
display: flex;
flex-direction: column;
align-items: flex-end;
list-style: none;
margin: 0;
overflow: hidden;
padding: 0;
position: absolute;
top: 20px;
left: 100%;
transition: left .25s, width .25s;
width: 0%;
}
.navbar__links-container__item {
left: 52px;
margin-bottom: 1rem;
position: relative;
transition: left .25s;
width: auto;
}
.navbar__links-container--open {
left: 0;
width: 100%;
}
.navbar__links-container__item--show {
left: -63px;
}
<nav class="navbar">
<div class="navbar__mobile-container">
<div class="navbar__logo-container">
<a class="navbar__logo-container__logo">BB</a>
</div>
<div class="navbar__hamburger-container">
<i class="fas fa-bars">MENU</i>
</div>
</div>
<ul class="navbar__links-container">
<li class="navbar__links-container__item">
<a class="navbar__links-container__item__link">About</a>
</li>
<li class="navbar__links-container__item">
<a class="navbar__links-container__item__link">Portfolio</a>
</li>
<li class="navbar__links-container__item">
<a class="navbar__links-container__item__link">Blog</a>
</li>
</ul>
</nav>
NOTES
I think the problem is the first if statement in the bars event-handler. Something about the way it's waiting for the transitionend event but the startNavAnimation hasn't been called.
There are two issues.
One is that you are adding a new event listener inside of the click event listener. I moved that outside.
The second issue is that the --open class is going to be there while the menu is opening or closing so you need another way to test open or closed status. To make the Codepen clear to understand, I just used an isOpen flag.
https://codepen.io/Jason_B/pen/jzGwQX?editors=0010
I like using classes for this, and your code shows that you do too, so you might want to have a class for open status and a class for visibility.

Prevent ghost image on dragging non-draggable elements?

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;
});

Categories

Resources