How to vertically align all text in CSS? - javascript

The issue seems to be that certain letters like g, y, q, etc. that have a tail that slopes downwards, do not allow for vertical centering. Here's an image to showcase the problem .
The characters in the green box are basically perfect, as they have no downward tail. Those in the red box demonstrate the problem.
I would like for all characters to be perfectly vertically centered. In the image, characters with a downward tail are not vertically centered. Is this possible to rectify?
Here is the fiddle that demonstrates the problem in full.
.avatar {
border-radius: 50%;
display: inline-block;
text-align: center;
width: 125px;
height: 125px;
font-size: 60px;
background-color: rgb(81, 75, 93);
font-family: "Segoe UI";
margin-bottom: 10px;
}
.character {
position: relative;
top: 50%;
transform: translateY(-50%);
line-height: 100%;
color: #fff;
}
<div class="avatar">
<div class="character">W</div>
</div>
<div class="avatar">
<div class="character">y</div>
</div>

Here is my solution using JS. The idea is to transform the element into an image in order to get its data as pixel then loop through them to find the top and bottom of each character and apply a translation to fix the alignment. This will work with dynamic font properties.
The code is not optimized but it highlight the main idea:
var elems = document.querySelectorAll(".avatar");
var fixes = [];
for (var i = 0; i < elems.length; i++) {
var current = elems[i];
domtoimage.toPixelData(current)
.then(function(im) {
/* Search for the top limit */
var t = 0;
for (var y = 0; y < current.scrollHeight; ++y) {
for (var x = 0; x < current.scrollWidth; ++x) {
var j = (4 * y * current.scrollHeight) + (4 * x);
if (im[j] == 255 && im[j + 1] == 255 && im[j + 2] == 255) {
t = y;
break;
}
}
}
/* Search the bottom limit*/
var b = 0;
for (var y = (current.scrollHeight - 1); y >= 0; --y) {
for (var x = (current.scrollWidth - 1); x >= 0; --x) {
var j = (4 * y * current.scrollHeight) + (4 * x);
if (im[j] == 255 && im[j + 1] == 255 && im[j + 2] == 255) {
b = current.scrollHeight - y;
break;
}
}
}
/* get the difference and apply a translation*/
var diff = (b - t)/2;
fixes.push(diff);
/* we apply the translation when all are calculated*/
if(fixes.length == elems.length) {
for (var k = 0; k < elems.length; k++) {
elems[k].querySelector('.character').style.transform = "translateY(" + fixes[k] + "px)";
}
}
});
}
.avatar {
border-radius: 50%;
display: inline-flex;
vertical-align:top;
justify-content: center;
align-items: center;
width: 125px;
height: 125px;
font-size: 60px;
background:
linear-gradient(red,red) center/100% 1px no-repeat,
rgb(81, 75, 93);
font-family: "Segoe UI";
margin-bottom: 10px;
}
.character {
color: #fff;
}
<script type="text/javascript" src="https://css-challenges.com/wp-content/themes/ronneby_child/js/dom-to-image.js"></script>
<div class="avatar">
<div class="character">W</div>
</div>
<div class="avatar">
<div class="character">y</div>
</div>
<div class="avatar">
<div class="character" style="font-size:35px">a</div>
</div>
<div class="avatar">
<div class="character" style="font-size:25px">2</div>
</div>
<div class="avatar">
<div class="character">o</div>
</div>
<div class="avatar">
<div class="character">|</div>
</div>
<div class="avatar">
<div class="character">#</div>
</div>
<div class="avatar">
<div class="character">Â</div>
</div>
<div class="avatar">
<div class="character" style="font-family:arial">Q</div>
</div>
<div class="avatar">
<div class="character">~</div>
</div>
<div class="avatar">
<div class="character">8</div>
</div>
<div class="avatar">
<div class="character">ä</div>
</div>
<div class="avatar">
<div class="character">ç</div>
</div>
<div class="avatar">
<div class="character">$</div>
</div>
<div class="avatar">
<div class="character">></div>
</div>
<div class="avatar">
<div class="character">%</div>
</div>
UPDATE
Here is a first optimization of the code:
var elems = document.querySelectorAll(".avatar");
var k = 0;
for (var i = 0; i < elems.length; i++) {
domtoimage.toPixelData(elems[i])
.then(function(im) {
var l = im.length;
/* Search for the top limit */
var t = 0;
for (var j = 0; j < l; j+=4) {
if (im[j+1] == 255) { /* Since we know the colors, we can only test the G composant */
t = Math.ceil((j/4)/125);
break;
}
}
/* Search the bottom limit*/
var b = 0;
for (var j = l - 1; j >= 0; j-=4) {
if (im[j+1] == 255) {
b = 125 - Math.ceil((j/4)/125);
break;
}
}
/* get the difference and apply a translation*/
elems[k].querySelector('.character').style.transform = "translateY(" + (b - t)/2 + "px)";
k++;
});
}
.avatar {
border-radius: 50%;
display: inline-flex;
vertical-align:top;
justify-content: center;
align-items: center;
width: 125px;
height: 125px;
font-size: 60px;
background:
linear-gradient(red,red) center/100% 1px no-repeat,
rgb(81, 75, 93);
font-family: "Segoe UI";
margin-bottom: 10px;
}
.character {
color: #fff;
}
<script type="text/javascript" src="https://css-challenges.com/wp-content/themes/ronneby_child/js/dom-to-image.js"></script>
<div class="avatar">
<div class="character">W</div>
</div>
<div class="avatar">
<div class="character">y</div>
</div>
<div class="avatar">
<div class="character" style="font-size:35px">a</div>
</div>
<div class="avatar">
<div class="character" style="font-size:25px">2</div>
</div>
<div class="avatar">
<div class="character">o</div>
</div>
<div class="avatar">
<div class="character">|</div>
</div>
<div class="avatar">
<div class="character">#</div>
</div>
<div class="avatar">
<div class="character">Â</div>
</div>
<div class="avatar">
<div class="character" style="font-family:arial">Q</div>
</div>
<div class="avatar">
<div class="character">~</div>
</div>
<div class="avatar">
<div class="character">8</div>
</div>
<div class="avatar">
<div class="character">ä</div>
</div>
<div class="avatar">
<div class="character">ç</div>
</div>
<div class="avatar">
<div class="character">$</div>
</div>
<div class="avatar">
<div class="character">></div>
</div>
<div class="avatar">
<div class="character">%</div>
</div>
I am using dom-to-image plugin for this.

Maybe there is a better answer, but it sounds like the only way to is to manually apply different styles depending on whether it is one of:
Capital letter
Lowercase with a tail
Lowercase with a stalk
Lowercase with neither
Now note that, in my understanding, the relative heights of tails and stalks I think is defined by the font. I'm not sure if there's a way to access that programatically - so you might need to adjust these values with the font.
Note also that this solution wouldn't work for supporting multiple languages - as you would need to define which category every single character fits in across dozens of different character sets.
const letters = ['a', 'b', 'y', 'X', 'c', 'y', 'A', 'B', 'Y'];
function getAdditionalClass(char){
//To do - fill arrays with the rest of the appropriate letters
if (['y', 'g'].includes(char)) {
return "tail";
}
if (['b', 'd'].includes(char)) {
return "stalk";
}
if (['a', 'c'].includes(char)) {
return "small";
}
return "capital";
}
letters.forEach(v => {
const avatar = document.createElement("div");
avatar.className = "avatar";
const character = document.createElement("div");
character.textContent = v;
character.className = `character ${getAdditionalClass(v)}`;
avatar.appendChild(character);
const root = document.getElementById("root");
root.appendChild(avatar);
});
.avatar {
border-radius: 50%;
display: block;
text-align: center;
width: 125px;
height: 125px;
font-size: 60px;
background-color: rgb(81, 75, 93);
font-family: "Segoe UI";
margin-bottom: 10px;
}
.character {
position: relative;
transform: translateY(-50%);
line-height: 100%;
color: #fff;
}
.small {
top: 45%;
}
.stalk {
top: 50%;
}
.tail {
top: 41%;
}
.capital {
top: 50%;
}
#root {
display: flex;
flex-flow: row wrap;
}
<div id = "root">
</div>

This is a tricky situation!
From what I can tell, this will be most difficult to make natively scalable (i.e. %, vw or vh values instead of px or em). If you need to make this look pretty on mobile or tablet, please consider using my solution with #media breakpoints.
My solution essentially detects if it is a lowercase element with a tail and adds a class to offset the height to compensate for the tail.
In my testing, it didn't appear that any additional handlers were required for capital letters or lower case letters without a tail. Be feel free to correct me if I'm wrong.
There's a JSFiddle if you want to mess around and change / test this solution.
var circles = document.getElementsByClassName('circle');
var tails = ['q', 'y', 'p', 'g', 'j'] ;
Array.from(circles).forEach(render);
function render(element) {
if(element.innerText == element.innerText.toLowerCase() &&
tails.includes(element.innerText)) {
element.className += " scale";
}
}
.circle {
height: 150px;
width: 150px;
background-color: #bbb;
border-radius: 50%;
display: inline-block;
text-align: center;
vertical-align: middle;
line-height: 150px;
font-size: 50px;
}
.scale {
line-height: 135px;
}
<div>
<div class="circle">W</div>
<div class="circle">X</div>
</div>
<div>
<div class="circle">y</div>
<div class="circle">t</div>
</div>
Let me know your thoughts and if I've missed anything. It'd be cool to get a final solution for this as I've had similar issues in the past myself!

You would probably need a helperclass for this, so that you can translate the lowercase letters more than the capital letters. A simple script can easily put these helper classes on automatically.
Hope this solves the problem for you :)
.avatar {
border-radius: 50%;
display: block;
text-align: center;
width: 125px;
height: 125px;
font-size: 60px;
background-color: rgb(81, 75, 93);
font-family: "Segoe UI";
margin-bottom: 10px;
}
.character {
position: relative;
top: 50%;
line-height: 100%;
color: #fff;
}
.character-lowercase {
transform: translateY(-60%);
}
.character-capital {
transform: translateY(-50%);
}
<div class="avatar">
<div class="character character-capital">W</div>
</div>
<div class="avatar">
<div class="character character-lowercase">y</div>
</div>

Related

How to make a toggle on several elements?

I have in my beacon <div class="post">
another beacon <p class="compteur"></p>
which is intended to be incremented to find out if the user likes (clicks) it or decrements it if the user likes (clicks) it.
for this reason I use a boolean to check the condition
in my js I have a function that its charge of the fact
but as I apply this function to multiple tag recovers in my code they all act on the same boolean and value that causes me problem.
I would like each of them to be independent
const cerle = document.getElementsByClassName('cerle')
const compteur = document.getElementsByClassName('compteur')
let onOff = false;
let nbr = 0;
function compte (i) {
if (onOff == false) {
nbr++
console.log( nbr);
onOff= true;
console.log( onOff);
compteur[i].innerHTML=`${nbr}k`
}else if(onOff == true){
nbr--
console.log( nbr);
onOff= false;
console.log( onOff);
compteur[i].innerHTML=`${nbr}k`
}
}
for (let i = 0; i < cerle.length; i++) {
cerle[i].addEventListener("click",()=>{
compte (i);
});
}
*{
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Nunito Sans', sans-serif;
}
.contain{
align-items: center;
display: flex;
flex-direction: row;
width: 100%;
height: 400px;
background-color: yellowgreen;
}
.post{
margin-left: 30px;
width: 30%;
height: 70%;
background-color: rgb(73, 50, 205);
}
.cerle{
width: 20%;
height: 30%;
border-radius: 50%;
background-color: rgb(205, 35, 35);
}
<body>
<div class="contain">
<div class="post">
<div class="cerle"></div>
<p class="compteur"></p>
</div>
<div class="post">
<div class="cerle"></div>
<p class="compteur"></p>
</div>
<div class="post">
<div class="cerle"></div>
<p class="compteur"></p>
</div>
</div>
</body>
well here I was able to solve my problem and thank you to everyone who kindly helped me
I went through creating a new object for the assigned to each recovered div only the js file changed.
Here's my code.
const cerle = document.querySelectorAll(".cerle");
const compteur = document.getElementsByClassName('compteur');
let etat = false;
var valeurDesPoste = new Array();
class compte {
constructor(onOff, valeur ,i) {
this.onOff = onOff;
this.valeur = valeur;
this.i = i;
}
teste() {
if (this.onOff == false) {
this.valeur++ ;
console.log( this.valeur);
this.onOff= true;
console.log( this.onOff);
compteur[this.i].innerHTML=`${this.valeur}k`
}else if(this.onOff == true){
this.valeur-- ;
console.log( this.valeur);
this.onOff= false;
console.log( this.onOff);
compteur[this.i].innerHTML=`${this.valeur}k`
}
}
}
for (let i = 0; i < cerle.length; i++) {
valeurDesPoste.push(50 *i);
// console.log( valeurDesPoste[i]);
let a =new compte(etat, valeurDesPoste[i] ,i);
cerle[i].addEventListener("click",()=>{
a.teste()
});
}
*{
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Nunito Sans', sans-serif;
}
.contain{
align-items: center;
display: flex;
flex-direction: row;
width: 100%;
height: 400px;
background-color: yellowgreen;
}
.post{
margin-left: 30px;
width: 30%;
height: 70%;
background-color: rgb(73, 50, 205);
}
.cerle{
width: 20%;
height: 30%;
border-radius: 50%;
background-color: rgb(205, 35, 35);
}
<div class="contain">
<div class="post">
<div class="cerle"></div>
<p class="compteur"></p>
</div>
<div class="post">
<div class="cerle"></div>
<p class="compteur"></p>
</div>
<div class="post">
<div class="cerle"></div>
<p class="compteur"></p>
</div>
</div>

Improve display/performance of 2 column infinite scroll layout on resize of browser

I have a UI that involves (on wider devices) a 2 column layout that scroll in opposite directions in an infinite loop. On page load the UI seems to work pretty well but I really need some help improving some aspects of it.
The key things I want to work on are:
Order of items (projects) matches HTML structure. Currently they're reversed
Improve how the UI reacts and displays on browser resize
Fix ESLint undefined errors for "Map" and Weakmap"
1. Order of Items (projects)
You can see I've number each .project and on load the one that appears last in the HTML is first when viewing the webpage. It would make more sense the 'top' item is visible in the viewport before revealing the others in cascading order on scroll.
2. UI on Browser Resize
Though the UI seems to work well on page load, I think due to the positional values set on each .project this seems to lead to content overlapping or getting cropped on resize. I've tried matchMedia to see if I can run/recalculate once the viewport has 'stopped' resizing but doesn't seem to work.
It doesn't seem as bad going from desktop to mobile size screens. But vice versa, if you open on a narrow viewport and enlarge no content is visible and the UI appears empty until you scroll ...and then the left column doesn't loop (it stops) until you refresh the page.
On the mid-point #media when each .project has 50vh and not 100vh on scroll the items appear to flicker. Again, until you refresh.
It seems like maybe I need to run the script again after each resize? This is for a 'fun' portfolio style project so I appreciate that's a bit heavy on the resource but maybe acceptable in this instance, as it's not a commercial or D2C site?
3. ESLint Errors
Lastly, I get 2 errors saying Map and Weakmap are undefined when I compile using CodeKit and ESLint. That is in relation to these 2 lines...
const lastScrollPos = new WeakMap();
const linkedLoops = new Map([
I know there's a couple of issues but I wanted to break them down to try and be as clear as possible.
const leftLoop = document.querySelector(".split-loop__left");
const rightLoop = document.querySelector(".split-loop__right");
const scrollHeight = leftLoop.scrollHeight;
const offsetBoundary = 200; //the offset from the borders at which the element reordering event is triggered
const lastScrollPos = new WeakMap();
const linkedLoops = new Map([
[leftLoop, rightLoop],
[rightLoop, leftLoop]
]);
let scrollLockElement = null;
let scrollLockTimeout = null;
// the function sets handlers to scrolling for infinite scrolling
function infiniteScrollHandler(loop) {
const virtualLoop = Array.from(loop.children);
virtualLoop.forEach(
(el) => (el.style.top = scrollHeight / 2 + el.offsetHeight + "px")
);
loop.addEventListener("scroll", () => {
if (virtualLoop.length < 2) return; // not enough items to scroll
const topBound = loop.scrollTop;
const bottomBound = loop.scrollTop + loop.offsetHeight;
const firstEl = virtualLoop[0];
const lastEl = virtualLoop[virtualLoop.length - 1];
if (firstEl.offsetTop >= topBound - offsetBoundary) {
lastEl.style.top = firstEl.offsetTop - lastEl.offsetHeight + "px";
virtualLoop.unshift(lastEl);
virtualLoop.pop();
} else if (
lastEl.offsetTop + lastEl.offsetHeight <
bottomBound + offsetBoundary
) {
firstEl.style.top = lastEl.offsetTop + lastEl.offsetHeight + "px";
virtualLoop.push(firstEl);
virtualLoop.shift();
}
});
}
// the function sets handlers to scrolling for reverse interaction with the linked loop
function reverseLinkLoopHandler(loop) {
loop.addEventListener("scroll", () => {
const delta = lastScrollPos.get(loop) - loop.scrollTop;
lastScrollPos.set(loop, loop.scrollTop);
// this is blocked to prevent deadlock when events of two blocks are called each other.
{
if (scrollLockElement !== null && scrollLockElement !== loop)
return;
scrollLockElement = loop;
clearTimeout(scrollLockTimeout);
scrollLockTimeout = setTimeout(
() => (scrollLockElement = null),
300
);
}
linkedLoops
.get(loop)
.scrollTo(0, linkedLoops.get(loop).scrollTop + delta);
});
}
// set scroll handlers on all loops
linkedLoops.forEach((loop) => {
infiniteScrollHandler(loop);
loop.scrollTo(0, scrollHeight / 2);
lastScrollPos.set(loop, scrollHeight / 2);
reverseLinkLoopHandler(loop);
});
/* Hide Scroll Bars */
::-webkit-scrollbar {
display: none;
}
html,
body {
margin: 0;
padding: 0;
-ms-overflow-style: none;
scrollbar-width: none;
}
/* Content will be in these eventually */
.bar-left,
.bar-right {
border-right: 2px solid black;
box-sizing: border-box;
height: 100vh;
position: fixed;
top: 0;
left: 0;
width: 48px;
z-index: 10000;
}
.bar-right {
border: none;
border-left: 2px solid black;
left: auto;
right: 0;
}
/* Split Loop */
.split-loop {
margin-left: 24px;
}
.split-loop__item {
background: white;
overflow: hidden;
}
.project {
box-sizing: border-box;
border-bottom: 2px solid black;
padding: 24px 24px 0;
width: 100%;
}
.project__media {
margin-bottom: 24px;
}
.project__img {
border: 2px solid black;
width: 100%;
max-width: 100%;
}
.project__title {
font-family: Arial;
font-size: 12px;
margin-bottom: 24px;
}
/* Tablet View */
#media screen and (min-width: 400px) {
.split-loop {
height: 100vh;
position: relative;
margin: 0 48px;
}
.split-loop__left {
border-right: 2px solid black;
box-sizing: border-box;
width: 50%;
}
.split-loop__right {
position: fixed;
right: 24px;
bottom: 0;
width: calc(50% - 48px);
}
.split-loop__item {
display: flex;
flex-flow: column;
height: 50vh;
}
.project__media {
display: flex;
flex-grow: 1;
align-items: center;
justify-content: center;
overflow: hidden;
margin-bottom: 24px;
}
.project__img {
box-sizing: border-box;
display: block;
margin-bottom: 0;
width: auto;
max-width: 100%;
height: 100%;
max-height: 100%;
object-fit: contain;
overflow: hidden;
}
/* Split Loop */
.split-loop {
position: relative;
margin: 0 48px;
}
.split-loop__left {
width: 50%;
overflow: auto;
position: relative;
max-height: 100vh;
}
.split-loop__right:before,
.split-loop__left:before {
display: block;
content: "";
z-index: -1;
height: 9999999px;
}
.split-loop__right {
box-sizing: border-box;
position: fixed;
right: 48px;
bottom: 0;
z-index: 5;
width: calc(50% - 48px);
overflow: auto;
max-height: 100vh;
}
.project {
box-sizing: border-box;
border-bottom: 2px solid black;
padding: 24px 24px 0;
position: absolute;
}
}
#media screen and (min-width: 600px) {
.split-loop__item {
height: 100vh;
}
}
<header class="bar-left"></header>
<div class="bar-right"></div>
<div class="split-loop" role="main">
<div class="split-loop__left">
<div class="split-loop__item project">
<div class="project__media">
<img src="https://www.fillmurray.com/g/600/800" alt="" class="project__img" />
</div>
<div class="project__copy">
<h2 class="project__title">Project Left #1</h2>
</div>
</div>
<div class="split-loop__item project">
<div class="project__media">
<img src="https://www.fillmurray.com/600/800" alt="" class="project__img" />
</div>
<div class="project__copy">
<h2 class="project__title">Project Left #2</h2>
</div>
</div>
<div class="split-loop__item project">
<div class="project__media">
<img src="https://www.fillmurray.com/g/600/800" alt="" class="project__img" />
</div>
<div class="project__copy">
<h2 class="project__title">Project Left #3</h2>
</div>
</div>
<div class="split-loop__item project">
<div class="project__media">
<img src="https://www.fillmurray.com/600/800" alt="" class="project__img" />
</div>
<div class="project__copy">
<h2 class="project__title">Project Left #4</h2>
</div>
</div>
</div>
<div class="split-loop__right">
<div class="split-loop__item project">
<div class="project__media">
<img src="https://www.fillmurray.com/600/800" alt="" class="project__img" />
</div>
<div class="project__copy">
<h2 class="project__title">Project Right #1</h2>
</div>
</div>
<div class="split-loop__item project">
<div class="project__media">
<img src="https://www.fillmurray.com/g/600/800" alt="" class="project__img" />
</div>
<div class="project__copy">
<h2 class="project__title">Project Right #2</h2>
</div>
</div>
<div class="split-loop__item project">
<div class="project__media">
<img src="https://www.fillmurray.com/600/800" alt="" class="project__img" />
</div>
<div class="project__copy">
<h2 class="project__title">Project Right #3</h2>
</div>
</div>
<div class="split-loop__item project">
<div class="project__media">
<img src="https://www.fillmurray.com/g/600/800" alt="" class="project__img" />
</div>
<div class="project__copy">
<h2 class="project__title">Project Right #4</h2>
</div>
</div>
</div>
</div>

Update mouse position on scroll

I came across this website's work page and would like to recreate the xray effect of each of the card element.
Here's my code for the xray effect: https://codepen.io/carljustineoyales/pen/yLMEVYd
HTML
<section class="banner">
<h1>this is a banner page</h1>
</section>
<section class="projects" id="projects">
<div class="cardlist" >
<div class="cardlist__grid" >
<div class="card" id="card" onmouseover="setSize()" onmouseout="resetSize()">
<div class="card__category">
category
</div>
<div class="card__thumbnail">
</div>
<div class="card__info">
<div class="card__title">title</div>
<div class="card__date">date</div>
</div>
</div>
<div class="card" id="card">
<div class="card__category">
category
</div>
<div class="card__thumbnail">
</div>
<div class="card__info">
<div class="card__title">title</div>
<div class="card__date">date</div>
</div>
</div>
</div>
</div>
<div class="cardlist--skeleton" id="skeleton">
<div class="cardlist--skeleton--bg"></div>
<div class="cardlist__grid">
<div class="card">
<div class="card__category">
category
</div>
<div class="card__thumbnail card__thumbnail--black">
</div>
<div class="card__info">
<div class="card__title">title</div>
<div class="card__date">date</div>
</div>
</div>
<div class="card">
<div class="card__category">
category
</div>
<div class="card__thumbnail card__thumbnail--black">
</div>
<div class="card__info">
<div class="card__title">title</div>
<div class="card__date">date</div>
</div>
</div>
</div>
</div>
</section>
</body>
SCSS
* {
margin: 0;
padding: 0;
}
.banner {
height: 100vh;
}
.projects {
max-width: 1440px;
width: 100%;
margin: 100px auto;
// padding: 100px 0;
position: relative;
}
.cardlist {
width: 100%;
color: #000;
&__grid {
display: flex;
flex-direction: row;
justify-content: space-between;
}
&--skeleton {
--x: 0;
--y: 0;
--size: 0px;
clip-path: circle(var(--size) at var(--x) var(--y));
user-select: none;
pointer-events: none;
// clip-path: circle(100px at 0 0);
// clip-path: circle(300px at 0% 0%);
transition: clip-path 0.1s ease;
&--bg {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: #838383;
z-index: -1;
}
width: 100%;
position: absolute;
top: 0;
left: 0;
color: #fff;
z-index: 1;
background-color: #838383;
}
}
.card {
padding: 50px;
&__thumbnail {
width: 500px;
height: 644px;
background-color: #838383;
&--black {
background-color: #000;
}
}
}
JS
let mouseX=0
let mouseY=0
let size = 0;
const projects = document.querySelector('#projects');
function setSize() {
size = 200
skeleton.style.setProperty('--size', size + "px");
}
function resetSize() {
size = 0
skeleton.style.setProperty('--size', size + "px");
}
function updateCoordinates(event) {
mouseX = event.pageX - projects.offsetLeft ;
mouseY = event.pageY - projects.offsetTop ;
skeleton.style.setProperty('--x', mouseX + "px");
skeleton.style.setProperty('--y', mouseY+ "px");
}
The problem is that I can't seem to find a solution for the updating cursor position while scrolling, also the hover animation became laggy when you open the console. Is there a way to update the cursor position on scroll and why the animation became laggy when the console is open.

Appended items flow outside of div

I have a simple "dice" roller, where the user can choose how many sides and how many dice based on input.
My issue is that if the user selects a large number of dice, it will force the page to scroll left in order to view the other dice.
I have tried to keep these dice within a div , even trying word-wrap: break-word; within the css, but this stacks the dice on top of eachother.
heres my code.
$(document).ready(function() {
$('#autoLoadR').click(function() {
$('#buttnLodr').html("");
if ($('#sideNum').val() < 100) {
if ($('#diceNum').val() < 20) {
for (i = 0; i < $('#diceNum').val(); i++) {
let index = i + 1;
let roll = index;
sidesAmount = $('#sideNum').val();
roll = Math.floor(Math.random() * sidesAmount) + 1;
$('#buttnLodr').append("<span id='diceBox'>" + roll + "</span>")
}
} else {
alert("Please enter a number for less than 20 for number of dice")
}
} else {
alert("Please enter a number less than 100 for number of sides")
}
});
});
body {
background: #add8e6;
margin-left: 2%;
margin-top: 2%;
width: 500px;
}
#spaceR {
color: lightblue;
}
.rollMeNow {
display: block;
color: #fff;
cursor: pointer;
border: 1px solid #d7d7d7;
font-size: 72px;
height: 156px;
line-height: 156px;
width: 256px;
background: #df1f3b;
border-radius: 4px;
text-align: center;
}
#optionDice {
border: solid;
width: 100%;
}
#diceBox {
border: solid;
padding: 7px 14px;
box-shadow: 10px 5px;
margin: 2%;
}
#rollTable {
width: 100%;
background: #fff;
height: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://aaronlilly.github.io/CDN/css/bootstrap.min.css">
<div id="optionDice">
<h1>Number of Dice :
<span id='spaceR'> :</span>
<input type="text" id="diceNum" placeholder="Dice" size="5" style="margin-top: 10px;padding-bottom: 5px;padding-top: 4px;">
</h1>
<h1>Number of Sides :
<input type="text" id="sideNum" placeholder="Sides" size="5" style="margin-top: 10px;padding-bottom: 5px;padding-top: 4px;">
</h1>
</div>
<br><br>
<div class="rollMeNow" caption="Populate" id="autoLoadR">Roll</div>
<br>
<h1>
<div id='rollTable'>
<br>
<div class="container">
<div class="row">
<!-- <div class="col-sm"> -->
<div id='buttnLodr'> </div>
</div>
</div>
</div>
</div>
</h1>
#diceBox {
// ...
display: inline-block;
}
Also, some other suggestions:
you have some implicitly declared variables (i in your for loop and sidesAmount in that loop)
use const instead of let whenever you are not re-asigning a variable
why looping from 0, then add 1 and then store it to another variable. And then you overwrite that variable with Math.floor
try to avoid selecting DOM elements (event if its only single) by IDs. Always use class.

JS addition game: how do I display 4 possible answers, 3 of them are random and 1 is the correct answer ? (Codepen included)

I am building a game out of JS. the rules of the game are simple: you are asked what (num1) + (num2) equals (as you can see in the codepen).
In the game you have 4 possible choices to answer the question.
We're I'm stuck right now is creating those possible options: I would like to display three random numbers that are false and one number that is the correct sum.
my JS:
var num1 = Math.floor((Math.random() * 30) + 10);
var num2 = Math.floor((Math.random() * 30) + 10);
var result = num1 + num2;
document.getElementById('field1').innerHTML = num1;
document.getElementById('field2').innerHTML = num2;
var options = {
option1: document.getElementById('option1'),
option2: document.getElementById('option2'),
option3: document.getElementById('option3'),
option4: document.getElementById('option4'),
}
Here is my codepen:
https://codepen.io/teenicarus/pen/Oxaaoe
How do i do this?
I appreciate all answers
The solution is a little complex, it will be so long to describe every row, so feel free to ask if anything isn't clear. Need to say, that the order of numbers on cards is randomly generated too. Here it is:
function shuffle(o) {
for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
function startGame() {
var num1 = Math.floor((Math.random() * 30) + 10);
var num2 = Math.floor((Math.random() * 30) + 10);
var result = num1 + num2;
var otherNumbers = [];
var counter = 0;
document.getElementById('field1').innerHTML = num1;
document.getElementById('field2').innerHTML = num2;
var options = {
option1: document.getElementById('option1'),
option2: document.getElementById('option2'),
option3: document.getElementById('option3'),
option4: document.getElementById('option4'),
}
function generateRandomNumber() {
for (var i = 0; counter < 3; i++) {
var num = Math.floor((Math.random() * 30) + 10);
if (num !== result && counter < 3) {
counter++;
otherNumbers.push(num);
} else {
generateRandomNumber();
}
}
}
generateRandomNumber();
otherNumbers.push(result);
otherNumbers = shuffle(otherNumbers);
var arrCount = otherNumbers.length - 1;
for (var key in options) {
if (arrCount >= 0) {
options[key].innerHTML = otherNumbers[arrCount];
arrCount--;
}
}
}
startGame();
.App {
text-align: center;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 60px;
}
.App-header {
background-color: #222;
height: 180px;
padding: 20px;
color: white;
}
.App-intro {
font-size: large;
}
#keyframes App-logo-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.text-info {
color: #fff;
font-weight: bold;
font-size: 2.1rem;
}
.question {
font-size: 2rem;
}
.options {
margin: 5%;
display: flex;
margin-right: -12px;
margin-left: -12px;
flex-direction: row;
flex-wrap: wrap;
align-items: stretch;
flex: 1 0 auto;
}
.fields {
display: flex;
padding: 12px;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
flex: 1;
}
.field-block {
display: flex;
min-height: 160px;
padding: 10%;
flex-direction: row;
justify-content: center;
align-items: center;
/*flex: 1 0 auto;*/
border-radius: 4px;
background-color: #f9bad0;
font-size: 6rem;
color: #fff;
cursor: pointer;
}
.quiz {
color: #ddd;
margin: 2%;
background-color: #ec1561;
padding: 2%;
width: 90%;
position: relative;
}
.button {
display: flex;
height: 48px;
padding-right: 16px;
padding-left: 16px;
flex-direction: row;
justify-content: center;
align-items: center;
flex: 0 0 auto;
border-radius: 4px;
background-color: #2fcaaa;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .05), 0 2px 12px 0 rgba(0, 0, 0, .1);
transition: box-shadow 200ms ease-out;
color: #fff;
font-weight: 500;
text-align: center;
cursor: pointer;
}
.quiz .after {
position: absolute;
top: 5%;
left: 5%;
width: 90%;
height: 80%;
/*display: none;*/
color: #FFF;
text-align: center;
align-items: center;
justify-content: center;
display: flex;
opacity: 0.8;
font-size: 3rem;
}
.correct {
background-color: green;
}
.wrong {
background-color: #D91E18;
}
.hide {
display: none !important;
}
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Adding 2 Numbers | Happy Learning!</title>
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.2/animate.min.css">
</head>
<body>
<img alt="Join Slack" height="40" width="139" src="http://i.imgur.com/0Lne5Vr.png"/>
<div>
<h1>Adding Game</h1>
<p id="demo">In this lecture, we will cover the game to add 2 numbers.</p>
</div>
<hr>
<div class="quiz">
<div class="quiz-content">
<div class="question">
What is the sum of <span class="text-info" id="field1">5</span> and <span class="text-info" id="field2">5</span>?
</div>
<div class="options">
<div class="fields animated zoomIn">
<div class="field-block" id="option1">
10
</div>
</div>
<div class="fields animated zoomIn">
<div class="field-block" id="option2">
10
</div>
</div>
<div class="fields animated zoomIn">
<div class="field-block" id="option3">
10
</div>
</div>
<div class="fields animated zoomIn">
<div class="field-block" id="option4">
10
</div>
</div>
</div>
<div class="after hide" id="after">
</div>
<div class="play-again">
<a class="button" onclick="startGame()">Play Again</a>
</div>
</div>
</div>
<script src='index.js'></script>
</body>
</html>
Here is a solution you can refer.
document.addEventListener("DOMContentLoaded", function(event) {
var num1 = Math.floor((Math.random() * 30) + 10);
var num2 = Math.floor((Math.random() * 30) + 10);
var result = num1 + num2;
document.getElementById('field1').innerHTML = num1;
document.getElementById('field2').innerHTML = num2;
var opts = [];
for(var i=0;i<3;i++){
opts.push(findRandom(result,opts));
}
opts.push(result);
opts.sort();
for(var i=1;i<5;i++){
document.getElementById('option'+i).innerHTML = opts[i-1];
}
});
function findRandom(n,opts){
var result = 0;
while(result !=n && result == 0){
result = Math.floor(Math.random() * (n + 1));
if(opts.indexOf(result) >0){
result = 0;
}
}
return result;
}
.App {
text-align: center;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 60px;
}
.App-header {
background-color: #222;
height: 180px;
padding: 20px;
color: white;
}
.App-intro {
font-size: large;
}
#keyframes App-logo-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.text-info {
color: #fff;
font-weight: bold;
font-size: 2.1rem;
}
.question {
font-size: 2rem;
}
.options {
margin: 5%;
display: flex;
margin-right: -12px;
margin-left: -12px;
flex-direction: row;
flex-wrap: wrap;
align-items: stretch;
flex: 1 0 auto;
}
.fields {
display: flex;
padding: 12px;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
flex: 1;
}
.field-block {
display: flex;
min-height: 160px;
padding: 10%;
flex-direction: row;
justify-content: center;
align-items: center;
/*flex: 1 0 auto;*/
border-radius: 4px;
background-color: #f9bad0;
font-size: 6rem;
color: #fff;
cursor: pointer;
}
.quiz {
color: #ddd;
margin: 2%;
background-color: #ec1561;
padding: 2%;
width: 90%;
position: relative;
}
.button {
display: flex;
height: 48px;
padding-right: 16px;
padding-left: 16px;
flex-direction: row;
justify-content: center;
align-items: center;
flex: 0 0 auto;
border-radius: 4px;
background-color: #2fcaaa;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .05), 0 2px 12px 0 rgba(0, 0, 0, .1);
transition: box-shadow 200ms ease-out;
color: #fff;
font-weight: 500;
text-align: center;
cursor: pointer;
}
.quiz .after {
position: absolute;
top: 5%;
left: 5%;
width: 90%;
height: 80%;
/*display: none;*/
color: #FFF;
text-align: center;
align-items: center;
justify-content: center;
display: flex;
opacity: 0.8;
font-size: 3rem;
}
.correct {
background-color: green;
}
.wrong {
background-color: #D91E18;
}
.hide {
display: none !important;
}
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Adding 2 Numbers | Happy Learning!</title>
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.2/animate.min.css">
</head>
<body>
<img alt="Join Slack" height="40" width="139" src="http://i.imgur.com/0Lne5Vr.png"/>
<div>
<h1>Adding Game</h1>
<p id="demo">In this lecture, we will cover the game to add 2 numbers.</p>
</div>
<hr>
<div class="quiz">
<div class="quiz-content">
<div class="question">
What is the sum of <span class="text-info" id="field1">5</span> and <span class="text-info" id="field2">5</span>?
</div>
<div class="options">
<div class="fields animated zoomIn">
<div class="field-block" id="option1">
10
</div>
</div>
<div class="fields animated zoomIn">
<div class="field-block" id="option2">
10
</div>
</div>
<div class="fields animated zoomIn">
<div class="field-block" id="option3">
10
</div>
</div>
<div class="fields animated zoomIn">
<div class="field-block" id="option4">
10
</div>
</div>
</div>
<div class="after hide" id="after">
</div>
<div class="play-again">
<a class="button">Play Again</a>
</div>
</div>
</div>
<script src='index.js'></script>
</body>
</html>
As per my comment, you will need to re-run that number generator to generate new and incorrect answers for the 3 remaining options. There are a few things that you want to watch out for:
You have to avoid collisions, i.e. do not generate numbers that are the same as the answer, or the same as any pre-generated incorrect options. We can make this simple check by using a while loop
You can use a generic function to generate num1 + num2, so that it can be re-used again to generate incorrect answers.
Instead of giving your options unique IDs, simply give them a generic class, e.g. <div class="field-block option"></div> . We want to be able to know how many options we have so that we generate correct number of answer + incorrect answers.
You might want to shuffle your answers array before appending them to the DOM, otherwise they will all have the first element containing the correct answer.
Side note: Although it is not mentioned in your original answer, I expected that you want to know which is the correct answer when a user click on that option. When a click event is fired from the option, you can simply get the index of the option, and check it against the answers array. If the option's index matches the index of the correct answer in the array, then you are good to go.
In the code snippet below, I have stripped the stylesheet and some unnecessary markup:
// FY shuffle
function shuffle(a) {
var j, x, i;
for (i = a.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
x = a[i];
a[i] = a[j];
a[j] = x;
}
}
// Function that generates all options
var generateAllOptions = function() {
// Number generator
var getRandomNumber = function() {
return Math.floor((Math.random() * 30) + 10);
};
// Get the question + correct answer
var num1 = getRandomNumber();
var num2 = getRandomNumber();
var correctAnswer = num1 + num2;
var answers = [correctAnswer];
// Update question
document.getElementById('field1').innerHTML = num1;
document.getElementById('field2').innerHTML = num2;
// Generate incorrect answers/options, but make sure there are no collisions
var options = document.querySelectorAll('.options .option');
while(answers.length < options.length) {
var incorrectAnswer = getRandomNumber() + getRandomNumber();
if (answers.indexOf(incorrectAnswer) === -1)
answers.push(incorrectAnswer);
}
// Shuffle answers
shuffle(answers);
// Store index of correct answer
var correctIndex = answers.indexOf(correctAnswer);
// Append shuffled answers to options
for (var i = 0; i < options.length; i++) {
var option = options[i];
// Write answer values into innerHTML
option.innerHTML = answers[i];
// Bind click event to all options, use IIFE!
(function(idx) {
option.addEventListener('click', function() {
if (idx === correctIndex) {
alert('You have selected the right answer!');
} else {
alert('That is an incorrect answer.');
}
});
})(i);
}
};
generateAllOptions();
.option {
font-weight: bold;
background-color: steelblue;
color: #fff;
border-radius: 4px;
padding: 10px;
margin: 5px;
}
<div>
<h1>Adding Game</h1>
<p id="demo">In this lecture, we will cover the game to add 2 numbers.</p>
</div>
<hr>
<div class="quiz">
<div class="quiz-content">
<div class="question">
What is the sum of <span class="text-info" id="field1">5</span> and <span class="text-info" id="field2">5</span>?
</div>
<div class="options">
<div class="fields animated zoomIn">
<div class="field-block option"></div>
</div>
<div class="fields animated zoomIn">
<div class="field-block option"></div>
</div>
<div class="fields animated zoomIn">
<div class="field-block option"></div>
</div>
<div class="fields animated zoomIn">
<div class="field-block option"></div>
</div>
</div>
<div class="after hide" id="after">
</div>
<div class="play-again">
<a class="button">Play Again</a>
</div>
</div>
</div>
Try this simple solution. This generates 4 unique random options out of which one option is the correct one. The option number of the correct answer is also random.
You only need to modify your js.
var num1 = Math.floor((Math.random() * 30) + 10);
var num2 = Math.floor((Math.random() * 30) + 10);
var result = num1 + num2;
var ansIndex=(Math.floor((Math.random()*10))%4)+1; //this index will be the position of the correct answer
var option=[];
//the below loop fills the options array with random but unique options
for(var i=0;i<4;i++){
var temp=Math.floor((Math.random() * 30) + 10);
if(final.indexOf(temp)==(-1)){
option.push(temp);
continue;
}
else{
i--;
}
}
//finally the correct option is overwritten
option[ansIndex-1]=result;
var answer=document.getElementsByClassName("field-block");
answer[0].innerHTML=option[0];
answer[1].innerHTML=option[1];
answer[2].innerHTML=option[2];
answer[3].innerHTML=option[3];
document.getElementById('field1').innerHTML = num1;
document.getElementById('field2').innerHTML = num2;

Categories