var in style.setProperty does not render - javascript

I have a progress bar which increases based on numbers entered by the user:
<div class="progressBarContainer percentBar">
<div class="progressBarPercent" style="--width:${gPercent}" id="${gName}-pbar"></div>
</div>
I use a variable in the style to define the width of the progress bar
.progressBarPercent {
background-color: var(--progressbar-main-color);
width: calc(var(--width, 0) * 1%); <== THIS
min-width: 10px;
max-width: calc(100% - 1px);
height: 17px;
border-radius: 15px;
}
This is how I try to update it:
document.getElementById(gName+"-pbar").style.setProperty('--width', calculatedPercent);
If I make a log, the new percentage is displayed correctly in the log, but the property of the element is not modified:
The element:
The element image
The log:
The log image

The code that you posted seems to work fine, the problem must be something else.
Here is the example working:
let progress = 0;
setInterval(() => {
if (++progress > 100) progress = 0;
document.getElementById("test").style.setProperty('--width', progress);
}, 100);
:root {
--progressbar-main-color: crimson;
}
.progressBarContainer {
width: 100%;
background-color: darkgray;
}
.progressBarPercent {
background-color: var(--progressbar-main-color);
width: calc(var(--width, 0) * 1%);
min-width: 10px;
max-width: calc(100% - 1px);
height: 17px;
border-radius: 15px;
}
<div class="progressBarContainer percentBar">
<div class="progressBarPercent" style="--width:0" id="test"></div>
</div>

Related

How to reverse the order of zindex in the whole site

I want to reverse the z-index of my website, so that objects with lower z-index are on top of objects with higher z-index - is there a solution for this ?
I don't know of any standard solutions, but you can always write a custom function to do it. Check if this works for you:
document.querySelector('button').addEventListener('click', reverseZIndex);
function reverseZIndex(){
// Get all body elements
Array.from(document.body.querySelectorAll('*'))
// Reverse those that have z-index
.forEach((elem, idx) => {
const zIndex = window.getComputedStyle(elem).getPropertyValue('z-index');
if (zIndex !== 'auto' && zIndex != 0) {
elem.style.zIndex = zIndex * -1;
}
})
}
button {
margin-bottom: 10px;
}
.container {
position: relative;
}
.z {
width: 50px;
height: 50px;
position: absolute;
left: 0;
}
.z1 {
z-index: 1;
background-color: green;
top: 0;
}
.z2 {
z-index: 2;
background-color: blue;
top: 20px;
left: 5px;
}
.z3 {
z-index: 3;
background-color: yellow;
top: 40px;
left: 10px;
}
.z4 {
z-index: 4;
background-color: grey;
top: 60px;
left: 15px;
}
.z5 {
z-index: 5;
background-color: red;
top: 80px;
left: 20px;
}
<button>Reverse z-index</button>
<div class="container">
<div class="z z1"></div>
<div class="z z2"></div>
<div class="z z3"></div>
<div class="z z4"></div>
<div class="z z5"></div>
</div>
One method that might work for your case is to dynamically, re-write the style sheet(s) using javascript.
Style sheets are html elements like any other and live references to them can be made using:
styleSheets = document.getElementsByTagName('style');
this creates a live html collection (see https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByTagName). If you have a single style sheet it is referenced in index[0], additional style sheets have incremented indices. Cricially, the innerHTML of the collection can be modified as for any other element and, because the reference is 'live', changes to it are automatically applied to the html tree displayed in the browser.
The following example takes the innerHTML of the style sheet and splits it into an array of lines. Array.map is used to examine each line for the presence of a z-index rule. If present, the numerical value following it is extracted and multiplied by -1 so the largest value becomes the smallest and vice versa. Other lines are left unaltered. The check and multiplication is performed using a ternary operator conditional, but you could equally loop through the lines with a for loop.
The results of the mapping are joined as new lines and the resulting string used to replace the innerHTML of the style sheet.
This working snippet applies a custom reverseZ function to the style sheet each time the page is clicked. If you have several style sheets, interation through the collection will achieve the same result.
document.addEventListener('click', reverseZ);
const styleSheet = document.getElementsByTagName('style')[0];
function reverseZ() {
const lines = styleSheet.innerHTML.split("\n");
const newLines = lines.map(line => {
return (line.indexOf('z-index') > -1) ? `z-index: ${parseInt(line.slice(line.indexOf(':')+1, line.indexOf(';')))*-1};`: line;
});
styleSheet.innerHTML = newLines.join("\n");
} // end function reverseZ
div {
position: absolute;
width: 100px;
aspect-ratio: 1;
border: 1px solid black;
display: flex;
align-items: center;
justify-content: center;
}
.red {
background: red;
z-index: -100;
top: 0;
left: 0;
}
.orange {
background: orange;
z-index: -70;
top: 20px;
left: 20px;
}
.yellow {
background: yellow;
z-index: -10;
top: 40px;
left: 40px;
}
.green {
background: green;
z-index: 0;
top: 60px;
left: 60px;
}
.blue {
background: blue;
z-index: 20;
top: 80px;
left: 80px;
}
.indigo {
background: indigo;
z-index: 50;
top: 100px;
left: 100px;
}
.violet {
background: violet;
z-index: 100;
top: 120px;
left: 120px;
}
<div class="red">click</div>
<div class="orange"></div>
<div class="yellow"></div>
<div class="green"></div>
<div class="blue"></div>
<div class="indigo"></div>
<div class="violet">click</div>

Creating health bar that increases in value based on users level

Hello I am trying to create a healthbar that scales in max value based on the users level.
But I am kinda stuck with it because everytime the healthbar ends up in not having the right length based on the users level and it's health. The system works like this: the user starts at level 1 with a health value of 150 and increases + 10 everytime the user levels up, the max level that exists is 32.
Now I know this might be possible to do with a loop but I am not sure on how do this correctly:
This is the code for the health bar. The user_level is the users level and I am trying to change the element style based on his health, but at the same time that it would match with the current level of the user.
for (let i = 0; i < user_level.value; i++) {
playerhealthbar.style.width = user_health // Something here but I dont know how to.
}
This is the CSS code if it helps. What happens is that the greenbar should shrink so that the red bar underneath becomes visible.
#playerhealth {
position: absolute;
bottom: 0;
left: 0;
height: 45px;
width: 325px;
background: lightgreen;
}
#playerhealthbar {
position: absolute;
height: 50px;
width: 330px;
border: rgb(255, 255, 255) 3px solid;
border-radius: 3px;
margin-right: 20px;
display: inline-block;
margin-top: 442px;
margin-left: 70px;
background: rgb(158, 31, 31);
}
#playerhealthvalue{
position: absolute;
margin-top: 500px;
margin-left: 220px;
font-size: 30px;
color: black;
}
The complete outerbar stays the same. But the greenbar thats inside the whole frame shrinks in size when the health goes down.
So the first thing you have to calculate is what the current maximum health value is. This is given by currentMaxHealth = 150 + 10 * (level-1).
The percent of the green bar is playerHealth / currentMaxHealth * 100.
The whole logic can be done with just custom properties calc and var.
So the CSS could look like this:
function setCurrentHealth(val) {
let root = document.documentElement;
root.style.setProperty('--curr-health', val);
}
function setUserLevel(level) {
let root = document.documentElement;
root.style.setProperty('--user-level', level);
}
document.querySelector('#level').addEventListener('input', (evt) => {
setUserLevel(evt.target.value)
})
document.querySelector('#health').addEventListener('input', (evt) => {
setCurrentHealth(evt.target.value)
})
:root {
--user-level: 1;
--curr-health: 10;
--base-health-level: 150;
--additional-health-per-level: 10;
}
.current-health {
width: calc(var(--curr-health) / (var(--base-health-level) + var(--additional-health-per-level) * (var(--user-level) - 1)) * 100%);
height: 100%;
background-color: green;
}
.health-bar {
border: 1px solid black;
width: 300px;
height: 20px;
}
<div class="health-bar">
<div class="current-health">
</div>
</div>
Level: <input value="1" id="level"><br>
Health: <input value="10" id="health">

Not able to make element stick to the bottom when offscreen in IE 11, just like in css sticky

I am trying to replicate the same behavior as in this
codepen in IE 11 (does not have css sticky)
I am able to detect when the item is offscreen at the start with:
if (
$(".main-content").height() + $(".main-content").offset().top <
$(".main-footer").offset().top
)
but then after it reaches the end of the scroll (in this case the page), I did not manage to check when it goes offscreen again. It is probably something simple as subtracting the scroll to figure out if the element is offscreen, I am just stuck...
Here is a codepen where I stuck am now.
IE doesn't support <main> so you can't use this tag in IE 11. You can monitor the scroll bar changes through JavaScript, and then change its class according to the position of the element.
Here is the code you can refer to:
$(document).scroll(function() {
var scroH = $(document).scrollTop();
var viewH = $(window).height();
var contentH = $(document).height();
$('.main-footer').addClass('main-footer1')
if (scroH > 100) {}
if (contentH - (scroH + viewH) <= 100) { // The height from the bottom is less than 100px
}
if (contentH <= (scroH + viewH + 100)) {
$('.main-footer').removeClass('main-footer1')
$('.main-footer').addClass('main-footer2')
} else {
$('.main-footer').addClass('main-footer1')
$('.main-footer').removeClass('main-footer2')
}
});
body {
color: #fff;
font-family: arial;
font-weight: bold;
font-size: 40px;
}
.main-container {
max-width: 600px;
margin: 0 auto;
border: solid 10px green;
padding: 10px;
margin-top: 40px;
}
.main-container * {
padding: 10px;
background: #aaa;
border: dashed 5px #000;
}
.main-container *+* {
margin-top: 20px;
}
.main-header {
height: 50px;
background: #aaa;
}
.main-content {
min-height: 1000px;
}
.main-footer {
border-color: red;
}
.main-footer1 {
position: fixed;
bottom: 0px;
margin: 0 auto;
width: 570px;
}
.main-footer2 {
position: relative;
margin-top: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="main-container">
<header class="main-header">HEADER</header>
<div class="main-content">MAIN contentH</div>
<footer class="main-footer">footer</footer>
</div>
Result in IE 11:

Compounding Value within an object used to slide dot across the page incrementally

I am unable to get a variable to function properly as the translateX value within my object. I am wanting to make the dot scroll across the page each time the next button is clicked. My code is only able to move it back and forth for the first step.
I am new to the animation API, and I have already made this work with CSS transitions but I am trying to get a good handle on the API.
html:
<div class="progress__container">
<div class="progress__bar">
<div id="progress__fill" class="step1"></div>
<div class="circ" id="circ__1"></div>
<div class="circ" id="circ__2"></div>
<div class="circ" id="circ__3"></div>
<div class="circ" id="circ__4"></div>
<div id="progress__dot" class="prog__1"></div>
</div>
<div class="backBar"></div>
<div class="flexrow">
<span class="stepName">Account</span>
<span class="stepName">Frequency</span>
<span class="stepName">Amount</span>
<span class="stepName">Taxes</span>
</div>
<div class="button__container">
<button class="buttonStep" id="back">Back</button>
<button class="buttonStep is-active" id="next">Next</button>
</div>
</div>
js:
// give a starting value for the transformation
var startVal = 0;
// define the keyframes
var moveDot = [
{ transform: `translateX(${startVal}px)`},
{ transform: `translateX(${startVal + 190}px)`}
];
// definte the timing
var dotTiming = {
duration: 400,
fill: "forwards",
easing: 'ease-in',
}
// make the animation happen
var movingDot = document.getElementById("progress__dot").animate(
moveDot,
dotTiming
);
// pause the animation until called
movingDot.pause();
// on click fire the animation
document.getElementById('next').addEventListener('click', function() {
movingDot.playbackRate = 1;
if (startVal <= 380) {
movingDot.play();
startVal += 190;
}
});
document.getElementById('back').addEventListener('click', function() {
movingDot.playbackRate = -1;
if (startVal >= 0) {
movingDot.play();
startVal -= 190;
}
});
css:
#progress__fill {
height:2px;
position: absolute;
top: 7px;
left: 0;
background-color: darkred;
}
#progress__dot {
background-color: darkred;
color: #fff;
border-radius: 50%;
height: 8px;
width: 8px;
position: absolute;
text-align:center;
line-height: 8px;
padding: 6px;
top: 0;
font-size: 12px;
}
/* Static Bar Elements */
.progress__container {
width: 600px;
margin: 20px auto;
position: relative;
}
.backBar {
height:2px;
width:96%;
position: absolute;
top: 7px;
left: 2%;
background-color: lightgrey;
}
.progress__bar {
z-index: 100;
position: relative;
width: 96%;
margin: 0 auto;
}
.circ {
background-color: #fff;
border: 2px solid lightgrey;
border-radius: 50%;
height: 12px;
width: 12px;
display: inline-block;
}
#circ__2, #circ__3 {
margin-left: 30%
}
#circ__4 {
float: right;
}
.passed {
background-color: darkred;
border: 2px solid darkred;
}
.hide {
visibility: hidden
}
.flexrow {
display: flex;
flex-direction: row;
justify-content: space-between;
}
/* Buttons */
.buttonStep {
background: grey;
color: #fff;
padding: 10px 25px;
border-radius: 10px;
font-size: 16px;
}
#back {
float: left;
}
#next {
float: right;
}
.is-active {
background: darkred;
}
The way I have it set up, I expect for the translateX values to increment or decrement depending on the click event listeners which would make the circle slide across the page. What is actually happening is that only the first step works. it will not go past the first stop point. If I log moveDot in the console it gives me the values that I am expecting, but it will only start/stop at 0 and 190. the back button functions the same way. link to fiddle
It is animated from and to the same place every time. Move the definition of moveDot into the event listener:
// give a starting value for the transformation
var startVal = 0;
// definte the timing
var dotTiming = {
duration: 400,
fill: "forwards",
easing: 'ease-in',
}
// on click fire the animation
document.getElementById('next').addEventListener('click', function() {
if (startVal > 380){return;}
// define the keyframes
var moveDot = [{transform: `translateX(${startVal}px)`},
{transform: `translateX(${startVal + 190}px)`}];
// make the animation happen
var movingDot = document.getElementById("progress__dot").animate(
moveDot,
dotTiming
);
movingDot.playbackRate = 1;
movingDot.play();
startVal += 190;
});
document.getElementById('back').addEventListener('click', function() {
movingDot.playbackRate = -1;
if (startVal >= 0) {
movingDot.play();
startVal -= 190;
}
});

responsive height of div: shrink to a breakpoint then start growing again

I'm using an hero section to show some content.
It's responsive using the padding-bottom percentage technique and an inner absolute positioned container to center the content.
Now the catch: reaching a breakpoint, let's say 768px, and on lower window size I would like the box to start growing again.
I found some js/jQuery code around the web and was able to get the result but it only works if I load the page when the window is <768px. In that case it works brilliantly. But if the page is loaded in a larger window the below 768px resizing get lost.
This is the html:
<div class="row row-home-hero" id="hero">
<div class="cont">
<h1>Title</h1>
<h2>Subtitle</h2>
<div class="cta-hero-home">
» CTA1
<span class="cta-hero-spacer">or</span>
» CTA2
</div>
</div>
</div>
This is the JS.
It's a mess since it's a mix from different sources.
And I'm using Wordpress so I've to replace some $ with jQuery.
Please forgive me :)
function screenClass() {
if(jQuery(window).innerWidth() < 768) {
jQuery('.row-home-hero').addClass('small-hero');
} else {
jQuery('.row-home-hero').removeClass('small-hero');
jQuery('.row-home-hero').css("height", "");
}
}
// Fire.
screenClass();
// And recheck if window gets resized.
jQuery(window).bind('resize',function(){
screenClass();
});
if (document.documentElement.clientWidth < 768) {
var $li = jQuery('.small-hero'), // Cache your element
startW = $li.width(); // Store a variable reference
function setMarginDiff() {
area = 500000;
width = jQuery('.small-hero').width();
jQuery('.small-hero').height(Math.ceil(area/width/1.7));
}
setMarginDiff(); // Do on DOM ready
jQuery(window).resize(setMarginDiff); // and on resize
}
And this is the CSS
.row-home-hero {
background-position: center;
-webkit-background-size: cover;
background-size: cover;
background-repeat: no-repeat;
position: relative;
background-color: red;
}
.row-home-hero:before {
display: block;
content: "";
width: 100%;
padding-top: 46%;
}
.row-home-hero .cont {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 40%;
text-align: center;
}
a.cta-hero-link {
display: block;
width: 100px;
max-width: 80%;
line-height: 40px;
background: white;
color: #1b9fdd;
text-transform: uppercase;
text-decoration: none;
margin: 10px auto;
text-align: center;
font-weight: 500;
box-shadow: 0px 0px 7px 1px rgba(0,0,0,.4);
}
#media screen and (max-width: 768px) {
.row-pre-footer .cont div {
width: 100%;
padding: 0 5%;
float: none;
margin: 0 auto 30px;
}
.progetto-footer, .loghi-footer {
width: 100%;
max-width: 320px;
margin: 0 auto 30px;
float: none;
}
.double-box .tib-tab {
float: none;
width: 90%;
margin: 5% auto;
padding-bottom: 90%;
}
.tib-box h2, .tab-box h2 {
font-size: calc(28px + (46 - 28) * (100vw - 320px) / (768 - 320));
margin-bottom: 18vw;
}
.double-box-inner p {
font-size: 22px;
line-height: 30px;
}
.row-home-hero.small-hero {
height: 500px;
}
.row-home-hero:before {
display: block;
content: "";
width: 100%;
padding-top: 0;
}
}
And this is a working demo
Thanks!
I moved the if (document.documentElement.clientWidth < 768) { block inside the resize event. So that it gets called whenever the window is resized.
In the original version, it would only get called when the page was loaded (and only if the screen was smaller than 768). With this adjustment, it will always be rechecked when resized.
I also merged all your code into one smaller function.
var breakpoint = 768
var area = 500000
var target = $('.row-home-hero')
$(window).bind('resize', function() {
if(window.innerWidth < breakpoint) {
var width = target.width()
target.addClass('small-hero')
target.height(Math.ceil(area / width / 1.7))
} else {
target.removeClass('small-hero')
target.css('height', '')
}
})
.trigger('resize')

Categories

Resources