CSS: Invisible element with transition: all flashes on page load [duplicate] - html

This question already has answers here:
Stop CSS transition from firing on page load
(9 answers)
Closed 3 months ago.
I have a popup element which is hidden by default and only shows up programmatically when the script assigns a specific class to its container and populates the popup text.
In css/stylesheet.css:
.error-message {
opacity: 0;
visibility: hidden;
transition: all 0.2s ease;
}
.container.with-error .error-message {
opacity: 1;
visibility: visible;
}
In index.html:
<link rel="stylesheet" href="css/stylesheet.css">
<div class="container">
<div class="error-message">This text will be changed by a script.</div>
</div>
According to this simple style declaration, the .error-message element should always be invisible, unless it is preceded by a .container.with-error, in which case it becomes visible, and its appearance is always animated because of transition property.
However, the .error-message triggers its transition when the page is loaded, resulting in a flash which I believe it should not do.
Related behavior I have observed:
The flash does not appear if the style is declared in an inline <style> tag
The flash appears if every style but transition: all is declared in an inline <style> tag
The flash does not appear if the style is loaded from a Base-64 encoded Data URL like this: <link href="data:text/css;base64,...">
The flash does not appear if the style loaded from <link rel="stylesheet"> is retrieved from cache.
I've created a demo that reproduces this bug every time. To simulate requesting a remote stylesheet without cache, a blob:// Object URL is generated from the style instead. The inline demo is available at the end of this question, but for best results, use JSBin. Use F5 to see the bug in action.
I'm curious how to fix this and what causes this issue as this is clearly not intended behavior.
<!doctype html>
<html lang="en">
<head>
<script>
/* jshint browser: true, esversion: 6 */
window.onload = function() {
// Log all transition events
window.ontransitioncancel = appendToTransitionLog;
window.ontransitionstart = appendToTransitionLog;
window.ontransitionrun = appendToTransitionLog;
window.ontransitionend = appendToTransitionLog;
// Simulates loading a stylesheet from a remote location
// Works the same way as if #simulated-stylesheet's content
// was hosted and served from <link rel=stylesheet> without cache
//
// Keep in mind that this bug does not appear
// if the style is injected or loaded from cache!
createFakeStylesheet();
};
function createFakeStylesheet() {
var styleContent = document.getElementById("simulated-stylesheet").text;
var styleBlob = new Blob([styleContent], {type: "text/css"});
var styleURL = URL.createObjectURL(styleBlob);
var linkElement = document.createElement("link");
linkElement.rel = "stylesheet";
linkElement.href = styleURL;
document.head.appendChild(linkElement);
}
// Functions below handle transition events logging
// Template import helper
function importTemplateFromId(id) {
return document.importNode(document.getElementById(id).content, true);
}
// Returns a string like "div.class1.class2" to describe an element
function describeElement(element) {
var tagName = element.tagName.toLowerCase();
var classes = element.classList.toString().split(" ").filter(className => className != "").map(className => "." + className).join("");
return tagName + classes;
}
// Returns a matching log group wrapper.
// The wrapper is created if the group does not exists.
// Used for grouping transition events by element descriptor
function getLogWrapper(logContainer, elementText) {
var matchingWrapper = logContainer.querySelector(".wrapper[data-for-element=\"" + elementText + "\"] .logs");
if (matchingWrapper) {
return matchingWrapper;
}
var wrapperTemplate = importTemplateFromId("wrapper-template");
var wrapperName = wrapperTemplate.querySelector(".name");
var wrapperElement = wrapperTemplate.querySelector(".wrapper");
wrapperName.textContent = elementText;
wrapperElement.dataset.forElement = elementText;
return logContainer.appendChild(wrapperElement).querySelector(".logs");
}
// Logs a transition event.
// Logs are grouped by each event type (start, run, end)
// and target element's descriptor (see describeElement)
function appendToTransitionLog(transitionEvent) {
var eventType = transitionEvent.type;
var eventProperty = transitionEvent.propertyName;
var logContainer = document.getElementById("log-" + eventType);
var elementText = describeElement(transitionEvent.target);
var logWrapper = getLogWrapper(logContainer, elementText);
var logEntry = document.createElement("span");
logEntry.textContent = eventProperty;
logEntry.className = "entry";
logWrapper.appendChild(logEntry);
}
</script>
<style>
#edit-with-js-bin {
display: none!important;
}
.log {
font-size: 14px;
}
.log .wrapper {
padding-left: 16px;
}
.wrapper .name {
text-decoration: underline;
}
.wrapper .logs {
padding-left: 12px;
}
.wrapper .entry {
display: inline-block;
color: grey;
padding: 8px 4px;
}
.wrapper .entry:nth-child(2n) {
color: lightgrey;
}
body {
font-family: monospace;
font-size: 0;
}
.side {
display: inline-block;
font-size: 14px;
vertical-align: top;
width: 50%;
height: 100%;
}
</style>
<template id="wrapper-template">
<div class="wrapper" data-for>
<span class="name"></span>
<div class="logs"></div>
</div>
</template>
<script id="simulated-stylesheet" type="text/css">
.remote {
background: crimson;
color: white;
display: inline-block;
margin: 8px;
padding: 8px;
}
.remote.transparent {
opacity: 0;
visibility: hidden;
}
.remote.transition-some {
transition: opacity, visibility 1s ease;
}
.remote.transition-all {
transition: all 1s ease;
}
</script>
<style>
.transition-all-inline {
transition: all 1s ease;
}
.local {
background: green;
color: white;
display: inline-block;
margin: 8px;
padding: 8px;
}
.local.transparent {
opacity: 0;
visibility: hidden;
}
.local.transition-some {
transition: opacity, visibility 1s ease;
}
.local.transition-all {
transition: all 1s ease;
}
.mock {
background: orangered;
transition: all 1s ease;
}
.mock:hover {
background: orange;
}
</style>
</head>
<body>
<div class="side left">
<div>
<u>.remote</u> <div class="remote">I'm always styled.</div>
</div>
<div>
.remote<u>.transparent</u> <div class="remote transparent">I'm always transparent.</div>
</div>
<div>
.remote.transparent<u>.transition-some</u> <div class="remote transparent transition-some">I'm invisible!</div>
</div>
<div>
.remote.transparent<u>.transition-all</u> <div class="remote transparent transition-all">I will briefly flash when the page loads.</div>
</div>
<div>
.remote.transparent<u>.transition-all-inline</u> <div class="remote transparent transition-all-inline">I will briefly flash when the page loads.</div>
</div>
</div>
<div class="side right">
<div>
<u>.local</u> <div class="local">I'm always styled.</div>
</div>
<div>
.local<u>.transparent</u> <div class="local transparent">I'm always transparent.</div>
</div>
<div>
.local.transparent<u>.transition-some</u> <div class="local transparent transition-some">I'm invisible!</div>
</div>
<div>
.local.transparent<u>.transition-all</u> <div class="local transparent transition-all">I'm invisible!</div>
</div>
<div>
.local.transparent<u>.transition-all-inline</u> <div class="local transparent transition-all-inline">I'm invisible!</div>
</div>
<div>
.local.mock <div class="local mock">Use me to debug transition events!</div>
</div>
</div>
<div class="log">
<div>
<b>ontransitionstart</b>
<div id="log-transitionstart"></div>
</div>
<div>
<b>ontransitionrun</b>
<div id="log-transitionrun"></div>
</div>
<div>
<b>ontransitionend</b>
<div id="log-transitionend"></div>
</div>
<div>
<b>ontransitioncancel</b>
<div id="log-ontransitioncancel"></div>
</div>
</div>
</body>
</html>
EDIT: The flash appears regardless of what property is being transitioned.
This still creates the same effect:
.error-message {
transition: opacity 0.2s ease;
}

It's a normal behavior of transition (not a bug of any specific browser).
The issue in your case is that you already have elements (that flashes) in the DOM tree.
That also means the elements have an initial state and any new state performs a transition. If a new style applied and it has a transition property a browser will show you animation between the initial state and the new state with a transition property. (Note that the initial state of the element that flashes is not transparent).
The possible fix for that is to add hidden elements when new styles already exist. Or refactor your styles to have a transparent initial state.

Related

How can I create a blink transition effect from one colour to another in a timeout function?

I am creating an application that trains my memory by memorising colours. Every 2 seconds, the colour of the box will change from one to another. However if it switches to the same colour, it becomes difficult to differentiate. I am hoping to implement a blink effect when it transits to another colour. I tried to use blink animation by adjusting the time but it does not work well. How can i implement with my current code?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
*{
box-sizing: border-box;
padding: 0;
margin: 0;
font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
}
#count {
font-size: 36px;
}
.section__hero {
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 50px;
position: relative;
}
#countdownTimer {
position: absolute;
font-size: 72px;
left: 50%;
top: 50%;
transform:translate(-50%,-50%)
}
.section__btns {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px
}
#count,
#element,
#btn__answer,
#countdownTimer {
visibility: hidden;
}
#element {
height: 100px;
width: 100px;
background-color: #000;
border: 1px solid black;
}
#btn__action,
#btn__answer {
padding: 15px 30px;
border: none;
font-size: 18px;
color: #fff;
border-radius: 8px;
cursor: pointer;
/* display: block; */
}
#btn__action {
background-color: #332cf2;
}
#btn__answer {
background-color: #000;
text-decoration: none;
}
</style>
<title>Document</title>
</head>
<body>
<div class="section__hero">
<div id="countdownTimer"></div>
<div id="count"></div>
<div id="element"></div>
<div class="section__btns">
<button id="btn__action" onclick="action()">Start</button>
Answer
</div>
</div>
</body>
<script>
const colors = ["#000", "#fff", "#ffff00", "#ff0000"];
const btnsSect = document.getElementsByClassName("section__btns");
const recallSect = document.getElementsByClassName("section__recall");
const verfiySect = document.getElementsByClassName("section__verify");
const actionBtn = document.getElementById("btn__action");
const answerBtn = document.getElementById("btn__answer");
const element = document.getElementById("element");
const count = document.getElementById("count");
const countdownTimer = document.getElementById("countdownTimer");
let interval;
let answers = {};
let nextState = "Start";
let countdownValue = 4;
let elementCount = 0;
let isCountdown = false;
function action() {
switch (nextState) {
case "Start":
start();
break;
case "Stop":
stop();
break;
case "Reset":
reset();
break;
}
}
function start() {
nextState = "Stop";
actionBtn.innerHTML = nextState;
actionBtn.style.visibility = "visible";
element.style.visibility = "visible";
count.style.visibility = "visible";
changeElementColour();
interval = setInterval(changeElementColour, 2000);
interval = setInterval(changeElementColour, 2000);
}
function changeElementColour() {
const newElement = colors[Math.floor(Math.random() * colors.length)];
element.style.backgroundColor = newElement;
answers[elementCount] = newElement
elementCount++;
count.innerHTML = elementCount;
}
</script>
</html>
You can use animation:
#-webkit-keyframes blinker {
0% { opacity: 1.0; }
50% { opacity: 0.0; }
100% { opacity: 1.0; }
}
#element{
animation: blinker 2s cubic-bezier(0, 0, 0.9, -0.02) infinite;
}
Simply add a css animation that lets you render a transition between the inverse of the color that last the duration you want it to say 1/60 a second, and you may wish to apply additional details. You can trigger this to happen each time by simply changing toggling a temporary class to retrigger the animation.
CSS For Inverting the Color
From W3Schools
/* The animation code */
#keyframes example {
from {filter: inverse(1);}
to {filter: inverse(0);}
}
//make sure to add browser extensions for webkit
/* The element to apply the animation to */
div.class {
animation-name: example;
animation-duration: 0.3s;
animation-timing-sequence: ease-in-out;
}
This code was modified from W3Schools and needs to be adjusted to meet your exact application's needs.
Simply Toggle off/on The Class this css is placed on using Javascript and the animation should replay
Also Jquery and Javascript both have great API's for handling Animation Events as well that you may play around with.
See CSS Animations
Toggling Classes With JQuery
Getting Animation Events With JQuery

CSS combination of :active and :not psuedo classes

I have a code where the "card" class takes the user to an article when clicked.
And its child, "category", takes the user to another website when clicked.
<div class="card">
<div class="img"></div>
<div class="category"></div>
<div class="title"></div>
<div class="description"></div>
</div>
I'm trying to write CSS animations with div:active for these two divs.
So when I have the following, the whole card animates:
.card:active {
transform: translateX(50px);
transition: all 0.5s ease;
}
But I don't want the card to animate when the user clicks on the category div.
So, I tried something like the following, and others, which didn't work.
:not(.category).card:active{
transform: translateX(50px);
transition: all 0.5s ease;
}
Is there a combination of :not and :active pseudo classes that I could use to make the card animate when clicked, but not animate when the category is being clicked?
solution 1, CSS only
Generally one could use...
/* CSS */
.card { pointer-events: none }
.card>:not(.category) { pointer-events: auto } /* all kids except .category */
.card:active { transform: translateX(50px) }
/* HTML */
<div class="card">
<div class="img">image</div>
<div class="category">CATEGORY</div>
<div class="title">no-card</div>
<div class="description">description</div>
</div>
...and clicking any child of .card, except class .category, will trigger the card :active event as well as :hover. However, any card space not occupied by child elements wil not trigger any event (i.e. .card:padding and .category will not trigger :active or :hover).
Another drawback is that .category will listen to no events at all and therefore cannot be an input element that needs to handle those events (like a <button>, as shown in the demo).
If this is acceptable, this solution is the easiest to code and maintain.
solution 2, CSS plus JS
This solution uses only simple CSS...
.effect:active { transform: translateX(50px) } /* NOT .card:active */
...and some Vanilla Javascript (pseudo code) that simply removes/adds the .effect class from .card when appropriate.
forEach cardList.item do
card.onmouseover = enableEffect();
card.category.onmouseenter = disableEffect();
card.category.onmouseout = enableEffect();
disableEffect = remove class 'effect' from .card
enableEffect = add class 'effect' to .card
The below snippet includes both solutions, is heavily commented and includes a few responsiveness extras (like CSS columns, main font and page spacing. Math used MathIsFun: Linear Equation).
Just copy the code and have fun with it!
SNIPPET
'use-strict';
// Traverse an array and execute the passed callback function for each array element found
var forEachEntryIn = function (array, callback, scope) {
for (var i = 0; i < array.length; i++) { callback.call(scope, i, array[i]); } };
// Get the list of cards
var cards = document.getElementsByClassName('card');
// Make this a function and you can toggle it with a <button>
var DEBUG = false; // set to 'true' for debug view and some console output
(DEBUG) ? document.body.setAttribute('outlines','1') : document.body.setAttribute('outlines','0');
// Traverse the list of cards
forEachEntryIn( cards,
function (idx,card,scope) {
// '.effect' is needed by default,
// so why add it in HTML class="" property when we can do it here...
card.classList.add('effect'); // remove if you want to assign in HMTL anyway
/*
MOUSEOVER events are bubbled to child elements
MOUSEENTER does not bubble, needed on '.category'
target: the element that triggered the event ('.card' OR any of its child elements)
currentTarget: the element that the event listener is attached to: '.card'
*/
card.onmouseover = function(e) { // Attach 'MOUSEOVER' listener to '.card'
// Parent check: event may be bubbled (from any '.card' children)
// So, is the parent a '.card' or maybe its parent?
if (e.target.parentElement == e.currentTarget) {
enableEffect(e.target.parentElement); // Activate '.card' animation
};
// NOTE: Disable the check, click a card and see what happens....funny!
};
var category = card.querySelector('.category');
if (card.contains(category)) {
category.onmouseenter = function(e) { disableEffect(e.currentTarget.parentElement); };
category.onmouseout = function(e) { enableEffect (e.currentTarget.parentElement); };
};
} // end function (idx,el,scope)
); // end forEachEntryIn
// Helper functions to keep main loop readable
function enableEffect(parent) {
if (!parent.classList.contains('effect')) { // if parent has no '.effect'
parent.classList.add('effect'); // then add it
};
if (DEBUG) logInfo(parent);
};
function disableEffect(parent) {
if (parent.classList.contains('effect')) { // parent if has '.effect'
parent.classList.remove('effect'); // then remove it
};
if (DEBUG) logInfo(parent);
};
// For debugging
function logInfo(p) {
console.log( // Show some info in browser console
((p.className) ? '<' + p.tagName +' class="' + p.className + '">': '<' + p.tagName +'>' ),
p.classList.contains('effect')
);
};
/********************************/
/* demo for CSS only solution 1 */
/********************************/
.no-card {
pointer-events: none;
}
.no-card>:not(.category) {
pointer-events: auto;
}
.no-card:active {
transform: translateX(50px);
transition: all 0.5s ease;
}
/***********************************/
/* demo for CSS plus JS solution 2 */
/***********************************/
/* class will be assigned with JS */
.effect:active {
transform: translateX(50px);
transition: all 0.5s ease;
}
/*****************************************************/
/* below just demo, everything can be safely removed */
/*****************************************************/
/**************************/
/* preferred global rules */
/**************************/
html,body { box-sizing: border-box; width: 100%; max-width: 100% }
*::before,*::after, * { box-sizing: inherit }
body { margin: 0 }
/* ALL math reference: https://www.mathsisfun.com/equation_of_line.html */
/* responsive base font size using y = mx + b */
html { font-size: calc(0.625vmin + 0.75rem) } /* (320,14)(1280,20) */
/* prohibit user from selecting text (put in <body>) */
[no-select] { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none }
[do-select] { -webkit-user-select: text; -moz-user-select: text; -ms-user-select: text; user-select: text; cursor: auto }
/* enable user to select text (put in specific elements) */
/* to show all elements with outlines (assigned to <body> with JS) */
[outlines="1"] * { outline: 1px dashed }
/***********************************/
/* Extra: plain responsive columns */
/***********************************/
body {
/*
responsive page padding using y = mx + b
p1(320,32) p2(1920, 72) => y = 0.025x + 24
p3(320, 8) p4(1920,320) => y = 0.195x - 54.4
*/
padding: calc(2.5vh + 24px) calc(19.5vw - 54.4px);
}
.cardList {
column-count: 3; /* preferred number of columns given column-width */
column-gap: 0; /* handled with card margins */
/*
column width using y = mx + b
mobile/tablet, 1 column : 320 - 60 = 260px
desktop, 3 columns: (1920 - 640) / 3 = 426 minus animation gap = 376px
p1(320,260) p2(1920,376)
=> y = 7.25x + 236.8
*/
column-width: calc(7.25vw + 230.8px); /* (320,260)(1920,376) */
/* (320,260)(1920,376) for scrollbar => 236.8 - (18/3) = 230.8px */
}
.card {
break-inside: avoid; /* don't split card over columns */
}
/******************/
/* card eye-candy */
/******************/
.wrapper,
.cardList {
background-color: rgba(0,0,0,.1); /* just to review body padding */
padding: 2rem 0;
}
.no-card, .card {
background-color: CornSilk;
padding: 1rem;
margin : 1rem;
margin-right: 60px; /* animation width plus 10px space */
/* GMC elevation 1dp */
box-shadow: 0px 2px 1px -1px rgba(0,0,0,.20),
0px 1px 1px 0px rgba(0,0,0,.14),
0px 1px 3px 0px rgba(0,0,0,.12);
}
.card:first-child { margin-top: 0 } /* otherwise jagged column tops */
/* Some :hover animation */
.no-card:hover, .card:hover {
/* GMC elevation 3dp */
box-shadow: 0px 3px 3px -2px rgba(0,0,0,.20),
0px 3px 4px 0px rgba(0,0,0,.14),
0px 1px 8px 0px rgba(0,0,0,.12);
}
<body no-select>
<h2>solution 1, CSS only</h2>
<div class="wrapper">
<div class="no-card">
<div class="img">image</div>
<button class="category">CATEGORY</button>
<div class="title">no-card</div>
<div class="description">description</div>
</div>
</div>
<h2>solution 2, CSS plus JS</h2>
<div class="cardList">
<div class="card">
<div class="img">image</div>
<button class="category">CATEGORY</button>
<div class="title">card 1</div>
<div class="description">description</div>
</div>
<div class="card">
<div class="img">image</div>
<button class="category">CATEGORY</button>
<div class="title">card 2</div>
<div class="description">description</div>
</div>
<div class="card">
<div class="img">image</div>
<button class="category">CATEGORY</button>
<div class="title">card 3</div>
<div class="description">description</div>
</div>
<div class="card">
<div class="img">image</div>
<button class="category">CATEGORY</button>
<div class="title">card 4</div>
<div class="description">description</div>
</div>
<div class="card">
<div class="img">image</div>
<div>some other element</div>
<div class="title">card 5</div>
<div class="description">description</div>
</div>
<div class="card">
<div class="img">image</div>
<button class="category">CATEGORY</button>
<div class="title">card 6</div>
<div class="description">description</div>
</div>
</div>
</body>

How to change data visible range to % percent

I am using this for my header that changes in a one page scroll up and down page. I noticed that it's not responsive so i am asking you if you maybe know a way to make that responsive. Like changing the 0-690 into a percentage so that it will work on mobile and also on a tv screen.
HTML
<div class="header header-1" data-visible-range="0-690">Portfolio</div>
<div class="header header-2" data-visible-range="691-2100">Services</div>
<div class="header header-3" data-visible-range="2101-">Contact</div>
CSS
.header-1 {
background-color:dimgray;
display: block;
}
.header-2 {
background-color:dimgray;
}
.header-3 {
background-color:dimgray;
}
.header {
position: fixed;
top: 0;
left: 0;
height:8vmax;
width: 100%;
display: none;
visibility:hidden;
transition: visibility .4s, opacity .4s ease-in-out;opacity:0;
font-size:4vmax;padding:1.58vmax;color:white;
}
What if, instead of basing it off pixels, you just checked to see if an element hit the top of the page, and then changed the header?
We'll call these elements "triggers." See my code below for an example of how they work.
let updateHeader = () => {
let scrollTop = $(window).scrollTop(),
triggerTitle = "Hi";
$('.trigger').each((i, el) => {
let topPos = $(el).offset().top,
distance = topPos - scrollTop;
if (distance < 0)
triggerTitle = $(el).data('title');
});
$('header h2').text(triggerTitle);
}
$(window).scroll(updateHeader);
$(window).on('touchmove', updateHeader);
body {
margin: 0;
}
#container {
height: 1000px;
}
header {
width: 100%;
position: fixed;
top: 0;
background-color: red;
}
p {
margin: 200px 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<header><h2>Hi</h2></header>
<p class="trigger" data-title="section1">
trigger1
</p>
<p class="trigger" data-title="section2">
trigger2
</p>
<p class="trigger" data-title="section3">
trigger3
</p>
</div>
As you scroll down the page, each trigger hits the top of the page, and the text in the header will change to the the value of the latest trigger's data-title. You could position these triggers appropriately above each of your website's sections, so that, no matter what size the screen, the header should update at the right time. Here's a codepen.
EDIT
Try this JS instead for maximum compatibility (no es6 involved).
function updateHeader() {
var scrollTop = $(window).scrollTop(),
triggerTitle = "Hi";
$('.trigger').each(function(i, el) {
var topPos = $(el).offset().top,
distance = topPos - scrollTop;
if (distance < 0)
triggerTitle = $(el).data('title');
});
$('header h2').text(triggerTitle);
}
$(window).scroll(updateHeader);
$(window).on('touchmove', updateHeader);

Allow select text on a HTML 5 draggable child element

Having a table with draggable rows where each row is draggable=true, how can the user still be able to select text from a column?
<table>
<thead>..</thead>
<tbody>
..
<tr draggable="true">
<td>..</td>
<td>Cool text but you can't select me</td>
<td>..</td>
</tr>
..
</tbody>
</table>
Another simple example (https://codepen.io/anon/pen/qjoBXV)
div {
padding: 20px;
margin: 20px;
background: #eee;
}
.all-copy p {
-webkit-user-select: all; /* Chrome all / Safari all */
-moz-user-select: all; /* Firefox all */
-ms-user-select: all; /* IE 10+ */
user-select: all; /* Likely future */
}
<div class="all-copy" draggable="true">
<p>Select me as text</p>
</div>
There are two things we need to do.
One thing is limitting the drag event only trigger on specified area, for example, the drag handle.
The other thing is that we only set the text on the div with content class can be selected. The reason why we do so is that the element that has been set to draggable, on which browser will add a default rule user-select: none.
const itemEl = document.querySelector('.item');
const handleEl = document.querySelector('.handle');
let mouseDownEl;
itemEl.onmousedown = function(evt) {
mouseDownEl = evt.target;
}
itemEl.ondragstart = function(evt) {
// only the handle div can be picked up to trigger the drag event
if (mouseDownEl.matches('.handle')) {
// ...code
} else {
evt.preventDefault();
}
}
.item {
width: 70px;
border: 1px solid black;
text-align: center;
}
.content {
border-top: 1px solid gray;
user-select: text;
}
<div class="item" draggable="true">
<div class='handle'>handle</div>
<div class='content'>content</div>
</div>
One way to make that work, is to actually check which element fired the event, e.target, against the element that has the listener attach to itself, #draggable (in this case using this).
if (e.target === this) {...}
This will allow default behavior on element positioned inside the draggable element, such as selecting a text and so on.
Note, since Firefox has issue with draggable="true", I used a different drag method.
Stack snippet
(function (elem2drag) {
var x_pos = 0, y_pos = 0, x_elem = 0, y_elem = 0;
document.querySelector('#draggable').addEventListener('mousemove', function(e) {
x_pos = e.pageX;
y_pos = e.pageY;
if (elem2drag !== null) {
elem2drag.style.left = (x_pos - x_elem) + 'px';
elem2drag.style.top = (y_pos - y_elem) + 'px';
}
})
document.querySelector('#draggable').addEventListener('mousedown', function(e) {
if (e.target === this) {
elem2drag = this;
x_elem = x_pos - elem2drag.offsetLeft;
y_elem = y_pos - elem2drag.offsetTop;
return false;
}
})
document.querySelector('#draggable').addEventListener('mouseup', function(e) {
elem2drag = null;
})
})(null);
#draggable {
display: inline-block;
background: lightgray;
padding:15px;
cursor:move;
position:relative;
}
span {
background: white;
line-height: 25px;
cursor:auto;
}
<div id="draggable">
<span>Select me as text will work<br>when the mouse is over the text</span>
</div>

HTML5/Cesium - making divs float over cesium map

I am using cesium : http://cesiumjs.org/
and I wanted to make some divs float over a cesium map, but I can't get it to work.
I tried the following container/tag method at jsfiddle.net/j08691/dChUR/5/ - substituing the image by a cesium map div - but it doesn't seem to work - the "tag" div isn't shown.
Any help?
You need to add position: absolute; and either top or bottom to your CSS, because the widget also uses absolute positioning. Adding this creates a new stacking context, which overrides z-index.
Here's a working example, hit "Run Code Snippet" at the bottom of this:
Cesium.Camera.DEFAULT_VIEW_FACTOR = 0;
var viewer = new Cesium.Viewer('cesiumContainer', {
timeline: false,
animation: false,
navigationHelpButton: false
});
var skyAtmosphere = viewer.scene.skyAtmosphere;
var skyCheckbox = document.getElementById('skyCheckbox');
skyCheckbox.addEventListener('change', function() {
viewer.scene.skyAtmosphere = skyCheckbox.checked ? skyAtmosphere : undefined;
}, false);
html, body, #cesiumContainer {
width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden;
font-family: sans-serif; color: #edffff;
}
#controlPanel {
position: absolute;
top: 5px;
left: 5px;
background: rgba(42, 42, 42, 0.8);
padding: 5px 8px;
border-radius: 5px;
}
label {
cursor: pointer;
}
label:hover span {
text-decoration: underline;
}
<link href="http://cesiumjs.org/releases/1.15/Build/Cesium/Widgets/widgets.css"
rel="stylesheet"/>
<script src="http://cesiumjs.org/releases/1.15/Build/Cesium/Cesium.js">
</script>
<div id="cesiumContainer"></div>
<div id="controlPanel">
This is a floating control panel<br/>
with a translucent background color.
<p>
<label>
<input id="skyCheckbox" type="checkbox" checked />
<span>Enable atmospheric effect</span>
</label><br/>
<button class="cesium-button">Button 1</button>
<button class="cesium-button">Button 2</button>
</p>
</div>
To add to emackey's answer, what I had to do in addition to adding position: absolute to my css was to add a top:150px or bottom:150px. Basically anything that will specify a position relative to the parent container.
Even though using the absolute position it is most likely being pushed down by the cesium widget since it takes up 100% height.