What causes this failure to execute observe on IntersectionObserver? - javascript

I am working on a pure JavaScript infinite scroll with the help of the Intersection Observer API.
The scrollable items are posts from the jsonplaceholder.typicode.com API.
I load 5 posts initially, then 5 more with every scroll to the bottom.
class InfiniteScroll {
constructor() {
this.postsContainer = document.querySelector('#postsContainer');
this.visiblePosts = [];
this.postsLot = [];
this.observer = null;
this.hasNextPage = true;
this.postsUrl = 'https://jsonplaceholder.typicode.com/posts';
this.limit = 5;
this.iterationCount = 0;
}
loadPosts() {
fetch(this.postsUrl)
.then(res => res.json())
.then(posts => {
this.postsLot = posts.slice(this.iterationCount * this.limit, this.limit);
// Add items to the array of visible posts
// with every iteration
if(this.postsLot.length > 0) {
this.postsLot.map(entry => {
return this.visiblePosts.push(entry);
});
}
this.renderVisiblePosts();
})
.catch(err => console.log(err));
}
renderVisiblePosts() {
let output = '';
this.visiblePosts.forEach(post => {
output += `<div class="post">
<h2>${post.id} ${post.title}</h2>
<p>${post.body}</p>
</div>`;
});
this.postsContainer.innerHTML = output;
}
getLastPost() {
return this.visiblePosts[this.visiblePosts.length - 1];
}
iterationCounter() {
if (this.hasNextPage) {
this.iterationCount = this.iterationCount + 1;
}
}
bindLoadMoreObserver() {
if (this.postsContainer) {
this.observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry && entry.isIntersecting) {
console.log('bottom');
observer.unobserve(entry.target);
this.loadPosts();
this.iterationCounter();
if (this.hasNextPage) {
observer.observe(this.getLastPost());
}
}
});
});
this.observer.observe(this.getLastPost());
}
}
init() {
this.getLastPost();
this.loadPosts();
this.bindLoadMoreObserver();
}
}
const infiniteScroll = new InfiniteScroll();
infiniteScroll.init();
body, body * {
margin: 0;
padding: 0;
}
body {
font-family: Arial, Helvetica, sans-serif;
}
.post {
margin: 20px;
padding: 15px;
border: 1px solid #ccc;
border-radius: 5px;
}
p {
line-height: 1.5;
}
<div id="postsContainer"></div>
The problem
Instead of observing when the last element comes into view, the browser throws the error:
Uncaught TypeError: Failed to execute 'observe' on 'IntersectionObserver': parameter 1 is not of type 'Element'.

You're not observing the Element as per your current implementation. You're observing the last object in your visiblePosts array which is not an element.
You can get the last element by using this.postsContainer.lastElementChild provided that till then this.postsContainer has children Elements.

Related

How to run a function when scroll reaches a certain component?

There are some components stacked on top of each other and the last component has a timer. I want the timer to start only when that component is visible on screen or when scroll is reached to that component. [REPL]
let count_val = 80;
let count = 0;
function startTimer() {
let count_interval = setInterval(() => {
count += 1;
if(count >= count_val) {
clearInterval(count_interval);
}
}, 100);
}
// check if scroll reached to component and run below function.
startTimer();
How do I achieve this?
Like commented this can be achieved using Intersection Observer and an action
REPL
<script>
let count_val = 80;
let count = 0;
function timer(node) {
let interval
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if(entry.isIntersecting) {
interval = setInterval(() => {
count += 1;
if(count === count_val) {
clearInterval(interval);
}
}, 100);
} else {
clearInterval(interval)
count = 0
}
})
})
observer.observe(node)
return {
destroy: () => observer.disconnect()
}
}
</script>
<div use:timer>
<p>
Counter - {count}
</p>
</div>
<style>
div {
height: 100vh;
display: grid;
place-items: center;
background-color: teal;
color: #0a0a0a;
font-size: 4rem;
}
</style>

Javascript while with time delay

My goal I want to run loop that decrements a global variable stepwise in n ms (for Example: 200ms) time intervals.
Thanks in advance!
What i already tried
I tried to use ascy await. But in combination with css transition i run in an infinite loop (In codepen.io). But here in SO you will see that it starts not running smoothly if you keep pressing arrow up.
const procentage = document.querySelector(".procentage");
const green = engine.querySelector(".green");
let number = 0;
let decrementing = false;
window.addEventListener('keydown', (e) => {
e = e || window.event;
e.preventDefault();
if (e.keyCode == '38') {
console.log("accelerate");
actionHandler( number++ );
decrementing = false;
downLoop();
}
});
function actionHandler(num) {
procentage.innerHTML = num;
const str = num + "%"
green.style.width = str;
procentage.innerHTML = str;
}
window.addEventListener('keyup', (e) => {
e = e || window.event;
e.preventDefault();
if (e.keyCode == '38') {
console.log("decelerate");
decrementing = true;
downLoop();
}
});
async function downLoop() {
if (! decrementing) {
return false
};
const timer = ms => new Promise(res => setTimeout(res, ms));
while (number > 1) {
// how to decrement ever 200ms???
actionHandler( number-- );
await timer(200)
}
}
#engine {
background-color:black;
height: 50px;
position: relative;
}
p {
text-align: center;
}
.green {
background:green;
height: 50px;
width:0%;
transition: width 0.2s;
text-align:center;
}
.procentage {
position:absolute;
top: 50%;
left: 50%;
transform: translate(0%,-50%);
color: white;
fon-weight: bold;
font-size:28px;
}
<div id="engine">
<div><span class="procentage">0</span></div>
<div class="green"></div>
</div>
<p>press arrow Up</p>
Whenever you animate, you shouldn't rely on setInterval or setTimeout, because that means that you will update "somewhere after X milliseconds" which will often end up in the middle of a screen repaint, and will therefor cause janky animation.
Instead, you should use RequestAnimationFrame which does a calculation before every repaint. So if you got a monitor with a framerate of 60 Hz, that means that you will do 60 repaints every second. For each repaint, check if enough time have passed since the last update (shouldTriggerUpdate() below) and then check if you should update the number.
I also added the class KeyHandler to keep track of which keys that have been pressed.
I got sloppy at the end and just added a decrement as an "else" of the if statement. You will figure something out when you get there when you want to set up more keys to be pressed.
You shouldn't use KeyboardEvent.keyCode, but instead KeyboardEvent.code.
const procentage = document.querySelector(".procentage");
const green = engine.querySelector(".green");
let number = 0;
let speed = 200 // ms
let lastUpdated = 0; // ms
let animationId = 0; // use later on to pause the animation
class KeyHandler {
ArrowLeft = false
ArrowUp = false
ArrowRight = false
ArrowDown = false
#setKey(code, value) { // private method
if (typeof this[code] != undefined) {
this[code] = value;
}
}
set pressedKey(code) {
this.#setKey(code, true);
}
set releasedKey(code) {
this.#setKey(code, false);
}
}
let keyHandler = new KeyHandler();
window.addEventListener('keydown', (e) => {
e = e || window.event;
e.preventDefault();
keyHandler.pressedKey = e.code;
});
window.addEventListener('keyup', (e) => {
e.preventDefault();
keyHandler.releasedKey = e.code
});
function actionHandler(num) {
const str = num + "%"
green.style.width = str;
procentage.innerHTML = str;
}
function shouldTriggerUpdate(timeInMillis) {
let difference = timeInMillis - lastUpdated;
return difference >= speed;
}
function planeAnimation() {
let timeInMillis = new Date().getTime();
if (shouldTriggerUpdate(timeInMillis)) {
lastUpdated = timeInMillis;
if (keyHandler.ArrowUp) {
actionHandler(++number)
} else if (number > 0) {
actionHandler(--number)
}
}
animationId = requestAnimationFrame(planeAnimation)
}
animationId = requestAnimationFrame(planeAnimation);
#engine {
background-color: black;
height: 50px;
position: relative;
}
p {
text-align: center;
}
.green {
background: green;
height: 50px;
width: 0%;
transition: width 0.2s;
text-align: center;
}
.procentage {
position: absolute;
top: 50%;
left: 50%;
transform: translate(0%, -50%);
color: white;
fon-weight: bold;
font-size: 28px;
}
<div id="engine">
<div><span class="procentage">0</span></div>
<div class="green"></div>
</div>
<p>press arrow up</p>
From the above comments ...
"Instead of incrementing each time the number value push a new async timer function, set to 200 msec delay but not immediately triggered, into an array. Create an async generator from it and iterate over the latter via the for-await...of statement where one could decrement number again." – Peter Seliger
"#PeterSeliger Hi Peter! Thank you for your comment. Can you make a small example please?" – Maik Lowrey
And here the requested demonstration.
function createWait(delay) {
return async function wait () {
let settle;
const promise = new Promise((resolve) => { settle = resolve;});
setTimeout(settle, delay, { delay, state: 'ok' });
return promise;
};
}
async function* getWaitIterables(list) {
let wait;
while (wait = list.shift()) {
yield wait();
}
}
// demo for ...
// - creating an async `wait` function
// or a list of such kind.
// - creating an async generator from
// a list of async `wait` functions.
// - iterating an async generator of
// async `wait` functions.
const waitingList = [ // const waitingList = [];
2000, // waitingList.push(createWait(2000));
1000, // waitingList.push(createWait(1000));
3000, // waitingList.push(createWait(3000));
].map(createWait); // - The OP of cause needs to push into.
let number = 3; // - The incremented `number` value e.g. ... 3.
(async () => {
for await (const { delay, state } of getWaitIterables(waitingList)) {
--number;
console.log({ number, delay, state });
}
})();
console.log('... running ...', { number });
.as-console-wrapper { min-height: 100%!important; top: 0; }

Tabs component in javascript class should be reusable

I have a component that should be reusable within a page. And it should be called only when in view.
I've added an extra class function outside class Tabs which is called class Pagination.
this class is not within class Tabs which is breaking the component if reused within the page (multiple times).
Would anyone help me to move the class Pagination inside Tabs? My goal is having a unique class, so I can reuse the component multiple times and only when is in view. I really hope it makes sense,
here you have a Pen demo, as you can see the pagination is not in the right constuctor. I brake the function everytime I try to move it.
class Pagination {
constructor(tabComponent, prevBtnId, nextBtnId) {
this.arrayUtils = new ArrayUtils();
this.tabComponent = tabComponent;
this.prevBtn = document.querySelector(prevBtnId);
this.nextBtn = document.querySelector(nextBtnId);
// listen to tabComponents newly created Toggle event
// in which we wanna make sure to disable Btns or something..
this.tabComponent.onTabsToggle((tabs, tabIndex) => {
this.applyPaginationRules(tabs, tabIndex);
});
}
setDisabled(btn, value) {
if (value) {
btn.setAttribute("disabled", "true");
} else {
btn.removeAttribute("disabled");
}
}
applyPaginationRules(tabs, newActiveIndex) {
const nextBtnDisabled = newActiveIndex === tabs.length - 1;
const prevBtnDisabled = newActiveIndex === 0;
this.setDisabled(this.nextBtn, nextBtnDisabled);
this.setDisabled(this.prevBtn, prevBtnDisabled);
}
paginate(btn, action) {
const block = btn.closest(".tabs-block");
const panels = block.querySelectorAll(".panel");
const tabs = block.querySelectorAll(".tab-wrapper > li > button");
const activeIndex = Array.from(tabs).findIndex(
(t) => t.getAttribute("data-open") === "true"
);
if (tabs.length < 2) {
console.log("0 OR 1 tabs to toggle so no action.");
return;
}
var newActiveIndex;
if (action === "next") {
newActiveIndex = this.arrayUtils.nextIndex(activeIndex, tabs);
} else if (action === "prev") {
newActiveIndex = this.arrayUtils.previousIndex(activeIndex, tabs);
} else {
throw "Invalid toggle action " + action;
}
// enable / disable next and previous btns..
this.applyPaginationRules(tabs, newActiveIndex);
this.tabComponent.toggleTabs(tabs[newActiveIndex], panels);
}
addPaginationListener(btn, action) {
btn.addEventListener("click", (e) => {
this.paginate(btn, action);
});
}
init() {
this.addPaginationListener(this.prevBtn, "prev");
this.addPaginationListener(this.nextBtn, "next");
// disable prev button on beggining since we start at 0..
this.setDisabled(this.prevBtn, true);
}
}
class ArrayUtils {
// getting next index in array
nextIndex(currentIndex, array) {
// if 0 OR 1 elements, index stays the same..
if (array.length < 2) return currentIndex;
// if possible increment.
if (currentIndex < array.length - 1) {
return currentIndex + 1;
}
// if index would exceed array size go to start.
return 0;
}
// getting previous INdex in array:
previousIndex(currentIndex, array) {
// if 0 OR 1 elements, index stays the same..
if (array.length < 2) return currentIndex;
// if possible decrement!
if (currentIndex > 0) {
return currentIndex - 1;
}
// start at the end of array when end is reached ofc.
return array.length - 1;
}
}
class Tabs {
constructor() {
this.tabsBlocks = document.querySelectorAll(".tabs-block");
this.onToggleHandlers = [];
}
onTabsToggle(fn) {
this.onToggleHandlers.push(fn);
}
emitTabsToggle(tabs, tabIndex) {
this.onToggleHandlers.forEach((fn) => fn(tabs, tabIndex));
}
init() {
if (this.tabsBlocks.length > 0) {
Array.prototype.forEach.call(this.tabsBlocks, (tabBlock, index) => {
const tabContainer = tabBlock.querySelector(".tab-wrapper");
const tabs = tabBlock.querySelectorAll(".tab-wrapper li button");
const panels = tabBlock.querySelectorAll(".panel");
tabContainer.setAttribute("role", "tablist");
Array.prototype.forEach.call(tabs, (tab, tabIndex) => {
if (tab.dataset.open === "true") this.toggleTabs(tab, panels);
tab.setAttribute("role", "tab");
tab.setAttribute(
"aria-controls",
`panel-${tab.dataset.target}-block-${index + 1}`
);
const associatedPanel = tabBlock.querySelector(
`[data-panel="${tab.dataset.target}"]`
);
if (associatedPanel !== null) {
associatedPanel.id = `panel-${tab.dataset.target}-block-${
index + 1
}`;
tab.id = `tab-${tab.dataset.target}-block-${index + 1}`;
}
tab.addEventListener("click", () => {
this.toggleTabs(tab, panels);
this.emitTabsToggle(tabs, tabIndex);
});
});
Array.prototype.forEach.call(panels, (panel) => {
const associatedTab = tabBlock.querySelector(
`[data-target="${panel.dataset.panel}"]`
);
panel.setAttribute("role", "tabpanel");
panel.setAttribute("aria-labelledby", `${associatedTab.id}`);
});
});
}
}
toggleTabs = (currentTab, panels) => {
const tabs = currentTab.closest(".tabs-block").querySelectorAll("button");
const target = currentTab.dataset.target;
Array.prototype.forEach.call(tabs, (tab) => {
if (tab.dataset.target !== target) {
tab.classList.remove("is-active");
tab.setAttribute("data-open", "false");
tab.setAttribute("aria-selected", "false");
}
});
Array.prototype.forEach.call(panels, (panel) => {
if (panel.dataset.panel !== target) {
panel.classList.remove("is-active");
} else {
panel.classList.add("is-active");
}
});
/// activate tab..
currentTab.classList.add("is-active");
currentTab.setAttribute("data-open", "true");
currentTab.setAttribute("aria-selected", "true");
};
}
const components = {
Tabs: new Tabs()
};
components.Tabs.init();
// have the pagination more decoupled from tabs!
// it uses tabs component but you can remove it OR apply it to other
// classes like so more easily..
const prevBtnId = ".pagination-prev";
const nextBtnId = ".pagination-next";
const pagination = new Pagination(components.Tabs, prevBtnId, nextBtnId);
pagination.init();
.tabs-block {
border: 1px solid red;
}
.tabs-block .tab-wrapper li {
flex: 1 1 0%;
text-align: center;
}
.tabs-block .tab-wrapper li button {
font-weight: lighter;
font-size: rem(20px);
}
.tabs-block .tab-wrapper li button.is-active {
font-weight: normal;
}
.tabs-block .panel {
display: none;
}
.tabs-block .panel.is-active {
display: block;
}
.is-active {
color: red;
}
<section class="tabs-block">
<ul class="tab-wrapper">
<li><button data-target="1" data-open="true">Tab title 1</button></li>
<li><button data-target="2">Tab title 2</button></li>
<li><button data-target="3">Tab title 3</button></li>
</ul>
<div class="panel-wrapper">
<div data-panel="1" class="panel">
<p>Panel 1 content</p>
</div>
<div data-panel="2" class="panel">
<p>Panel 2 content</p>
</div>
<div data-panel="3" class="panel">
<p>Panel 3 content</p>
</div>
</div>
<button class="buttonPrev pagination-prev">
<< Prev</button>
<button class="buttonNext pagination-next">Next >></button>
</section>

Scrolling Text to Change Fixed Background Image on Scroll

I'm looking for some help, I'm trying to recreate the homepage on this site.
https://madebyarticle.com/
I don't want to use Jquery, either Plain JS or Vue.
I've got it to do most what I want it to but have a few issues.
Codepen Example
HTML
<main id="parent" class="Loop js-loop">
<section>
<h1 class="step1">One</h1>
</section>
<section>
<h1 class="step2">For</h1>
</section>
<section>
<h1 class="step3">All</h1>
</section>
<section>
<h1>And</h1>
</section>
<section>
<h1>All</h1>
</section>
<section>
<h1>For</h1>
</section>
<!--
These blocks are the same as the first blocks to get that looping illusion going.
You need to add clones to fill out a full viewport height.
-->
<section class="green is-clone">
<h1>One</h1>
</section>
<section class="red is-clone">
<h1>For</h1>
</section>
</main>
CSS
html,
body {
height: 100%;
overflow: hidden;
}
.Loop {
position: relative;
height: 100%;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
section {
position: relative;
text-align: center;
/* min-height: 300px;
max-height: 700px; */
height: 100vh;
}
::scrollbar {
display: none;
}
body {
font-family: "Avenir Next", Helvetica, sans-serif;
font-weight: normal;
font-size: 100%;
}
h1 {
margin: 0;
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 100%;
font-size: 80px;
letter-spacing: 5px;
/* color: #fff; */
text-transform: uppercase;
}
.mystyle {
background: red;
}
.mystyle1 {
background-image: url(https://images.unsplash.com/photo-1528834379234-2de7f8328fd8?ixlib=rb-1.2.1&auto=format&fit=crop&w=1920&q=10);
}
.mystyle2 {
background-image: url(https://images.unsplash.com/photo-1501854140801-50d01698950b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2600&q=80);
}
JS
var doc = window.document,
context = doc.querySelector(".js-loop"),
clones = context.querySelectorAll(".is-clone"),
disableScroll = false,
scrollHeight = 0,
scrollPos = 0,
clonesHeight = 0,
i = 0;
function getScrollPos() {
return (context.pageYOffset || context.scrollTop) - (context.clientTop || 0);
}
function setScrollPos(pos) {
context.scrollTop = pos;
}
function getClonesHeight() {
clonesHeight = 0;
for (i = 0; i < clones.length; i += 1) {
clonesHeight = clonesHeight + clones[i].offsetHeight;
}
return clonesHeight;
}
function reCalc() {
scrollPos = getScrollPos();
scrollHeight = context.scrollHeight;
clonesHeight = getClonesHeight();
if (scrollPos <= 0) {
setScrollPos(1); // Scroll 1 pixel to allow upwards scrolling
}
}
function scrollUpdate() {
if (!disableScroll) {
scrollPos = getScrollPos();
if (clonesHeight + scrollPos >= scrollHeight) {
// Scroll to the top when you’ve reached the bottom
setScrollPos(1); // Scroll down 1 pixel to allow upwards scrolling
disableScroll = true;
} else if (scrollPos <= 0) {
// Scroll to the bottom when you reach the top
setScrollPos(scrollHeight - clonesHeight);
disableScroll = true;
}
}
if (disableScroll) {
// Disable scroll-jumping for a short time to avoid flickering
window.setTimeout(function() {
disableScroll = false;
}, 40);
}
}
function init() {
reCalc();
context.addEventListener(
"scroll",
function() {
window.requestAnimationFrame(scrollUpdate);
},
false
);
window.addEventListener(
"resize",
function() {
window.requestAnimationFrame(reCalc);
},
false
);
}
if (document.readyState !== "loading") {
init();
} else {
doc.addEventListener("DOMContentLoaded", init, false);
}
// Just for this demo: Center the middle block on page load
window.onload = function() {
setScrollPos(
Math.round(
clones[0].getBoundingClientRect().top +
getScrollPos() -
(context.offsetHeight - clones[0].offsetHeight) / 2
)
);
};
const images = document.querySelectorAll('h1');
observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.intersectionRatio > 0) {
entry.target.classList.add('active');
} else {
entry.target.classList.remove('active');
}
});
});
images.forEach(image => {
observer.observe(image);
});
const header = document.getElementById("parent");
const sectionOne = document.querySelector("h1.step1");
const sectionTwo = document.querySelector("h1.step2");
const sectionThree = document.querySelector("h1.step3");
const sectionOneObserver = new IntersectionObserver(function(
entries,
sectionOneObserver
) {
entries.forEach(entry => {
if (!entry.isIntersecting) {
header.classList.add("mystyle");
} else {
header.classList.remove("mystyle");
}
});
});
sectionOneObserver.observe(sectionOne);
const sectionTwoObserver = new IntersectionObserver(function(
entries,
sectionTwoObserver
) {
entries.forEach(entry => {
if (!entry.isIntersecting) {
header.classList.add("mystyle1");
} else {
header.classList.remove("mystyle1");
}
});
});
sectionTwoObserver.observe(sectionTwo);
const sectionThreeObserver = new IntersectionObserver(function(
entries,
sectionThreeObserver
) {
entries.forEach(entry => {
if (!entry.isIntersecting) {
header.classList.add("mystyle2");
} else {
header.classList.remove("mystyle2");
}
});
});
sectionThreeObserver.observe(sectionThree);
// if (document.querySelectorAll('h1.step1.active')){
// document.getElementById("parent").classList.toggle("mystyle");
// } else {
// document.getElementById("parent").classList.remove("mystyle");
// }
// if (document.classList.contains("h1.step1.active")) {
// document.getElementById("parent").classList.add("mystyle");
// } else {
// document.getElementById("parent").classList.remove("mystyle");
// }
I'm having to duplicate the IntersectionObserver for every image I need, is there a cleaner way todo this?
At some points, there are 2 active states so it doesn't display any image only a background colour.
What would be the best way to fade the image on change like the example website?
Are there any examples or scripts that can do what I'm trying todo?
Thanks

How to create a ludo board dynamically using Javascript

I am making a board game ludo. I contains four players. The board of ludo looks something like
I have managed to create some part of it dynamically using Javascript.
const qs = str => document.querySelector(str);
const qsa = str => document.querySelectorAll(str);
const ce = (str, props) => {
let elm = document.createElement(str);
if(props){
for(let k in props){
elm[k] = props[k]
}
}
return elm;
}
let main = qs('#main');
function createDiv(type, color){
let div = ce('div');
div.style.backgroundColor = color;
div.style.display = type;
}
let game = Array(52).fill(0);
function createPlayer(color,angle){
let div = ce('div', {className: 'player-cont'})
let table = ce('table');
div.style.transform = `rotate(${angle}deg)`
function createRow(len,colorsSet){
const tr = ce('tr',{className:'tile-row'});
[...Array(len)].forEach((x,i) =>{
let elm = ce('td', {className:'tile'});
if(colorsSet.has(i)){
elm.style.backgroundColor = color;
}
tr.appendChild(elm)
})
return tr;
}
function createBase(){
const base = ce('table', {className: 'base'});
[...Array(2)].forEach(x => {
let row = ce('tr', {className: 'base-row'});
[...Array(2)].forEach(a => {
let td = ce('td', {className: 'base-tile'})
row.appendChild(td);
})
base.appendChild(row)
})
return base;
}
let colorSets = [
new Set(),
new Set([0,1,2,3,4]),
new Set([4])
]
colorSets.forEach(x => {
table.appendChild(createRow(6, x))
})
div.appendChild(table);
div.appendChild(createBase());
return div;
}
let colors = ['red','blue','green','pink'];
colors.forEach((x, i) => {
main.appendChild(createPlayer(x, (i * 90) - 180));
})
.tile {
height: 50px;
width: 50px;
background: orange;
border: 1px solid black;
display: inline-block;
}
table.base td {
height: 50px;
background: orange;
width: 50px;
border-radius: 100px;
}
.player-cont {
background: aliceblue;
width: fit-content;
}
<div id = "main"></div>
But now I am not sure how will I finish this up. You can ignore two things in the above image:
The big border of the of big square of different colors. I just need a single color throughout.
You can ignore the colorful triangles in center. I just need a one color box instead of that.

Categories

Resources