So I am not using any CSS framework like bootstrap to get responsiveness out of the box which is why I am having trouble making responsive layout.
Please see jsbin
I essentially what to auto-resize colorful boxes based on browser window size eg that should shrink or grow automatically based on window size. Colorful boxes inside their parent should always be in horizontal row but should be able to adjust their width and height like this example.
I tried using flex-wrap: nowrap; but it didn't do the trick :(
Please note that colorful boxes are using position:absolute with parent's position being relative. I am also adding left css property to these boxes via JavaScript to change their position for the sake of sliding animation.
function Carousel(options) {
options = options || {};
// options vars
let speed = options.speed || 1; // animation speed in seconds
let width = options.width || 200;
let height = options.height || 100;
let space = options.space || 30;
// other vars
let container = document.querySelector('.carousel-container .carousel');
let slides = container.querySelectorAll('.carousel-item');
let curSlide = null;
let prevSlide = null;
let nextSlide = null;
if (areSlidesPresent()) {
setup();
}
// functions //
function setup() {
// we assume first slide to be current one as per UI requirements
//slides[0].classList.add("current");
curSlide = slides[0];
// we assume second slide to be next as per UI requirements
nextSlide = slides[1];
// we assume last slide to be prev as per UI requirements
prevSlide = slides[slides.length - 1];
// position elements horizontally
positionSlides();
}
function areSlidesPresent() {
return slides.length > 0;
}
this.getCurrentSlide = function() {
return curSlide;
}
this.getNextSlide = function() {
return nextSlide;
}
this.getPreviousSlide = function() {
return prevSlide;
}
this.setNextSlide = function() {
if (areSlidesPresent()) {
let allSlides = [];
// build new order of slides
allSlides.push(nextSlide);
// middle ones
for (let i = 2; i < slides.length; i++) {
allSlides.push(slides[i]);
}
allSlides.push(curSlide);
// now add to DOM after cleaning previous slide order
for (let i = 0; i < allSlides.length; i++) {
container.appendChild(allSlides[i]);
}
slides = allSlides;
setup();
}
}
this.setPreviousSlide = function() {
if (areSlidesPresent()) {
let allSlides = [];
// build new order of slides
allSlides.push(prevSlide);
allSlides.push(curSlide);
// middle ones
for (let i = 1; i < slides.length - 1; i++) {
allSlides.push(slides[i]);
}
// now add to DOM after cleaning previous slide order
for (let i = 0; i < allSlides.length; i++) {
container.appendChild(allSlides[i]);
}
slides = allSlides;
setup();
}
}
function positionSlides() {
curSlide.style.marginLeft = '0px';
for (let i = 0; i < slides.length; i++) {
slides[i].querySelector('.carousel-content').style.width = (width) + 'px';
slides[i].querySelector('.carousel-content').style.height = (height) + 'px';
let elementWidth = getStyle(nextSlide, 'width');
if (i === 0) {
slides[i].style.zIndex = -10;
//slides[i].style.opacity = '1';
slides[i].querySelector('.carousel-content').style.width = (width + 50) + 'px';
slides[i].querySelector('.carousel-content').style.height = (height + 50) + 'px';
} else {
slides[i].style.zIndex = 0;
//slides[i].style.opacity = '0.7';
}
if (i > 0) {
slides[i].style.marginLeft = (space * 2) + 'px';
elementWidth = parseInt(elementWidth, 10) + space;
}
slides[i].style.transition = speed + 's';
slides[i].style.left = (elementWidth * i) + 'px';
}
}
function getStyle(el, prop) {
return window.getComputedStyle(el, null).getPropertyValue(prop)
.replace('px', '')
.replace('em', '');
}
}
// utility
function log(text) {
console.log(text);
}
var options = {
speed: 1, // animation speed
width: 250, // slide width
height: 150, // slide height
space: 25 // space in px between slides
};
var carousel = new Carousel(options);
function selectCurrent() {
log(carousel.getCurrentSlide());
}
function selectNext() {
carousel.setNextSlide();
}
function selectPrev() {
carousel.setPreviousSlide();
}
.carousel-container {
width: auto;
height: auto;
margin: 25px;
display: flex;
align-items: center;
justify-content: center;
}
.carousel {
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.carousel .carousel-item {
position: absolute;
transition: transform .5s ease-in-out;
color: #fff;
margin-left: 10px;
-webkit-box-reflect: below 10px -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(70%, transparent), to(rgba(255, 255, 255, 0.2)));
}
.carousel .carousel-item:first-child .carousel-content {
opacity: 1;
}
.carousel .carousel-item .carousel-title {
font-size: 24px;
text-align: center;
}
.carousel .carousel-item .carousel-content {
font-size: 18px;
font-weight: bold;
border: 1px solid #ccc;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
}
/* temp css below */
body {
background: #2C374A;
padding-top: 150px;
}
.navigation {
display: flex;
align-items: center;
justify-content: center;
margin-top: 150px;
}
.button {
color: #444;
padding: 10px;
width: 60px;
cursor: pointer;
background: #CCC;
text-align: center;
font-weight: bold;
border-radius: 5px;
border-top: 1px solid #FFF;
box-shadow: 0 5px 0 #999;
transition: box-shadow 0.1s, top 0.1s;
margin: 10px;
}
.button:hover,
.button:hover {
color: #000;
}
.button:active,
.button:active {
top: 104px;
box-shadow: 0 1px 0 #999;
}
<div class="carousel-container">
<div class="carousel">
<div class="carousel-item">
<div class="carousel-title">Make a Call</div>
<div class="carousel-content" style="background:#0E6DE8;border:10px solid #78B1FA">Slide One</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Message</div>
<div class="carousel-content" style="background:#D90080;border:10px solid #E357A9">Slide Two</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Picture</div>
<div class="carousel-content" style="background:#FEC601;border:10px solid #FFDD64">Slide Three</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Video</div>
<div class="carousel-content" style="background:#3DB365;border:10px solid #90E0AB">Slide Four</div>
</div>
</div>
</div>
<div class="navigation">
<div class="button" onclick="selectNext()">Next</div>
<div class="button" onclick="selectCurrent()">Select</div>
<div class="button" onclick="selectPrev()">Prev</div>
</div>
Problem here was:
Width was hard-coded in your JS, so if width is in px it can't be responsive.
By applying position:absolute to you carousel-item, it forced the children to get out of the box.
What I did:
Got rid of the static width and other functionalities related to width from your JS
Removed position:absolute from carousel-item
Let me know if this is what you are expecting.
function Carousel(options) {
options = options || {};
// options vars
let speed = options.speed || 1; // animation speed in seconds
// let width = options.width || 100;
let height = options.height || 100;
let space = options.space || 30;
// other vars
let container = document.querySelector('.carousel-container .carousel');
let slides = container.querySelectorAll('.carousel-item');
let curSlide = null;
let prevSlide = null;
let nextSlide = null;
if (areSlidesPresent()) {
setup();
}
// functions //
function setup() {
// we assume first slide to be current one as per UI requirements
//slides[0].classList.add("current");
curSlide = slides[0];
// we assume second slide to be next as per UI requirements
nextSlide = slides[1];
// we assume last slide to be prev as per UI requirements
prevSlide = slides[slides.length - 1];
// position elements horizontally
positionSlides();
}
function areSlidesPresent() {
return slides.length > 0;
}
this.getCurrentSlide = function() {
return curSlide;
}
this.getNextSlide = function() {
return nextSlide;
}
this.getPreviousSlide = function() {
return prevSlide;
}
this.setNextSlide = function() {
if (areSlidesPresent()) {
let allSlides = [];
// build new order of slides
allSlides.push(nextSlide);
// middle ones
for (let i = 2; i < slides.length; i++) {
allSlides.push(slides[i]);
}
allSlides.push(curSlide);
// now add to DOM after cleaning previous slide order
for (let i = 0; i < allSlides.length; i++) {
container.appendChild(allSlides[i]);
}
slides = allSlides;
setup();
}
}
this.setPreviousSlide = function() {
if (areSlidesPresent()) {
let allSlides = [];
// build new order of slides
allSlides.push(prevSlide);
allSlides.push(curSlide);
// middle ones
for (let i = 1; i < slides.length - 1; i++) {
allSlides.push(slides[i]);
}
// now add to DOM after cleaning previous slide order
for (let i = 0; i < allSlides.length; i++) {
container.appendChild(allSlides[i]);
}
slides = allSlides;
setup();
}
}
function positionSlides() {
curSlide.style.marginLeft = '0px';
for (let i = 0; i < slides.length; i++) {
// slides[i].querySelector('.carousel-content').style.width = (width) + 'px';
slides[i].querySelector('.carousel-content').style.height = (height) + 'px';
let elementWidth = getStyle(nextSlide, 'width');
if (i === 0) {
slides[i].style.zIndex = -10;
//slides[i].style.opacity = '1';
// slides[i].querySelector('.carousel-content').style.width = (width + 50) + 'px';
slides[i].querySelector('.carousel-content').style.height = (height + 50) + 'px';
} else {
slides[i].style.zIndex = 0;
//slides[i].style.opacity = '0.7';
}
if (i > 0) {
slides[i].style.marginLeft = (space * 2) + 'px';
// elementWidth = parseInt(elementWidth, 10) + space;
}
slides[i].style.transition = speed + 's';
// slides[i].style.left = (elementWidth * i) + 'px';
}
}
function getStyle(el, prop) {
return window.getComputedStyle(el, null).getPropertyValue(prop)
.replace('px', '')
.replace('em', '');
}
}
// utility
function log(text) {
console.log(text);
}
var options = {
speed: 1, // animation speed
width: 250, // slide width
height: 150, // slide height
space: 25 // space in px between slides
};
var carousel = new Carousel(options);
function selectCurrent() {
log(carousel.getCurrentSlide());
}
function selectNext() {
carousel.setNextSlide();
}
function selectPrev() {
carousel.setPreviousSlide();
}
.carousel-container {
height: auto;
margin: 25px;
display: flex;
}
.carousel {
flex: 1;
height: 100%;
width: 100vh;
/* overflow:hidden; */
display: flex;
align-items: center;
justify-content: center;
}
.carousel .carousel-item {
transition: transform .5s ease-in-out;
color: #fff;
flex: 1;
margin-left: 10px;
-webkit-box-reflect: below 10px -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(70%, transparent), to(rgba(255, 255, 255, 0.2)));
}
.carousel .carousel-item:first-child .carousel-content {
opacity: 1;
}
.carousel .carousel-item .carousel-title {
font-size: 24px;
text-align: center;
}
.carousel .carousel-item .carousel-content {
font-size: 18px;
font-weight: bold;
border: 1px solid #ccc;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
}
/* temp css below */
body {
background: #2C374A;
}
.navigation {
display: flex;
align-items: center;
justify-content: center;
}
.button {
color: #444;
padding: 10px;
width: 60px;
cursor: pointer;
background: #CCC;
text-align: center;
font-weight: bold;
border-radius: 5px;
border-top: 1px solid #FFF;
box-shadow: 0 5px 0 #999;
transition: box-shadow 0.1s, top 0.1s;
margin: 10px;
}
.button:hover,
.button:hover {
color: #000;
}
.button:active,
.button:active {
box-shadow: 0 1px 0 #999;
}
<div class="navigation">
<div class="button" onclick="selectNext()">Next</div>
<div class="button" onclick="selectCurrent()">Select</div>
<div class="button" onclick="selectPrev()">Prev</div>
</div>
<div class="carousel-container">
<div class="carousel">
<div class="carousel-item">
<div class="carousel-title">Make a Call</div>
<div class="carousel-content" style="background:#0E6DE8;border:10px solid #78B1FA">Slide One</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Message</div>
<div class="carousel-content" style="background:#D90080;border:10px solid #E357A9">Slide Two</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Picture</div>
<div class="carousel-content" style="background:#FEC601;border:10px solid #FFDD64">Slide Three</div>
</div>
<div class="carousel-item">
<div class="carousel-title">Send a Video</div>
<div class="carousel-content" style="background:#3DB365;border:10px solid #90E0AB">Slide Four</div>
</div>
</div>
</div>
Related
I would like to implement the following: The animation should only start when I hover the mouse over the div. After I hovered over the div, the end number should remain visible and not change to the start value.
This is my code:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Animation</title>
<style>
.counter{
color: white;
font-size: 100px;
height: 300px;
width: 400px;
background-color: black;
display: flex;
align-items:center;
justify-content: center;
}
.animate{
position:absolute;
opacity:0;
transition:0s 180s;
}
.animate:hover {
opacity:1;
transition:0s;
}
</style>
</head>
<body>
<div id="animate" style="background-color: orange; width: 300px; height: 200px;" class="counter" data-target="500">0</div>
<script>
const counters = document.querySelectorAll('.counter');
for(let n of counters) {
const updateCount = () => {
const target = + n.getAttribute('data-target');
const count = + n.innerText;
const speed = 5000; // change animation speed here
const inc = target / speed;
if(count < target) {
n.innerText = Math.ceil(count + inc);
setTimeout(updateCount, 1);
} else {
n.innerText = target;
}
}
updateCount();
}
</script>
</body>
</html>
Add onmousover to id="animate"
<div id="animate" style="background-color: orange; width: 300px; height: 200px;" class="counter" data-target="500" onmouseover="animationEffect();">0</div>
Wrap the whole script in a method:
function animationEffect(){
const counters = document.querySelectorAll('.counter');
for(let n of counters) {
const updateCount = () => {
const target = + n.getAttribute('data-target');
const count = + n.innerText;
const speed = 5000; // change animation speed here
const inc = target / speed;
if(count < target) {
n.innerText = Math.ceil(count + inc);
setTimeout(updateCount, 1);
} else {
n.innerText = target;
}
}
updateCount();
}
}
Should solve the problem
EDIT:
The old answer was refering to the question before being edited. For the current case the following could be done:
const updateCount = n => {
const target = +n.getAttribute('data-target')
const count = +n.innerText
const speed = 5000 // change animation speed here
const inc = target / speed
if (count < target) {
n.innerText = Math.ceil(count + inc)
requestAnimationFrame(() => updateCount(n))
} else {
n.innerText = target
}
}
const counters = document.querySelectorAll('.counter')
for (let n of counters) {
n.addEventListener('mouseenter', () => updateCount(n), {
once: true
})
}
.counter {
color: white;
font-size: 100px;
height: 300px;
width: 400px;
background-color: black;
display: flex;
align-items: center;
justify-content: center;
}
.animate {
position: absolute;
opacity: 0;
transition: 0s 180s;
}
.animate:hover {
opacity: 1;
transition: 0s;
}
<div id="animate" style="background-color: orange; width: 300px; height: 200px" class="counter" data-target="500">
0
</div>
Old answer:
You would need to add a mouseenter event to the parent element. Note that the {once: true} option will make the event only fire once.
const parent = document.getElementById('parent')
parent.addEventListener('mouseenter', mouseEnterHandler, {once: true})
Then define the mouseEnterHandler callback as follows:
function mouseEnterHandler() {
for (let n of counters) {
n.style.display = 'block'
updateCount(n)
}
/* If you only have one counter then just get it by its Id:
const div = document.getElementById('hover-content')
div.style.display = 'block'
updateCount(div)
*/
}
n.style.display = 'block' will make the counter visible so no need for the css rule #parent:hover #hover-content { display:block; }.
Here is a working example:
const updateCount = n => {
const target = +n.getAttribute('data-target')
const count = +n.innerText
const speed = 5000 // change animation speed here
const inc = target / speed
if (count < target) {
n.innerText = Math.ceil(count + inc)
requestAnimationFrame(() => updateCount(n))
} else {
n.innerText = target
}
}
const counters = document.querySelectorAll('.counter')
const parent = document.getElementById('parent')
parent.addEventListener('mouseenter', mouseEnterHandler, {
once: true
})
function mouseEnterHandler() {
for (let n of counters) {
n.style.display = 'block'
updateCount(n)
}
}
.counter {
color: white;
font-size: 100px;
height: 140px;
width: 400px;
background-color: black;
display: flex;
align-items: center;
justify-content: center;
}
#hover-content {
display: none;
}
<div id="parent">
Some content
<div hidden id="hover-content" class="counter" data-target="232">0</div>
</div>
I have this fiddle.
https://jsfiddle.net/oeuc8L9y/2/
If you click the background the coordinates where you clicked get centered.
Is it possible to make the pointer events only work inside the hole?
Since I'm using an image for the ring the whole thing blocks the pointer-events but if I set it to pointer-events: none; I can click trough the ring too.
This is good.
This is bad.
I assume a way would be to get the pixel coordinate and calculate if it's inside the hole but I feel like that would only work for a specific screen size and it'd break if resized.
tooltip_X = $("#x-coords");
tooltip_Y = $("#y-coords");
$("#background").on("mouseover", (e) => {
showTooltip(e);
});
$("#background").on("mousemove", (e) => {
updateCoords(e);
moveTooltip(e);
});
$("#background").on("mouseout", (e) => {
hideTooltip(e);
});
$("#background").on("click", (e) => {
move(e);
updateCoords(e);
});
function showTooltip(e) {
$("#tooltip").css("display", "block");
}
function hideTooltip(e) {
$("#tooltip").css("display", "none");
}
function moveTooltip(e) {
var left = 0;
var top = 0;
if (e.pageX + $("#tooltip").width() + 10 < document.body.clientWidth) {
left = e.pageX + 10;
} else {
left = document.body.clientWidth + 5 - $("#tooltip").width();
}
if (e.pageY + $("#tooltip").height() + 10 < document.body.clientHeight) {
top = e.pageY + 10;
} else {
top = document.body.clientHeight + 5 - $("#tooltip").height();
}
$("#tooltip").offset({ top: top, left: left });
}
function updateCoords(e) {
var mouse_x = e.clientX - $("#background").offset().left;
var mouse_y = e.clientY - $("#background").offset().top;
$("#x-coords").text(Number.parseInt(mouse_x));
$("#y-coords").text(Number.parseInt(mouse_y));
}
function move(e) {
var mouse_x = e.clientX - $("#background").offset().left;
var mouse_y = e.clientY - $("#background").offset().top;
var new_x = 0;
var new_y = 0;
if (mouse_x < 250) {
mouse_x = 250;
}
if (mouse_y < 250) {
mouse_y = 250;
}
if (mouse_x > 1670) {
mouse_x = 1670;
}
if (mouse_y > 950) {
mouse_y = 950;
}
new_x = -(mouse_x - 250);
new_y = -(mouse_y - 250);
$("#background").css("margin-top", new_y);
$("#background").css("margin-left", new_x);
}
#container {
position: relative;
width: 500px;
height: 500px;
overflow: hidden;
z-index: 100;
}
#ring {
position: absolute;
border-radius: 100%;
max-height: 500px;
max-width: 500px;
z-index: 90;
pointer-events: none;
}
#lens {
position: absolute;
border: 1px solid red;
/*
something to make it round
*/
z-index: 80;
pointer-events: none;
}
#background {
position: absolute;
width: 1920px;
height: 1200px;
background-image: url("https://wallpaperaccess.com/full/197542.jpg");
z-index: 80;
outline: 5px dotted purple;
outline-offset: -5px;
}
#tooltip {
position: absolute;
white-space: nowrap;
background: #ffffcc;
border: 1px solid black;
padding: 5px;
color: black;
z-index: 999;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<head> </head>
<body>
<div id="container">
<img id="ring" src="https://pngimg.com/uploads/circle/circle_PNG26.png" alt="Lens Ring" />
<div id="lens"></div>
<div id="background"></div>
<span id="tooltip"> Div Coords (<span id="x-coords"></span>,<span id="y-coords"></span>) </span>
</div>
</body>
</html>
I created a traffic light using vue.js, but it doesn't work. It should display the colours (red, yellow and green) according to the time duration. Is there a problem that I've missed?
CSS
#screen {
width: 450px;
height: 740px;
background: #222;
border-radius: 12px;
margin: auto;
padding: 23px;
}
.light {
width: 230px;
height: 270px;
display: inline-block;
opacity: 0.2;
border-radius: 100%;
transition: opacity 10s;
margin-bottom: 12px;
}
.active {
opacity: 1;
}
.red {
background: red;
}
.yellow {
background: yellow;
}
.green {
background: green;
}
My HTMl
I've created the divs.
<div id="screen">
<div id="light red" :class="{active: now=='red'}"></div>
<div id="light yellow" :class="{active: now=='yellow'}"></div>
<div id="light green" :class="{active: now=='green'}"></div>
</div>
and this is vue.js
It seems like everything on it's place and console doesn't send any error.
But I still can't understand, why it isn't working?
class State {
constructor(name, dur, next){
this.name = name;
this.dur = dur;
this.next = next;
}
}
class Constroller {
trigger(state, callback){
callback(state);
setTimeout(()=>{
this.trigger(state.next, callback);
}, state.dur * 10)
}
}
var app = new Vue({
el: '#screen',
data:{
now: 'red'
},
mounted(){
var constroller = new Constroller();
var red = new State('red', 2);
var yellowRed = new State('yellow', 1);
var yellowGreen = new State('yellow', 1);
var green = new State('green', 3);
red.next = yellowRed;
yellowR.next = green;
green.next = yellowGreen;
yellowG.next = red;
constroller.trigger(red, (state)=>{
this.now = state.name;
});
}
})
Am I missing smth? Should I rewrite my function?
Well there are several things wrong here:
<div id="light red" :class="{active: now=='red'}"></div>
<div id="light yellow" :class="{active: now=='yellow'}"></div>
<div id="light green" :class="{active: now=='green'}"></div>
Should be class instead of id.
var red = new State('red', 2);
var yellowRed = new State('yellow', 1);
var yellowGreen = new State('yellow', 1);
var green = new State('green', 3);
red.next = yellowRed;
yellowR.next = green;
green.next = yellowGreen;
yellowG.next = red;
You named yellowRed and yellowGreen but then used yellowR and YellowG
Anyway, I made a quick fiddle here https://jsfiddle.net/Lo50j4rw/ that you can check out, I also tweak some duration stuff.
Also, at least in my country the light goes green after red :D
Your main problem is that you used id where you should have used class for your HTML light elements. You also had weird choices for timing: each light had a very short duration, but the transition was 10 seconds.
Fixing those, you get this:
class State {
constructor(name, dur, next) {
this.name = name;
this.dur = dur;
this.next = next;
}
}
class Constroller {
trigger(state, callback) {
callback(state);
setTimeout(() => {
this.trigger(state.next, callback);
}, state.dur * 1000)
}
}
new Vue({
el: '#screen',
data: {
now: 'red'
},
mounted() {
var constroller = new Constroller();
var red = new State('red', 2);
var yellowRed = new State('yellow', 1);
var yellowGreen = new State('yellow', 1);
var green = new State('green', 3);
red.next = yellowRed;
yellowRed.next = green;
green.next = yellowGreen;
yellowGreen.next = red;
constroller.trigger(red, (state) => {
this.now = state.name;
});
}
})
#screen {
width: 450px;
height: 740px;
background: #222;
border-radius: 12px;
margin: auto;
padding: 23px;
}
.light {
width: 230px;
height: 270px;
display: inline-block;
opacity: 0.2;
border-radius: 100%;
transition: opacity 0.3s;
margin-bottom: 12px;
background-color: white;
}
.active {
opacity: 1;
}
.red {
background: red;
}
.yellow {
background: yellow;
}
.green {
background: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="screen">
<div class="light red" :class="{active: now=='red'}"></div>
<div class="light yellow" :class="{active: now=='yellow'}"></div>
<div class="light green" :class="{active: now=='green'}"></div>
</div>
Having a flex layout with top, left, middle and right.
Middle is divided into main and foot.
Within the main I want to have fixed elements, kind of like an MDI, as well as static elements.
If one scroll the fixed element should stay in same position of view. But, it should be contained within the main element if it is moved above or to the left of main. As in: not overlap the top, left, right etc.
THIS:
Colors and margins added to make a visual representation of the layout
NOT THIS:
Below is a simplified sample with a container within a container.
If one select the fixed positioning for the sub "window" it stay in place on scrolling, but it overlaps the parent if moved outside.
I can use absolute and reposition it on scroll by using JavaScript, but wondered if there was a pure CSS / layout way to get the same result.
function set_style_pos (e) {
moveable.style.position = e.target.value;
}
function halt (e) {
e.preventDefault();
e.stopPropagation();
}
const drag = {
el: null,
ex: 0,
ey: 0,
xs: 0,
ys: 0,
move: function (e) {
halt(e);
drag.el.style.marginLeft = (e.clientX - drag.sx + drag.ex) + 'px';
drag.el.style.marginTop = (e.clientY - drag.sy + drag.ey) + 'px';
},
end: function (e) {
halt(e);
window.removeEventListener('mouseup', drag.end);
window.removeEventListener('mousemove', drag.move);
},
start: function (e) {
let cs;
halt(e);
window.addEventListener('mouseup', drag.end);
window.addEventListener('mousemove', drag.move);
drag.el = e.target;
cs = getComputedStyle(drag.el);
drag.ex = parseInt(cs.getPropertyValue('margin-left')) || 0;
drag.ey = parseInt(cs.getPropertyValue('margin-top')) || 0;
drag.sx = e.clientX;
drag.sy = e.clientY;
},
check: function (e) {
let t = e.target;
if (t.dataset.moveable == "1")
drag.start(e);
}
};
document.addEventListener('mousedown', drag.check);
document.addEventListener('change', set_style_pos);
lines.textContent = "scroll me\n".repeat(100);
* {
box-sizing: border-box;
}
body {
margin: 0;
height: 100vh;
display: flex;
flex-direction: column;
color: #444;
font: 14px sans-serif;
}
label {
cursor: pointer;
}
.outer {
display: flex;
padding: 20px;
background: goldenrod;
flex-grow: 1;
overflow: hidden;
}
.inner {
position: relative;
overflow: scroll;
background: gray;
flex-grow: 1;
}
.box {
position: absolute;
width: 140px;
height: 150px;
background: silver;
box-shadow: 0 0 3px red;
cursor: move;
margin-left: 90px;
margin-top: -5px;
padding: 20px;
}
.box div {
font-weight: 700;
pointer-events: none;
text-align: center;
}
<div class="outer">
<div class="inner">
<div class="box" id="moveable" data-moveable="1">
<div>Move Me</div><br />
<label><input type="radio" name="p" value="absolute" checked />absolute</label><br />
<label><input type="radio" name="p" value="fixed" />fixed</label>
</div>
<pre id="lines"></pre>
</div>
</div>
Just use z-index.
Example:
function set_style_pos (e) {
moveable.style.position = e.target.value;
}
function halt (e) {
e.preventDefault();
e.stopPropagation();
}
const drag = {
el: null,
ex: 0,
ey: 0,
xs: 0,
ys: 0,
move: function (e) {
halt(e);
drag.el.style.marginLeft = (e.clientX - drag.sx + drag.ex) + 'px';
drag.el.style.marginTop = (e.clientY - drag.sy + drag.ey) + 'px';
},
end: function (e) {
halt(e);
window.removeEventListener('mouseup', drag.end);
window.removeEventListener('mousemove', drag.move);
},
start: function (e) {
let cs;
halt(e);
window.addEventListener('mouseup', drag.end);
window.addEventListener('mousemove', drag.move);
drag.el = e.target;
cs = getComputedStyle(drag.el);
drag.ex = parseInt(cs.getPropertyValue('margin-left')) || 0;
drag.ey = parseInt(cs.getPropertyValue('margin-top')) || 0;
drag.sx = e.clientX;
drag.sy = e.clientY;
},
check: function (e) {
let t = e.target;
if (t.dataset.moveable == "1")
drag.start(e);
}
};
document.addEventListener('mousedown', drag.check);
document.addEventListener('change', set_style_pos);
lines.textContent = "scroll me\n".repeat(100);
* {
box-sizing: border-box;
}
body {
margin: 0;
height: 100vh;
display: flex;
flex-direction: column;
color: #444;
font: 14px sans-serif;
}
label {
cursor: pointer;
}
.outer {
display: flex;
padding: 20px;
background: goldenrod;
/*flex-grow:1; Disable to control the height for presentaion*/
height:200px !important;
overflow:hidden; /*to hide scrollme lines*/
}
.inner {
position: relative;
overflow: scroll;
background: gray;
flex-grow: 1;
}
.box {
position: absolute;
width: 140px;
height: 150px;
background: silver;
box-shadow: 0 0 3px red;
cursor: move;
margin-left: 90px;
margin-top: -5px;
padding: 20px;
}
.box div {
font-weight: 700;
pointer-events: none;
text-align: center;
}
.prevent{
width:200px;
height:200px;
display:flex;
background-color:blue;
color:white;
justify-content:center;
align-items:center;
font-weight:bold;
/*--The solution--*/
z-index:1;
}
<div class="outer">
<div class="inner">
<div class="box" id="moveable" data-moveable="1">
<div>Move Me</div><br />
<label><input type="radio" name="p" value="absolute" checked />absolute</label><br />
<label><input type="radio" name="p" value="fixed" />fixed</label>
</div>
<pre id="lines"></pre>
</div>
</div>
<div class="prevent">
Prevent overlap
</div>
I hope this helps.
Use a sticky container and let children be absolute.
Had tested with z-index, all over, before posting but had not found any satisfactory solution that way.
I also tried various with position: sticky, and there is where I found the solution at last :)
One can wrap the sub windows in a sticky container which is positioned top left of the main container.
Pros:
Simple
Fairly clean HTML structure
The window stay below scroll-bars of container
Positioning relative to content wrapper
Cons:
If one want to make it non-fixed / non-sticy one have to move the element to parent and vice versa.
Absolute positioned children will not expand the container – thus not rearranging the DOM flow. (Which was the issue on earlier attempts using sticky).
Tested in FireFox, Chrome, Vivaldi, Opera Mini and Opera.
The core of it:
<div class="outer">
<div class="main">
<div class="wrap-sticky">
<div class="sub-window">
Fixed Window
</div>
</div>
Other "normal" content
</div>
</div>
And:
.outer {
overflow: hidden;
}
.main {
position: relative;
overflow: scroll;
}
.wrap-sticky {
position: sticky;
top: 0;
left: 0;
}
.sub-window {
position: absolute;
}
function get_pos (el) {
let cs = getComputedStyle(el);
return [
parseInt(cs.getPropertyValue('left')) || 0,
parseInt(cs.getPropertyValue('top')) || 0
];
}
function set_style_pos (e) {
let [x, y] = get_pos (moveable);
if (e.target.value == "sticky") {
wrap_sticky.appendChild(moveable);
moveable.style.left = (x - inner.scrollLeft) + 'px';
moveable.style.top = (y - inner.scrollTop) + 'px';
} else {
inner.appendChild(moveable);
moveable.style.left = (x + inner.scrollLeft) + 'px';
moveable.style.top = (y + inner.scrollTop) + 'px';
}
}
function halt (e) {
e.preventDefault();
e.stopPropagation();
}
const drag = {
el: null,
ex: 0,
ey: 0,
xs: 0,
ys: 0,
move: function (e) {
halt(e);
drag.el.style.left = (e.clientX - drag.sx + drag.ex) + 'px';
drag.el.style.top = (e.clientY - drag.sy + drag.ey) + 'px';
},
end: function (e) {
halt(e);
window.removeEventListener('mouseup', drag.end);
window.removeEventListener('mousemove', drag.move);
},
start: function (e) {
halt(e);
window.addEventListener('mouseup', drag.end);
window.addEventListener('mousemove', drag.move);
drag.el = e.target;
[drag.ex, drag.ey] = get_pos(drag.el);
drag.sx = e.clientX;
drag.sy = e.clientY;
},
check: function (e) {
let t = e.target;
if (t.dataset.moveable == "1")
drag.start(e);
}
};
document.addEventListener('mousedown', drag.check);
document.addEventListener('change', set_style_pos);
lines.textContent = "scroll me\n".repeat(100) + "horiz".repeat(100) + 'END';
* {
box-sizing: border-box;
}
body {
margin: 0;
height: 100vh;
display: flex;
flex-direction: column;
color: #444;
font: 14px sans-serif;
}
label {
cursor: pointer;
}
.outer {
display: flex;
padding: 20px;
background: goldenrod;
flex-grow: 1;
overflow: hidden;
}
.inner {
position: relative;
overflow: scroll;
background: gray;
flex-grow: 1;
}
.box {
position: absolute;
width: 160px;
height: 100px;
background: silver;
box-shadow: 0 0 3px red;
cursor: move;
padding: 20px;
top: 20px;
left: 20px;
}
.box div {
font-weight: 700;
pointer-events: none;
text-align: center;
}
.wrap-sticky {
position: sticky;
top: 0;
left: 0;
}
<div class="outer">
<div class="inner" id="inner">
<div class="wrap-sticky" id="wrap_sticky">
<div class="box" id="moveable" data-moveable="1">
<div>Drag & Move Me</div>
<label><input type="radio" name="p" value="sticky" checked />In sticky</label><br />
<label><input type="radio" name="p" value="absolute" />In main</label>
</div>
</div>
<pre id="lines"></pre>
</div>
</div>
I have the following HTML. The problem is that I can not make the div with id="ladder" have the same position with respect to its parent div with id="grid" when I zoom in or out. here is the code snakes and Ladders
var gameBoard = {
createBoard: function(dimension, mount) {
var mount = document.querySelector(mount);
if (!dimension || isNaN(dimension) || !parseInt(dimension, 10)) {
return false;
} else {
dimension = typeof dimension === 'string' ? parseInt(dimension, 10) : dimension;
var table = document.createElement('table'),
row = document.createElement('tr'),
cell = document.createElement('td'),
rowClone,
cellClone;
var output;
for (var r = 0; r < dimension; r++) {
rowClone = row.cloneNode(true);
table.appendChild(rowClone);
for (var c = 0; c < dimension; c++) {
cellClone = cell.cloneNode(true);
rowClone.appendChild(cellClone);
}
}
mount.appendChild(table);
output = gameBoard.enumerateBoard(table);
}
return output;
},
enumerateBoard: function(board) {
var rows = board.getElementsByTagName('tr'),
text = document.createTextNode(''),
rowCounter = 1,
size = rows.length,
cells,
cellsLength,
cellNumber,
odd = false,
control = 0;
for (var r = size - 1; r >= 0; r--) {
cells = rows[r].getElementsByTagName('td');
cellsLength = cells.length;
rows[r].className = r % 2 == 0 ? 'even' : 'odd';
odd = ++control % 2 == 0 ? true : false;
size = rows.length;
for (var i = 0; i < cellsLength; i++) {
if (odd == true) {
cellNumber = --size + rowCounter - i;
} else {
cellNumber = rowCounter;
}
cells[i].className = i % 2 == 0 ? 'even' : 'odd';
cells[i].id = cellNumber;
cells[i].appendChild(text.cloneNode());
cells[i].firstChild.nodeValue = cellNumber;
rowCounter++;
}
}
var lastRow = rows[0].getElementsByTagName('td');
lastRow[0].id = '100';
var firstRow = rows[9].getElementsByTagName('td');
firstRow[0].id = '1';
return gameBoard;
}
};
gameBoard.createBoard(10, "#grid");
function intialPosition() {
$("#1").append($("#player1"));
$("#1").append($("#player2"));
var currentPosition = parseInt($("#1").attr('id'));
return currentPosition;
}
var start = intialPosition();
var face1 = new Image();
face1.src = "http://s19.postimg.org/fa5etrfy7/image.gif";
var face2 = new Image();
face2.src = "http://s19.postimg.org/qb0jys873/image.gif";
var face3 = new Image();
face3.src = "http://s19.postimg.org/fpgoms1vj/image.gif";
var face4 = new Image();
face4.src = "http://s19.postimg.org/xgsb18ha7/image.gif";
var face5 = new Image();
face5.src = "http://s19.postimg.org/lsy96os5b/image.gif";
var face6 = new Image();
face6.src = "http://s19.postimg.org/4gxwl8ynz/image.gif";
function rollDice() {
var status = document.getElementById("status");
var random = Math.floor(Math.random() * 6) + 1;
document.images["mydice"].src = eval("face" + random + ".src")
status.innerHTML = "You rolled " + random;
//if (random == 6) {
// status.innerHTML += "! You get a free turn!!";
//}
return random;
}
var currentPosition = start;
var random1;
function move() {
var m = 1;
random1 = rollDice();
destination = currentPosition + random1;
$('#' + destination).fadeIn(100).fadeOut(100).fadeIn(100).fadeOut(100).fadeIn(100);
$('#' + destination).append($('#player' + m));
if (destination == 36) {
destination = 55;
$('#' + destination).append($('#' + "player" + m));
}
if (destination == 96) {
destination = 57;
$('#' + destination).append($('#' + "player" + m));
}
if (destination >= 100) {
destination = 100;
$('#' + destination).append($('#' + "player" + m));
alert("You Won!!!!!!!");
}
currentPosition = parseInt($('#' + destination).attr('id'));
return currentPosition;
}
/*body {
background-image: url('snakesandladder2.png');
background-repeat: no-repeat;
background-size: 100%;
background-color: #4f96cb;
}*/
#game {
width: 80%;
margin-left: auto;
margin-right: auto;
display: table;
}
#gameBoardSection {
border: 3px inset #0FF;
border-radius: 10px;
width: 65%;
display: table-cell;
position: relative;
/*margin: 5px;*/
/*margin: auto;*/
}
/*#grid{
position:relative;
}*/
table {
width: 100%;
position: relative;
}
td {
border-radius: 10px;
width: 60px;
height: 60px;
line-height: normal;
vertical-align: bottom;
text-align: left;
border: 0px solid #FFFFFF;
position: relative;
}
table tr:nth-child(odd) td:nth-child(even),
table tr:nth-child(even) td:nth-child(odd) {
/*color: #FF0000 ;*/
background-color: PowderBlue;
}
table tr:nth-child(even) td:nth-child(even),
table tr:nth-child(odd) td:nth-child(odd) {
background-color: SkyBlue;
}
#100 {
background-image: url('rotstar2_e0.gif');
background-repeat: no-repeat;
background-size: 100%;
}
#ladder {
position: absolute;
top: 300px;
left: 470px;
-webkit-transform: rotate(30deg);
z-index: 1;
opacity: 0.7;
}
#bigSnake {
position: absolute;
top: 20px;
left: 200px;
opacity: 0.7;
z-index: 1;
}
#playerAndDiceSection {
background-color: lightpink;
border: 1px;
border-style: solid;
display: table-cell;
/*margin: 2px;*/
border-radius: 10px;
border: 3px inset #0FF;
width: 35%;
margin: 2%;
}
<body>
<div id="game">
<div id="gameBoardSection">
<div id="grid"></div>
<div id="ladder">
<img src="http://s19.postimg.org/otai9he2n/oie_e_RDOY2iqd5o_Q.gif" />
</div>
<div id="bigSnake">
<img src="http://s19.postimg.org/hrcknaagz/oie_485727s_RN4_KKBG.png" />
</div>
<div id="player1" style="position:absolute; top:10px; left:10px;">
<!--style="position: absolute; top: 597px; z-index: 1;"-->
<img src="http://s19.postimg.org/t108l496n/human_Piece.png" />
</div>
<div id="player2" style="position:absolute; top:15px; left:5px;">
<img src="http://s19.postimg.org/l6zmzq1dr/computer_Piece.png" />
</div>
</div>
<div id="playerAndDiceSection">
<div id="playerSection">
<div id="player1">
<img src="http://s19.postimg.org/t108l496n/human_Piece.png" />Player1
<!--<p>Player1</p>-->
</div>
<div id="player2">
<img src="http://s19.postimg.org/l6zmzq1dr/computer_Piece.png" />Player2
</div>
</div>
<div id="diceSection">
<img src="http://s19.postimg.org/fa5etrfy7/image.gif" name="mydice" onclick="move()" style="background-color: white;">
<h2 id="status"></h2>
</div>
</div>
</div>
</body>