I want to have a webpage whose entire viewable area is filled with divs. I am currently using the following code:
var wh= window.innerHeight;
var ww= window.innerWidth;
var area= wh * ww;
i= 1;
while(area > 0) {
document.getElementById("map").innerHTML+= "<div class='map-box' id='box" + i + "'></div>";
area-= 20 * 20;
i+=1;
}
.map-box {width: 20px; height: 20px; border-color: grey; border-width: 1px; border-style: solid; display: inline-block; margin: 0; padding: 0;}
<body>
<div id='map'></div>
</body>
If you try to use this code is your browser, you will see that there are two flaws in this:
First, it creates too many extra divs which go outside the viewable screen.
Second, this code is also somewhat slow.
Can someone here help me address both of these flaws and also optimize this code for faster performance?
1.) That <div> is not 20x20, because of the border:
let d = document.getElementById("test");
console.log(d.offsetWidth, d.offsetHeight);
.map-box {
width: 20px;
height: 20px;
border-color: grey;
border-width: 1px;
border-style: solid;
display: inline-block;
margin: 0;
padding: 0;
}
<div id="test" class="map-box"></div>
2.) There's still the default border around the entire thing, and also some spacing between the lines:
var wh = window.innerHeight;
var ww = window.innerWidth;
var area = wh * ww;
i = 1;
while (area > 0) {
document.getElementById("map").innerHTML += "<div class='map-box' id='box" + i + "'></div>";
area -= 22 * 22; // hardcoding is not that nice
i += 1;
}
.map-box {
width: 20px;
height: 20px;
border-color: grey;
border-width: 1px;
border-style: solid;
display: inline-block;
margin: 0;
padding: 0;
}
#map {
background: blue;
}
body {
background: red;
}
<div id='map'></div>
3.) Half cells are evil, so the width/height should be rounded downwards to a multiple of 22. Suddenly the grid is becoming an actual rectangle, at least in Chrome/Edge. The between-spacing is still a problem:
var wh = Math.floor(window.innerHeight / 22) * 22; // <--!!
var ww = Math.floor(window.innerWidth / 22) * 22; // <--!!
var area = wh * ww;
i = 1;
while (area > 0) {
document.getElementById("map").innerHTML += "<div class='map-box' id='box" + i + "'></div>";
area -= 22 * 22;
i += 1;
}
.map-box {
width: 20px;
height: 20px;
border-color: grey;
border-width: 1px;
border-style: solid;
display: inline-block;
margin: 0;
padding: 0;
}
#map {
background: blue;
}
body {
background: red;
margin: 0; // <--!!
padding: 0; // <--!!
}
<div id='map'></div>
I don't actually know how to use line-height properly, this one works on my machine with my scaling/DPI, in Chrome/Edge, but that's all I can say about it. The 22-s are cut back, area now simply stores the number of <div>s to generate.
var wh = Math.floor(window.innerHeight / 22);
var ww = Math.floor(window.innerWidth / 22);
var area = wh * ww;
i = 1;
while (area > 0) {
document.getElementById("map").innerHTML += "<div class='map-box' id='box" + i + "'></div>";
area--;
i += 1;
}
.map-box {
width: 20px;
height: 20px;
border-color: grey;
border-width: 1px;
border-style: solid;
display: inline-block;
margin: 0;
padding: 0;
}
#map {
line-height: 0.6;
}
body {
margin: 0;
padding: 0;
}
<div id='map'></div>
Instead of accessing dom element's inner html on each loop iteration - do it once after the loop with "prepared" data to set there
const wh = window.innerHeight;
const ww = window.innerWidth;
let area = wh * ww;
i = 1;
const ms = Date.now();
const divs = [];
while (area > 0) {
divs.push("<div class='map-box' id='box" + i + "'></div>");
area -= 20 * 20;
i += 1;
}
document.getElementById("map").innerHTML = divs.join("");
console.log("done fast", Date.now() - ms);
js fiddle with comparison https://jsfiddle.net/aL7zqwy9/
The final solution, not ideal but
<html>
<body>
<div id='map'></div>
</body>
<style>
body {
margin: 0;
padding: 0;
/* Overflow appears when last row is added and shrinks the "width" */
overflow-y: hidden;
}
#map {
/* To exclude space between rows */
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.map-box {
width: 20px;
height: 20px;
border: 1px solid grey;
display: block;
margin: 0;
padding: 0;
/* So border thickness will not affect element size */
box-sizing: border-box;
}
</style>
<script>
const cellSize = 20; // px
const wh = window.innerHeight;
const ww = window.innerWidth;
// not always divisible by cell size without a remainder
const columnsCount = Math.floor(ww / cellSize);
const rowsCount = Math.floor(wh / cellSize);
const cellsCount = columnsCount * rowsCount;
console.log(`wh: ${wh}, ww: ${ww}, cols: ${columnsCount}, rows: ${rowsCount}`);
const divs = [];
for (let i = 0; i < cellsCount; i++) {
divs.push(`<div class='map-box' id='box${i}'></div>`);
}
document.getElementById("map").innerHTML = divs.join("");
</script>
</html>
Related
The title says it all. To see the issue, copy this code to the following online compiler: https://www.w3schools.com/php/phptryit.asp?filename=tryphp_compiler
<!DOCTYPE HTML>
<html>
<style>
/*MAIN*/
* {
margin: 0;
padding: 0;
user-select: none;
overflow: hidden;
}
body {
background-color: #FF0000;
margin: 0;
padding: 0;
}
/*ELEMENTS*/
div {
width: 100vw;
height: 100vh;
float: left;
margin-left: 0vw;
}
h1 {
font-family: verdana;
font-size: 5vh;
text-transform: uppercase;
}
h1.white {
color: #F4F4F4;
}
</style>
<body>
<div id = "main" style = "width: auto; margin-left: 0vw;">
<div id = "home" class = "container" style = 'background-color: #000000;'>
<h1 class = "white">click arrow to see how the next page doesn't appear until after the transition is complete</h1>
<!--ARROW BUTTON-->
<p id = 'arrowButton' style = 'color: #FFFFFF; position: absolute; height: 10vh; width: auto; margin: 45vh 0 0 75vw; font-size: 3vh;' onMouseDown = 'NextButtonClick();'>--></p>
</div>
<div id = "welcome" class = "container" style = 'background-color: #FFFFFF;'>
<h1 style = 'margin: 47.5vh 0 0 50vw'>welcome to my portfolio</h1>
</div>
</div>
<script>
var mainDiv, welcomeDiv;
var transitionSeconds = 0.5;
var isTransitioning = false;
function NextButtonClick() {
if(!isTransitioning) {
isTransitioning = true;
i = 0;
thisInterval = setInterval(function() {
mainDiv.style.marginLeft = (100 / i) - 101 + "vw";
i++;
if(i == 100) {
clearInterval(thisInterval);
mainDiv.style.marginLeft = "-100vw";
isTransitioning = false;
}
}, transitionSeconds);
}
}
window.onload = function() {
mainDiv = document.getElementById("main");
welcomeDiv = document.getElementById("welcome");
var arrowButton = document.getElementById("arrowButton");
var arrowButtonX, arrowButtonY;
var arrowButtonGlowDistance = 100;
arrowButtonX = arrowButton.getBoundingClientRect().left + arrowButton.getBoundingClientRect().width/2;//center
arrowButtonY = arrowButton.getBoundingClientRect().top + arrowButton.getBoundingClientRect().height/2;//center
document.onmousemove = function(e) {
x = e.clientX; y = e.clientY;
};
};
</script>
</body>
</html>
The background is red on purpose so that you can see how, even though the "welcome" div should be rendered over top the background, it is not being rendered until the very last second after the transition is completed and 100% of the element is on the screen.
I am stumped, and I'm not sure why this is since HTML usually doesn't seem to behave this way. Even when I highlight the element in Inspect Element, the Inspector doesn't show me where the element is on the screen until the final moment when it is rendered.
Any help would be greatly appreciated, and I look forward to hearing your feedback!
The problem here is that your DIVs are placed under each other and while one is moving horizontally, the next div is still underneath of it until first one is completely out of the way (just like Jenga game in reverse).
To solve this, you can try add display: flex, to place them horizontally instead:
var mainDiv, welcomeDiv;
var transitionSeconds = 0.5;
var isTransitioning = false;
function NextButtonClick() {
if (!isTransitioning) {
isTransitioning = true;
i = 0;
thisInterval = setInterval(function() {
mainDiv.style.marginLeft = (100 / i) - 101 + "vw";
i++;
if (i == 100) {
clearInterval(thisInterval);
mainDiv.style.marginLeft = "-100vw";
isTransitioning = false;
}
}, transitionSeconds);
}
}
window.onload = function() {
mainDiv = document.getElementById("main");
welcomeDiv = document.getElementById("welcome");
var arrowButton = document.getElementById("arrowButton");
var arrowButtonX, arrowButtonY;
var arrowButtonGlowDistance = 100;
arrowButtonX = arrowButton.getBoundingClientRect().left + arrowButton.getBoundingClientRect().width / 2; //center
arrowButtonY = arrowButton.getBoundingClientRect().top + arrowButton.getBoundingClientRect().height / 2; //center
document.onmousemove = function(e) {
x = e.clientX;
y = e.clientY;
};
};
* {
margin: 0;
padding: 0;
user-select: none;
overflow: hidden;
}
body {
background-color: #FF0000;
margin: 0;
padding: 0;
}
/*ELEMENTS*/
div {
width: 100vw;
height: 100vh;
float: left;
margin-left: 0vw;
display: flex; /* added */
}
h1 {
font-family: verdana;
font-size: 5vh;
text-transform: uppercase;
}
h1.white {
color: #F4F4F4;
}
<div id="main" style="width: auto; margin-left: 0vw;">
<div id="home" class="container" style='background-color: #000000;'>
<h1 class="white">click arrow to see how the next page doesn't appear until after the transition is complete</h1>
<!--ARROW BUTTON-->
<p id='arrowButton' style='color: #FFFFFF; position: absolute; height: 10vh; width: auto; margin: 45vh 0 0 75vw; font-size: 3vh;' onMouseDown='NextButtonClick();'>--></p>
</div>
<div id="welcome" class="container" style='background-color: #FFFFFF;'>
<h1 style='margin: 47.5vh 0 0 50vw'>welcome to my portfolio</h1>
</div>
</div>
The script creates a random number of divs (in range 20-40) and puts some text in every single div. The script calculates the width of divs so that they fit in a single row. The height should be equal to the width - every div must be a square. Here's the code:
var quantity = Math.floor(Math.random() * (40 - 20 + 1)) + 20;
for (var i = 0; i < quantity; i++) {
var elem = document.createElement("div");
elem.className = "part";
elem.id = 'p' + i;
document.getElementById("scale").appendChild(elem);
}
var parts = document.getElementsByClassName("part");
for (var i = 0; i < parts.length; i++) {
parts[i].style.fontSize = (500 / quantity) + 'px';
parts[i].style.lineHeight = (460 / quantity) + 'px';
parts[i].textContent = ("block #" + (i + 1));
}
for (var i = 0; i < parts.length; i++) {
parts[i].style.height = parts[i].style.width;
}
let text = document.getElementById('txt');
text.textContent = 'BLOCKS: ' + quantity;
body {
margin: 0;
}
#scale {
position: absolute;
display: table;
width: 100%;
top: 50%;
transform: translateY(-100%);
table-layout: fixed;
border-spacing: 1px;
}
.part {
display: table-cell;
background-color: #a9cce3;
padding: 3px;
box-sizing: border-box;
border: 1px solid black;
border-radius: 2px;
overflow: hidden;
}
#txt {
position: absolute;
top: 50%;
left: 50%;
margin-top: 20px;
transform: translateX(-50%);
font-size: 18px;
font-family: 'Arial', sans-serif;
}
<div id="scale"> </div>
<div id='txt'> </div>
The first problem is the divs are not always square. The second problem is I can't properly set the font size depending on a div size, so text fits a div. I think that's the worst solution
parts[i].style.fontSize = (500 / quantity) + 'px';
parts[i].style.lineHeight = (460 / quantity) + 'px';
There were two problems with the code:
To get element with use .clientWidth because style.width can be accessed when you have set that but in your code, I couldn't see you are doing so.
Second use font size in per cent, I don't think line-height is required. To make it centre use CSS as in example follows.
Use following:
var quantity = Math.floor(Math.random() * (40 - 20 + 1)) + 20;
for (var i = 0; i < quantity; i++) {
var elem = document.createElement("div");
elem.className = "part";
elem.id = 'p' + i;
document.getElementById("scale").appendChild(elem);
}
var parts = document.getElementsByClassName("part");
for (var i = 0; i < parts.length; i++) {
parts[i].style.fontSize = (500 / quantity) + '%';
parts[i].style.lineHeight = (460 / quantity) + '%';
parts[i].textContent = ("block #" + (i + 1));
}
for (var i = 0; i < parts.length; i++) {
parts[i].style.height = parts[i].clientWidth + "px";
}
let text = document.getElementById('txt');
text.textContent = 'BLOCKS: ' + quantity;
body {
margin: 0;
}
#scale {
position: absolute;
display: table;
width: 100%;
top: 50%;
transform: translateY(-100%);
table-layout: fixed;
border-spacing: 1px;
}
.part {
display: table-cell;
background-color: #a9cce3;
padding: 3px;
box-sizing: border-box;
border: 1px solid black;
border-radius: 2px;
overflow: hidden;
vertical-align: middle;
text-align: center;
}
#txt {
position: absolute;
top: 50%;
left: 50%;
margin-top: 20px;
transform: translateX(-50%);
font-size: 18px;
font-family: 'Arial', sans-serif;
}
<div id="scale"> </div>
<div id='txt'> </div>
Or if you don't want to use your text font to be reduced like this use following:
.part {
display: table-cell;
background-color: #a9cce3;
padding: 3px;
box-sizing: border-box;
border: 1px solid black;
border-radius: 2px;
overflow: hidden;
vertical-align: middle;
text-align: center;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
Hi I am trying to make the columns and rows in the mainContent div but the problem is the it is getting out of the mainContent after 2-3 clicks. I want it to remain inside and should create equally sized columns and rows inside of it. here is my code.
var test2 = document.getElementById('btn');
test2.addEventListener('click', function() {
console.log('clicked');
var contain = document.getElementById('contentArea'); // for selecting id
var newGriding = document.createElement('div');
newGriding.setAttribute('id', 'grid');
contain.appendChild(newGriding);
});
#contentArea {
background-color: #babab3;
height: 74vh;
margin: 0 auto;
}
#grid {
margin: 0;
padding: 0;
border: none;
outline: 5px dashed #aba4a4;
display: inline-block;
height: 100%;
width: 50%;
}
<div id="contentArea">
</div>
<button id="btn">
create
</button>
Because you are using fixed height for the appending element.
You should resize the element after every click using some logic or you can use the display of your parent as flex and flex wrap true.
var test2 = document.getElementById('btn');
test2.addEventListener('click', function() {
var contain = document.getElementById('contentArea'); // for selecting id
var newGriding = document.createElement('div');
newGriding.setAttribute('id', 'grid');
contain.appendChild(newGriding);
});
#contentArea {
background-color: #babab3;
height: 74vh;
margin: 0 auto;
display: flex;
flex-wrap: wrap;
}
#grid {
margin: 0;
padding: 0;
border: none;
outline: 5px dashed #aba4a4;
display: inline-block;
width: 50%;
}
<div id="contentArea">
</div>
<button id="btn">create</button>
or
var test2 = document.getElementById('btn');
test2.addEventListener('click', function() {
var contain = document.getElementById('contentArea'); // for selecting id
var newGriding = document.createElement('div');
newGriding.setAttribute('id', 'grid');
contain.appendChild(newGriding);
resizeDiv();
});
var maxInRow = 2;
function resizeDiv() {
var allGrids = document.querySelectorAll("#contentArea > #grid");
var width = 100 / maxInRow;
var len = allGrids.length;
var colNo = Math.floor(len / maxInRow);
colNo = colNo - (len / maxInRow) == 0 ? colNo : colNo + 1;
var height = 100 / colNo;
for (var i = 0; i < len; i++) {
allGrids[i].style.width = width + "%";
//"calc(" + width + "% - 10px)"; --- if doesn't want box-sizing to be borderbox
allGrids[i].style.height = height + "%";
//"calc(" + height + "% - 10px)"; --- if doesn't want box-sizing to be borderbox
//reduce the size of box which increased due to outline
}
}
#contentArea {
background-color: #babab3;
height: 74vh;
margin: 0 auto;
position: relative;
}
#grid {
margin: 0;
padding: 0;
border: 5px dashed #aba4a4;
display: inline-block;
height: 100%;
width: 50%;
position: relative;
box-sizing: border-box;
}
<div id="contentArea">
</div>
<button id="btn">
create
</button>
I want this div to go from point A to point B and vice-versa. Problem is the dist variable (distance) never gets to 0, it is usually either 1 or -1 (or some other range of values), which results in the div getting stuck and not going anywhere. In my complete code, point A and B positions are randomized every time I open the page.
The example below is reproducing the problem. To see it working as intended set #pointA { top: 5px; left: 5px; } | #pointB { top: 5px; left: 105px; }
var score = 0, points = 0, boxMode = 0, speed, speedX, speedY, angleRadians,
distX, distY, dist;
var destinationX = $("#pointB").position().left;
var destinationY = $("#pointB").position().top;
var pointAPos = $("#pointA").position();
var pointAx = pointAPos.left;
var pointAy = pointAPos.top;
var pointBPos = $("#pointB").position();
var pointBx = pointBPos.left;
var pointBy = pointBPos.top;
var boxPos = $('#box').position();
var boxX = pointAx;
var boxY = pointAy;
function calculateDistance(x, y) {
distX = boxX - x;
distY = boxY - y;
dist = Math.sqrt((distX*distX)+(distY*distY));
}
function boxMove() {
angleRadians = Math.atan2(-distX, -distY);
speed = 2;
speedX = speed * Math.sin(angleRadians);
speedY = speed * Math.cos(angleRadians);
document.getElementById("box").style.left = boxX + "px";
document.getElementById("box").style.top = boxY + "px";
boxX += speedX;
boxY += speedY;
}
function boxAI() {
calculateDistance(destinationX, destinationY);
if (!dist == 0) {
boxMove();
} else {
if (boxMode == 0) {
points += 1;
if (points == 50) {
// change destination to point A
destinationX = pointAx;
destinationY = pointAy;
boxMode = 1;
}
} else {
// change destination to point B
destinationX = pointBx;
destinationY = pointBy;
score += points;
points = 0;
boxMode = 0;
}
}
}
function mainLoop() {
boxAI();
$("#debug").html(points + " " + score + " " + boxMode);
requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);
#levelWrapper {
position: relative;
width: 400px;
height: 150px;
top: 2px;
margin: auto;
border: 1px solid red;
}
#pointA {
position: absolute;
background: beige;
width: 45px;
height: 45px;
top: 3px;
left: 50px;
margin: auto;
}
#pointB {
position: absolute;
background: beige;
width: 45px;
height: 45px;
top: 45px;
left: 195px;
margin: auto;
}
#box {
position: absolute;
background: black;
height: 5px;
width: 5px;
}
#debug {
position: relative;
background: white;
width: 250px;
height: 120px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div id="debug"></div>
<div id="levelWrapper">
<div id="pointA">A</div>
<div id="pointB">B</div>
<div id="box"></div>
</div>
<script src="jquery-3.2.1.js"></script>
<script type="text/javascript" src="woods_2.js"></script>
</body>
In JavaScript the ! operator has precedence over the ==, so if (dist != 0) would already be an improvement.
But as you use a speed variable which determines the difference between two consecutive measurements of the distance, you should make your test accordingly, and see if the distance is one step removed from zero:
if (dist >= speed)
Here is your code with some changes:
var score = 0, points = 0, boxMode = 0, speed, speedX, speedY, angleRadians,
distX, distY, dist;
var destinationX = $("#pointB").position().left;
var destinationY = $("#pointB").position().top;
var pointAPos = $("#pointA").position();
var pointAx = pointAPos.left;
var pointAy = pointAPos.top;
var pointBPos = $("#pointB").position();
var pointBx = pointBPos.left;
var pointBy = pointBPos.top;
var boxPos = $('#box').position();
var boxX = pointAx;
var boxY = pointAy;
var speed = 2; // make speed visible to other functions
function calculateDistance(x, y) {
distX = boxX - x;
distY = boxY - y;
dist = Math.sqrt((distX*distX)+(distY*distY));
}
function boxMove() {
angleRadians = Math.atan2(-distX, -distY);
speedX = speed * Math.sin(angleRadians);
speedY = speed * Math.cos(angleRadians);
document.getElementById("box").style.left = boxX + "px";
document.getElementById("box").style.top = boxY + "px";
boxX += speedX;
boxY += speedY;
}
function boxAI() {
calculateDistance(destinationX, destinationY);
// As the distance makes jumps of <speed> units,
// check whether it is within one step of the target
if (dist >= speed) {
boxMove();
} else {
score++; // Not sure what score should be measuring
if (boxMode == 0) {
// Change destination to point A
destinationX = pointAx;
destinationY = pointAy;
boxMode = 1;
} else {
// Change destination to point B
destinationX = pointBx;
destinationY = pointBy;
boxMode = 0;
}
}
}
function mainLoop() {
boxAI();
$("#debug").html(points + " " + score + " " + boxMode);
requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);
#levelWrapper {
position: relative;
width: 400px;
height: 150px;
top: 2px;
margin: auto;
border: 1px solid red;
}
#pointA {
position: absolute;
background: beige;
width: 45px;
height: 45px;
top: 3px;
left: 50px;
margin: auto;
}
#pointB {
position: absolute;
background: beige;
width: 45px;
height: 45px;
top: 45px;
left: 195px;
margin: auto;
}
#box {
position: absolute;
background: black;
height: 5px;
width: 5px;
}
#debug {
position: relative;
background: white;
width: 250px;
height: 120px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="debug"></div>
<div id="levelWrapper">
<div id="pointA">A</div>
<div id="pointB">B</div>
<div id="box"></div>
</div>
I did not understand what you wanted to do with score and points, so you will need to modify the code in that respect. For now I removed the changes to the points variable, and only increased score with every bounce.
I'm looking to create a horizontal chart with thresholds at certain percentages- if you've ever used Battle.Net, kind of similar to their download bar:
Blizzard Download Bar
I'm sure there's a name for it, I just don't know what it is...
Thanks!
I made this just in case it helps. Have 'fun'.
You can edit the little threshold pointer so they look stylish with css. But yea that all depends on what you want to do
// Btw, I'm a big fan of the canvas element that's why I'm doing this. Otherwise, you can also use a progress bar
var c = document.getElementById('progressBar'),
dt = document.getElementById('download-text'),
btn = document.querySelector('button'),
p2 = document.querySelector('progress');
var cx = c.getContext('2d');
var counter = 0,
loop;
btn.onclick = function() {
btn.innerText = 'N';
var timeInter = setInterval(function(){
btn.innerText += 'o';
},70);
setTimeout(function(){
clearTimeout(timeInter);
btn.hidden = true;
p2.hidden = false;
progress2();
},1000);
}
var loop2 = '',
counter2 = 0;
function progress2() {
counter2++;
var percentage = (counter2 / 17).toFixed(2);
if (percentage - 0 >= 100) {
percentage = 100;
}
p2.value = percentage - 0 +'';
if (percentage - 0 > 100) {
cancelAnimationFrame(loop2);
} else {
loop2 = requestAnimationFrame(progress);
}
//This is me failing to animate the progress bar
}
function progress() {
counter++;
var percentage = (counter / 17).toFixed(2);
if (percentage >= 100) {
percentage = 100+'.00';
}
dt.innerText = "Download: " + percentage + "%";
cx.fillStyle = '#00FFFF';
cx.fillRect(0, 0, c.width * percentage / 100, c.height);
cx.strokeStyle = '#ccc';
cx.beginPath();
cx.moveTo(100, 70);
cx.lineTo(100, 0);
cx.stroke();
cx.moveTo(300, 70);
cx.lineTo(300, 0);
cx.stroke();
if (percentage - 0 > 100) {
cancelAnimationFrame(loop);
} else {
loop = requestAnimationFrame(progress);
}
}
progress();
#progressBar {
border: 1px solid grey;
z-index: -10;
}
#progressBar,
#download-text,
#playable,
#optimal,
button,
progress{
position: absolute;
top: 0px;
left: 0px;
}
button,
progress{
top:200px;
}
#download-text {
height: 70px;
padding-top: 25px;
padding-left: 7px;
font-family: sans-serif;
background-color: transparent;
}
#playable,
#optimal {
width: 100px;
height: 43px;
text-align: center;
padding-top: 20px;
font-family: sans-serif;
font-weight: bolder;
}
#playable {
background-color: yellow;
position: absolute;
top: 72px;
left: 100px;
}
#optimal {
background-color: green;
position: absolute;
top: 72px;
left: 300px;
}
<div>
<canvas id="progressBar" width="430px" height="70px"></canvas>
<div id="download-text">Hello</div>
</div>
<div id="playable">PLAYABLE</div>
<div id="optimal">OPTIMAL</div>
<button type="button">Please don't press me just stick to canvas</button>
<progress max="100" hidden="true"></progress>