Codepen: https://codepen.io/jitifor864/pen/GRvvpeK?editors=1100
I'm trying to figure out some CSS errors I'm having. At the moment, the problems I'm having are:
The text that is being typed out isn't centered on top of the search bar
If the word gets too long, the bar beneath it begins to expand. How can I keep that bar constant size and not expand if the text gets long
I can't seem to make the blinker a tiny bit wider and stop at the end of the word (it looks like it goes one extra blank character)
Could I get some help on these? I'm sure these are 'small' fixes, just can't figure it out. Thanks!
// values to keep track of the number of letters typed, which quote to use. etc. Don't change these values.
var i = 0,
a = 0,
isBackspacing = false;
// Typerwrite text content. Use a pipe to indicate the start of the second line "|".
var textArray = [
"AskReddit", "AskMen", "Gaming", "FemaleFashionAdvice", "Nosleep", "LetsNotMeet", "Technology", "Funny", "Memes", "Politics", "News"
];
// Speed (in milliseconds) of typing.
var speedForward = 100, //Typing Speed
speedWait = 1000, // Wait between typing and backspacing
speedBackspace = 25; //Backspace Speed
//Run the loop
typeWriter("typewriter", textArray);
function typeWriter(id, ar) {
var element = $("#" + id),
aString = ar[a],
eHeader = element.children("h1"); //Header element
// Determine if animation should be typing or backspacing
if (!isBackspacing) {
// If full string hasn't yet been typed out, continue typing
if (i < aString.length) {
eHeader.text(eHeader.text() + aString.charAt(i));
i++;
setTimeout(function(){ typeWriter(id, ar); }, speedForward);
}
// If full string has been typed, switch to backspace mode.
else if (i == aString.length) {
isBackspacing = true;
setTimeout(function(){ typeWriter(id, ar); }, speedWait);
}
// If backspacing is enabled
} else {
// If either the header, continue backspacing
if (eHeader.text().length > 0) {
// If paragraph still has text, continue erasing, otherwise switch to the header.
if (eHeader.text().length > 0) {
eHeader.addClass("cursor");
eHeader.text(eHeader.text().substring(0, eHeader.text().length - 1));
}
setTimeout(function(){ typeWriter(id, ar); }, speedBackspace);
// If the head has no text, switch to next quote in array and start typing.
} else {
isBackspacing = false;
i = 0;
a = (a + 1) % ar.length; //Moves to next position in array, always looping back to 0
setTimeout(function(){ typeWriter(id, ar); }, 50);
}
}
}
.parent {
display: flex;
height: 100vh;
align-items: center;
justify-content: center;
}
.search-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.typewriter-wrapper {
width: 100%;
text-align: center;
}
.typewriter {
display: inline-block;
overflow: hidden;
border-right: .15em solid orange;
white-space: nowrap;
letter-spacing: .15em;
animation:
typing 2s steps(40, end),
blink-caret .50s step-end infinite;
}
.search-form {
display: flex;
justify-content: center;
width: 100%
}
.search-input {
-webkit-appearance: none;
background-clip: padding-box;
background-color: white;
vertical-align: middle;
border-radius: 0.25rem;
border: 1px solid #e0e0e5;
font-size: 1rem;
line-height: 2;
padding: 0.375rem 1.25rem;
-webkit-transition: border-color 0.2s;
-moz-transition: border-color 0.2s;
transition: border-color 0.2s;
margin-bottom: 0;
flex-grow: 1;
flex-shrink: 0;
flex-basis: auto;
align-self: center;
height: 51px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.search-button {
height: 51px;
margin: 0;
padding: 1rem 1.3rem;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-top-right-radius: 0.25rem;
border-bottom-right-radius: 0.25rem;
font-size: 1rem;
display: inline-block;
font-weight: 600;
font-size: 0.8rem;
line-height: 1.15;
letter-spacing: 0.1rem;
background: #F95F5F;
color: #292826;
border: 1px solid transparent;
vertical-align: middle;
text-shadow: none;
-webkit-transition: all 0.2s;
-moz-transition: all 0.2s;
transition: all 0.2s;
}
.cursor::after {
content:'';
display:inline-block;
margin-left:3px;
background-color:white;
animation-name:blink;
animation-duration:0.5s;
animation-iteration-count: infinite;
}
h1.cursor::after {
height:24px;
width:13px;
}
#keyframes blink-caret {
from, to {
border-color: transparent
}
50% {
border-color: orange;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="parent">
<div class="search-container">
<div class="typewriter-container">
<div class="typewriter" id="typewriter">
<h1 class="cursor"> </h1>
</div>
<form class="search-form" method="GET" action="{% url 'ssearch' %}">
<input class="search-input" type="search" name="subreddit">
<button class="search-button" type="submit"> Search </button>
<!-- <i class="fa fa-search"></i> -->
</form>
</div>
</div>
1 - to center the text you should display .typewriter-container as flex with direction column and align-items as center.
.typewriter-container {
/* this are new CSS selector rules you'll need to add */
display: flex;
flex-direction: column;
align-items: center;
}
2 - the browser has a default width for input fields which is setting the initial width of your form. Give the form a fixed width, eg. 330px.
.search-form {
/* Modify this existing rules to set the form width */
display: flex;
justify-content: center;
width: 330px;
}
3 - the blinking border is being spaced by the ::after pseudo element in your h1. Remove the margin of the ::after and set its width to 0 (or whatever spacing you want).
h1.cursor::after {
/* Modify this existing rules to set the margin and width */
height: 24px;
width: 0;
margin: 0;
}
A forked codepen with the changes can be found here https://codepen.io/jla91ab37103f/pen/GRvvqWe
Related
I can't figure out why I'm getting this little bit of green when the window is an odd number of pixels wide. I think it has something to do with sub-pixel rendering, but I'm just not sure where the green is coming from. It's just the 2nd div too which is weird.
I have some script that is animating the BG of this div. I'm sure this is part of the issue, but I can't figure out why it's only happening to my 2nd div.
I tried to manually set the width of this div, but I was hoping it would be responsive and scale with the window size.
let currentStage = 1
function performAction(selectedStage) {
currentStage = selectedStage
let stages = document.body.getElementsByClassName('stage-flow-item')
let stageLines = document.body.getElementsByClassName('stage-flow-line')
console.log("selectedStage: " + selectedStage)
for (let stage of stages) {
if (stage.id > currentStage) {
stage.classList.remove('completed')
stage.classList.add('active')
} else {
stage.classList.remove('active')
stage.classList.add('completed')
}
}
for (let stageLine of stageLines) {
if (stageLine.id > currentStage) {
stageLine.classList.remove('lineCompleted')
stageLine.classList.add('lineActive')
} else {
stageLine.classList.remove('lineActive')
stageLine.classList.add('lineCompleted')
}
}
}
.stage-flow-container {
display: flex;
justify-content: space-between;
align-items: center;
height: 70px;
padding: 0 30px;
}
.stage-flow-item {
width: 70px;
height: 70px;
min-width: 70px;
border-radius: 50%;
background-color: #ddd;
display: flex;
justify-content: center;
align-items: center;
font-size: 18px;
color: #fff;
cursor: pointer;
}
.stage-flow-item.active {
background-color: #ddd;
}
.stage-flow-item.completed {
background-color: #6ab04c;
}
.stage-flow-line {
width: calc(100vw);
height: 6px;
background-color: #ddd;
/* default color */
background: linear-gradient(to left, #ddd 50%, #6ab04c 50%) right;
position: relative;
background-size: 200%;
transition: .5s ease-out;
}
.stage-flow-line.lineCompleted {
background-position: left;
background-color: #6ab04c;
}
.stage-flow-line.lineActive {
background-position: right;
background-color: #ddd;
}
<div class="stage-flow-container">
<div id=1 class="stage-flow-item" onclick="performAction(1)">1</div>
<div id=1 class="stage-flow-line"></div>
<div id=2 class="stage-flow-item" onclick="performAction(2)">2</div>
<div id=2 class="stage-flow-line"></div>
<div id=3 class="stage-flow-item" onclick="performAction(3)">3</div>
</div>
I'm not sure if this is on the right track, but I'd eliminate the odd 100vw width on the connectors and instead make them flex. I'd then remove the 200% background size multiplier. By setting the gradient points to 100% the problem is gone. I really don't know if this covers your use case, though.
I converted from background gradient to a pseudo-element solution for the color transition. I think it's simpler. You'd probably have to use CSS animations (as opposed to simple transitions) to make it work otherwise. Of course, you could apply the same principle to the stage items as well, implementing a delay to crate a consistent animation across the item and the line.
Note that duplicated ID values are invalid in HTML. They must be unique. I've refactored to use data attributes instead and an event listener instead of inline JavaScript.
const stageEls = document.querySelectorAll('.stage-flow-item')
const lineEls = document.querySelectorAll('.stage-flow-line')
let currentStage = 1
stageEls.forEach(el => {
el.addEventListener('click', () => {
performAction(el.dataset.stage)
})
})
function performAction(selectedStage) {
currentStage = selectedStage
for (let el of stageEls) {
if (el.dataset.stage > currentStage) {
el.classList.remove('completed')
el.classList.add('active')
} else {
el.classList.remove('active')
el.classList.add('completed')
}
}
for (let el of lineEls) {
if (el.dataset.stage > currentStage) {
el.classList.remove('lineCompleted')
el.classList.add('lineActive')
} else {
el.classList.remove('lineActive')
el.classList.add('lineCompleted')
}
}
}
.stage-flow-container {
display: flex;
align-items: center;
height: 70px;
padding: 0 30px;
}
.stage-flow-item {
width: 70px;
height: 70px;
min-width: 70px;
border-radius: 50%;
background-color: #ddd;
display: flex;
justify-content: center;
align-items: center;
font-size: 18px;
color: #fff;
cursor: pointer;
}
.stage-flow-item.active {
background-color: #ddd;
}
.stage-flow-item.completed {
background-color: #6ab04c;
}
.stage-flow-line {
flex: 1;
height: 6px;
background: #ddd;
position: relative;
}
.stage-flow-line::after {
position: absolute;
content: '';
top: 0;
left: 0;
width: 0;
height: 100%;
background: #6ab04c;
transition: all 0.5s ease-out;
}
.stage-flow-line.lineCompleted::after {
width: 100%;
}
<div class="stage-flow-container">
<div data-stage=1 class="stage-flow-item">1</div>
<div data-stage=1 class="stage-flow-line"></div>
<div data-stage=2 class="stage-flow-item">2</div>
<div data-stage=2 class="stage-flow-line"></div>
<div data-stage=3 class="stage-flow-item">3</div>
</div>
Id like to make a component in react that allows me to have a textarea with tags that can be inserted when clicked from a dropdown. Id also like this textarea to be able to mix text aswell. I have currently been trying to use tagify with react but I cant seem to figure out a way to the tagify's function that adds the tag to be accessed by the onClick that is connected to the dropdown.
Any ideas?
I believe you can get your answer in this URL of other question asked on StackOverflow https://stackoverflow.com/a/38119725/15405352
var $container = $('.container');
var $backdrop = $('.backdrop');
var $highlights = $('.highlights');
var $textarea = $('textarea');
var $toggle = $('button');
// yeah, browser sniffing sucks, but there are browser-specific quirks to handle that are not a matter of feature detection
var ua = window.navigator.userAgent.toLowerCase();
var isIE = !!ua.match(/msie|trident\/7|edge/);
var isWinPhone = ua.indexOf('windows phone') !== -1;
var isIOS = !isWinPhone && !!ua.match(/ipad|iphone|ipod/);
function applyHighlights(text) {
text = text
.replace(/\n$/g, '\n\n')
.replace(/[A-Z].*?\b/g, '<mark>$&</mark>');
if (isIE) {
// IE wraps whitespace differently in a div vs textarea, this fixes it
text = text.replace(/ /g, ' <wbr>');
}
return text;
}
function handleInput() {
var text = $textarea.val();
var highlightedText = applyHighlights(text);
$highlights.html(highlightedText);
}
function handleScroll() {
var scrollTop = $textarea.scrollTop();
$backdrop.scrollTop(scrollTop);
var scrollLeft = $textarea.scrollLeft();
$backdrop.scrollLeft(scrollLeft);
}
function fixIOS() {
// iOS adds 3px of (unremovable) padding to the left and right of a textarea, so adjust highlights div to match
$highlights.css({
'padding-left': '+=3px',
'padding-right': '+=3px'
});
}
function bindEvents() {
$textarea.on({
'input': handleInput,
'scroll': handleScroll
});
$toggle.on('click', function() {
$container.toggleClass('perspective');
});
}
if (isIOS) {
fixIOS();
}
bindEvents();
handleInput();
#import url(https://fonts.googleapis.com/css?family=Open+Sans);
*, *::before, *::after {
box-sizing: border-box;
}
body {
margin: 30px;
background-color: #f0f0f0;
}
.container, .backdrop, textarea {
width: 460px;
height: 180px;
}
.highlights, textarea {
padding: 10px;
font: 20px/28px 'Open Sans', sans-serif;
letter-spacing: 1px;
}
.container {
display: block;
margin: 0 auto;
transform: translateZ(0);
-webkit-text-size-adjust: none;
}
.backdrop {
position: absolute;
z-index: 1;
border: 2px solid #685972;
background-color: #fff;
overflow: auto;
pointer-events: none;
transition: transform 1s;
}
.highlights {
white-space: pre-wrap;
word-wrap: break-word;
color: transparent;
}
textarea {
display: block;
position: absolute;
z-index: 2;
margin: 0;
border: 2px solid #74637f;
border-radius: 0;
color: #444;
background-color: transparent;
overflow: auto;
resize: none;
transition: transform 1s;
}
mark {
border-radius: 3px;
color: transparent;
background-color: #b1d5e5;
}
button {
display: block;
width: 300px;
margin: 30px auto 0;
padding: 10px;
border: none;
border-radius: 6px;
color: #fff;
background-color: #74637f;
font: 18px 'Opens Sans', sans-serif;
letter-spacing: 1px;
appearance: none;
cursor: pointer;
}
.perspective .backdrop {
transform:
perspective(1500px)
translateX(-125px)
rotateY(45deg)
scale(.9);
}
.perspective textarea {
transform:
perspective(1500px)
translateX(155px)
rotateY(45deg)
scale(1.1);
}
textarea:focus, button:focus {
outline: none;
box-shadow: 0 0 0 2px #c6aada;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="backdrop">
<div class="highlights"></div>
</div>
<textarea>This demo shows how to highlight bits of text within a textarea. Alright, that's a lie. You can't actually render markup inside a textarea. However, you can fake it by carefully positioning a div behind the textarea and adding your highlight markup there. JavaScript takes care of syncing the content and scroll position from the textarea to the div, so everything lines up nicely. Hit the toggle button to peek behind the curtain. And feel free to edit this text. All capitalized words will be highlighted.</textarea>
</div>
<button>Toggle Perspective</button>
Reference- https://codepen.io/lonekorean/pen/gaLEMR for example
I am trying to a make carousel using pure Javascript. I successfully manage to slide the carousel and have created left and right buttons.
I took my slide functions and added them to the button on-click event-listener, but I have problems when I implement the function on my buttons. It does not behave as expected. My code is below, how can I fix this?
const images = document.getElementById('imgs'); //here
const allImages = document.querySelectorAll('#imgs img');
const leftBtn = document.getElementById('left');
const rightBtn = document.getElementById('right');
let index = 0;
function changeSliderPage() {
const dot = [...document.getElementsByClassName('star')];
index++;
if (index > allImages.length - 1) {
index = 0
}
imgs.style.transform = `translateX(${-index * 500}px)`;
dot.forEach((dot, i) => {
if (i === index) {
dot.classList.add('active')
} else {
dot.classList.remove('active')
}
});
};
allImages.forEach(i => {
const elem = document.createElement('div');
elem.classList.add('star');
document.body.appendChild(elem)
});
rightBtn.onclick = () => {
changeSliderPage(index + 1);
}
leftBtn.onclick = () => {
changeSliderPage(index - 1);
}
let x = setInterval(changeSliderPage, 100000);
images.onmouseover = () => {
clearInterval(x)
}
images.onmouseout = () => {
x = setInterval(changeSliderPage, 2000);
}
*{
box-sizing: border-box;
}
body {
margin: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.carousel {
overflow: hidden;
width: 500px;
height: 500px;
box-shadow: 2px 2px 5px rgba(0, 0, 0, .3);
border-radius: 5px;
}
.image-container {
display: flex;
transition: transform 300ms linear;
transform: translateX(0);
}
img {
width:500px;
height: 500px;
object-fit: cover;
}
.star{
cursor: pointer;
height: 15px;
width: 15px;
margin: 0 10px;
border-radius: 50%;
display: inline-block;
transition: background-color 0.6s ease;
background-color: #eeeeee;
}
.star.active{
background-color: red;
}
button{
cursor: pointer;
position: relative;
font-size: 18px;
transition: 0.6s ease;
user-select: none;
height: 50px;
width: 40px;
display: flex;
justify-content: center;
align-items: center;
align-content: center;
top: calc(50% - 25px);
}
button:hover {
background-color: rgba(0,0,0,0.8);
};
button.left {
border-radius: 3px 0 0 3px;
right: 0;
}
button.left {
border-radius: 3px 0 0 3px;
left: 0;
}
<button id="left">❮</button>
<button id="right">❯</button>
<div class="carousel">
<div class="image-container" id="imgs" >
<img src="https://images.unsplash.com/photo-1599736375341-51b0a848f3c7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60" alt="">
<img src="https://images.unsplash.com/photo-1516026672322-bc52d61a55d5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60" alt="">
<img src="https://images.unsplash.com/photo-1573081586928-127ecc7948b0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60" alt="">
<img src="https://images.unsplash.com/flagged/photo-1572850005109-f4ac7529bf9f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60" alt="">
</div>
</div>
Logic that I use with carousels:
for example you have 4 images:
[1][2][3][4]
I have an animation for sliding every image, I add 5th image which is same as image no 1:
[1][2][3][4][1]
Imagine cursor which shows what image is currently displayed, Ill mark cursor as ! !
So at begin:
[!1!][2][3][4][1]
Now the slider moves on...
[1][!2!][3][4][1]
etc...
It moves to last image:
[1][2][3][4][!1!]
And now it has to move under the hood from last image to first image, but without any animation so the whole change is not visible by user:
[!1!][2][3][4][5]
This way you can get inifinite carousel, just need to check in javascript if current image is last one and you want to slide right -> no animation. Same if you are on 1st image and want to slide left.
I'm creating an Etch-a-Sketch for The Odin Project using HTML, CSS, & JavaScript. I'm having some difficulties with the JavaScript for the "shader pen" I've implemented.
Note: The "pens" described in this question are not to be confused with code pens.
There are two pens: a black pen that turns a cell black and the shader pen which, on the first pass, turns the cell black but changes the opacity to 0.1 so that it's almost transparent. This creates the illusion of slight darkening since the background behind the cell is the same as the original color of the cell. After each subsequent pass with the shader pen, the opacity increases by 0.1.
The shader pen is the default pen. It works fine at first, but when I click on the black pen and then go back to the shader pen, the shader pen increases the opacity once and stops. It also overrides the black cells.
Here's how I set up the pens:
makeGrid(16); // generate grid
pen("shader"); // default pen: shader
document.querySelector('#black').addEventListener("click", e => pen("black"));
document.querySelector('#shader').addEventListener("click", e => pen("shader"));
function pen(selected) {
let cells = document.querySelectorAll('div.cell');
cells.forEach(cell => {
cell.addEventListener('mouseover', function(e) {
if (selected == "black") {
// turns cell black
cell.classList.remove('shade');
cell.style.backgroundColor = '#101010';
cell.style.opacity = '1';
console.log('Make cell black')
} else if (selected == "shader") {
let opacity = cell.style.opacity;
if (cell.classList.contains("shade")) {
// increases opacity by 0.1
cell.style.opacity = (Number(opacity) + 0.1);
console.log('If there is shade, increase opacity.')
} else {
// turns cell to 0.1 opacity
cell.classList.add('shade');
cell.setAttribute('style', 'opacity:0.1');
cell.style.backgroundColor = '#101010';
console.log('Else, add shade class.')
}
}
})
});
}
I ran this code with a console.log statement in each conditional block and it looks like the black pen is still running when I go back to using the shader pen. My best guess is that this is what's causing the problem, but I'm not sure how to fix it.
I've created a CodePen with the rest of the code so you can see it in action. As previously mentioned, the default pen is the shader. It works as intended at the beginning, but it just stops after the black pen is selected.
Any help or guidance would be greatly appreciated.
There's so many problems with your code, The main one is when you change the pen type (when you call the pen() function) you add a new mouseover event listener.
Let's walk through it
Your code starts by calling pen('shader') making the shader the default pen type
pen("shader");
let's look at the pen() definition
function pen(selected) {
let cells = document.querySelectorAll('div.cell');
cells.forEach(cell => {
cell.addEventListener('mouseover', function(e){
...
})
});
}
What happens is the event listener takes the string shader and closes on it (This is called a closure) for that instance now whenever you hover over a cell the pen type will be shader
Now when you select the black pen you call the function pen() again adding a new set of event listeners.
This does not replace the old event listener with the new one, it stacks them like calling two functions one after the other.
Now when you hover over a cell the first event listener with the pen type shader will fire and then the second one with the black pen will fire after it, you can see this in the console you see two messages being printed from both if statement conditions.
Now when you pick the shader again you add a new set of event listeners with the shader pen type, what happens is the very first event listener will fire setting opacity to 0.1 then comes the black one removing the shade class then comes the third listener again checking if the cell has the class shade if(cell.classList.contains("shade")) which is false then falls down to the last else statement.
There's a ton of ways to fix this, but seeing that you're a beginner i will give you simple solutions
First make the pen type a global variable and on each button click you change it
Second add only one event listener to all cels at the start of your code and have them check the global variable
Demo
let penType = 'shader';
makeGrid(4); // generate grid
document.querySelector('#black').addEventListener("click", e => penType = "black");
document.querySelector('#shader').addEventListener("click", e => penType = "shader");
// generate grid
function makeGrid(dimension) {
const canvas = document.querySelector('#canvas');
const canvasWidth = document.getElementById("canvas").offsetWidth;
const cellWidth = canvasWidth / dimension;
for (let x = 1; x <= dimension * dimension; x++) {
const makeCell = document.createElement('div');
makeCell.classList.add('cell');
canvas.appendChild(makeCell);
};
canvas.style.gridTemplateRows = `repeat(${dimension}, ${cellWidth}px [row-start]`;
canvas.style.gridTemplateColumns = `repeat(${dimension}, ${cellWidth}px [column-start]`;
// after all cells have been generated add the event listener
// this is not the most optimize version of this but it gets the job done
let cells = document.querySelectorAll('div.cell');
cells.forEach(cell => {
cell.addEventListener('mouseover', function(e) {
console.log(penType)
if (penType == "black") { // turns cell black
cell.classList.remove('shade');
cell.style.backgroundColor = '#101010';
cell.style.opacity = '1';
console.log('Make cell black')
} else if (penType == "shader") { // turns cell 0.1
let opacity = cell.style.opacity;
if (cell.classList.contains("shade")) {
cell.style.opacity = (Number(opacity) + 0.1);
console.log('If there is shade, increase opacity.')
} else {
cell.classList.add('shade');
cell.setAttribute('style', 'opacity:0.1');
cell.style.backgroundColor = '#101010';
console.log('Else, add shade class.')
}
}
})
});
}
body {
margin: 0;
background-color: #514c53;
color: #fff;
font-size: 18pt;
}
/* DIV STYLING */
#container {
margin: 20px auto;
max-width: 400px;
display: flex;
flex-wrap: wrap;
}
.full {
width: 100%;
}
.left {
width: 75%;
margin-top: 15px;
}
.right {
width: 25%;
text-align: right;
}
#canvas {
display: grid;
flex-wrap: wrap;
grid-template-rows: repeat(16, 30px [row-start]);
grid-template-columns: repeat(16, 30px [col-start]);
background: #e8dfd6;
}
.cell {
background: #e8dfd6;
}
/* CELL SHADING */
.black {
background-color: #101010;
}
.shade {
background-color: #101010;
width: 100%;
height: 100%;
}
/* ELEMENT STYLING */
button {
font-weight: 700;
margin: 10px 0;
background-color: #b9967d;
color: #fff;
padding: 0px 15px;
text-shadow: 1px 1px #2e2e2e;
box-shadow: 3px 3px #2e2e2e;
border-radius: 5px;
border: 0px;
text-transform: uppercase;
height: 30px;
outline: none;
transition: 0.3s;
}
button:hover {
cursor: pointer;
background-color: #aa8062;
box-shadow: 0px -3px #2e2e2e;
}
button:active {
background-color: #997963;
}
.circle {
float: left;
height: 15px;
width: 15px;
border: 1px solid #fff;
border-radius: 50%;
margin: 1px 5px 0 0;
}
.circle:hover {
cursor: pointer;
/*transition: 0.3s;
border: 2px solid #fff;*/
}
.meaning {
float: left;
font-size: .542em;
text-transform: uppercase;
padding-top: 2px;
margin-right: 10px;
}
#black {
background-color: #101010;
}
#shader {
background-image: linear-gradient(#807b76, #dfd8d0);
}
<div id="container">
<div class="left" style="margin-bottom:10px">
<div class="circle" id="black"></div>
<div class="meaning">Black</div>
<div class="circle" id="shader"></div>
<div class="meaning">Shader</div>
</div>
<div id="canvas" class="full">
<!--grid generates here-->
</div>
</div>
<!--container-->
One tiny problem with your logic, when go to the black pen you don't have to remove the shade class because then when you come back to the shade pen and you hover over a black cell it sets opacity back to 0.1
I changed a lot, so I can't describe all of the changes, but here's a link to my forked codepen:
https://codepen.io/scoutskylar/pen/ExxWPMb
The biggest change was removing the black and shade classes and just changing the opacity instead. (All of the cells now start with 0 opacity.) I also added erasers just for fun. :) The other big thing was making the event handlers once instead of adding a handler every time the pen changed.
Here's the code:
var currentPen = "shader"; // default pen
makeGrid(16); // generate grid
document.querySelector("#black").addEventListener("click", e => {
currentPen = "black";
});
document.querySelector("#shader").addEventListener("click", e => {
currentPen = "shader";
});
document.querySelector("#eraser").addEventListener("click", e => {
currentPen = "eraser";
});
document.querySelector("#deshader").addEventListener("click", e => {
currentPen = "deshader";
});
// generate grid
function makeGrid(dimension) {
const canvas = document.querySelector("#canvas");
const canvasWidth = document.getElementById("canvas").offsetWidth;
const cellWidth = canvasWidth / dimension;
for (let x = 1; x <= dimension * dimension; x++) {
const makeCell = document.createElement("div");
makeCell.classList.add("cell");
canvas.appendChild(makeCell);
}
canvas.style.gridTemplateRows = `repeat(${dimension}, ${cellWidth}px [row-start]`;
canvas.style.gridTemplateColumns = `repeat(${dimension}, ${cellWidth}px [column-start]`;
let cells = document.querySelectorAll("div.cell");
cells.forEach(cell => {
cell.addEventListener("mouseover", function(e) {
if (currentPen == "black") {
// turns cell black
cell.style.opacity = "1";
// console.log("Make cell black");
} else if (currentPen == "shader") {
// turns cell 0.1 opacity darker
let opacity = Number(cell.style.opacity);
cell.style.opacity = opacity >= 1 ? "1" : opacity + 0.1 + "";
// console.log("Increase opacity");
} else if (currentPen == "eraser") {
// resets color
cell.style.opacity = "0";
// console.log("Reset opacity");
} else if (currentPen == "deshader") {
// turns cell 0.1 opacity lighter
let opacity = Number(cell.style.opacity);
cell.style.opacity = opacity <= 0 ? "0" : opacity - 0.1 + "";
// console.log("Decrease opacity");
}
});
});
}
body {
margin: 0;
background-color: #514c53;
color: #fff;
font-size: 18pt;
}
/* DIV STYLING */
#container {
margin: 20px auto;
max-width: 400px;
display: flex;
flex-wrap: wrap;
}
.full {
width: 100%;
}
.left {
width: 75%;
margin-top: 15px;
}
.right {
width: 25%;
text-align: right;
}
#canvas {
display: grid;
flex-wrap: wrap;
grid-template-rows: repeat(16, 30px [row-start]);
grid-template-columns: repeat(16, 30px [col-start]);
background: #e8dfd6;
}
.cell {
/* background: #e8dfd6; */
background-color: #101010;
opacity: 0;
}
/* CELL SHADING */
/* .black {
background-color: #101010;
} */
/* .shade {
background-color: #101010;
width: 100%;
height: 100%;
} */
/* ELEMENT STYLING */
button {
font-weight: 700;
margin: 10px 0;
background-color: #b9967d;
color: #fff;
padding: 0px 15px;
text-shadow: 1px 1px #2e2e2e;
box-shadow: 3px 3px #2e2e2e;
border-radius: 5px;
border: 0px;
text-transform: uppercase;
height: 30px;
outline: none;
transition: 0.3s;
cursor: pointer;
}
button:hover {
background-color: #aa8062;
box-shadow: 0px -3px #2e2e2e;
}
button:active {
background-color: #997963;
}
.circle {
display: inline-block;
height: 15px;
width: 15px;
border: 1px solid #fff;
border-radius: 50%;
margin: 1px 5px 0 0;
cursor: pointer;
}
.meaning {
font-size: 0.542em;
text-transform: uppercase;
padding-top: 2px;
margin-right: 10px;
cursor: pointer;
}
.penbutton {
display: inline-block;
}
#black .circle {
background-color: #101010;
}
#shader .circle {
background: linear-gradient(#807b76, #dfd8d0);
}
#eraser .circle {
background-color: #e8dfd6;
}
#deshader .circle {
background: linear-gradient(#9e9a96, #e8dfd6);
}
<head>
<title>Etch-a-Sketch</title>
</head>
<body>
<div id="container">
<div class="left" style="margin-bottom:10px">
<span id="black" class="penbutton">
<span class="circle"></span>
<span class="meaning">Black</span>
</span>
<span id="shader" class="penbutton">
<span class="circle"></span>
<span class="meaning">Shader</span>
</span>
<span id="eraser" class="penbutton">
<span class="circle"></span>
<span class="meaning">Super Eraser</span>
</span>
<span id="deshader" class="penbutton">
<span class="circle"></span>
<span class="meaning">Light Eraser</span>
</span>
</div>
<div id="canvas" class="full">
<!--grid generates here-->
</div>
</div>
<!--container-->
</body>
<script src="assets/js/eas.js"></script>
I have this Slider example created with pure JS.
The slider is working great. The only thing left to do would be to activate the three dots so when the 1st slide opens, 1st dot activates, showing different color than the other dots, and so on. Also, you should be able to open the correct slide when clicking dots, so 1st dot opens 1st slide, 2nd dot 2nd slide, and so on.
Could you help me to achieve this? You can find the source code below.
const nextBtn = document.querySelector('.nextBtn');
const prevBtn = document.querySelector('.prevBtn');
const container = document.querySelector('.images');
const offers = document.getElementById('offers');
const link = document.getElementById('links');
let colors = ['#7f86ff', '#2932d1', '#00067f'];
let currentSlide = 0;
let texts = ['Change1', 'Change2', 'Change3'];
let currentText = 0;
let links = ['Link1', 'Link2', 'Link3'];
let currentLink = 0;
function updateSlide(direction) {
currentSlide =
(colors.length + currentSlide + direction)
% colors.length;
container.style.backgroundColor = colors[currentSlide];
container.animate([{opacity:'0.1'}, {opacity:'1.0'}],
{duration: 200, fill:'forwards'})
}
function updateText(direction) {
currentText =
(texts.length + currentText + direction)
% texts.length;
offers.innerHTML = texts[currentText];
offers.animate([{transform:'translateY(-50px)', opacity:'0.0'}, {transform:'translateY(0)', opacity:'1.0'}],
{duration: 200, fill:'forwards'})
}
function updateLink(direction) {
currentLink =
(links.length + currentLink + direction)
% links.length;
link.innerHTML = links[currentLink];
link.animate([{transform:'scale(0,0)'}, {transform:'scale(1.1)'}],
{duration: 200, fill:'forwards'})
}
updateSlide(0);
updateText(0);
updateLink(0);
nextBtn.addEventListener('click', nextSlide);
prevBtn.addEventListener('click', prevSlide);
function nextSlide() {
updateSlide(+1);
updateText(+1);
updateLink(+1);
clearInterval(myInterval);
}
function prevSlide() {
updateSlide(-1);
updateText(-1);
updateLink(-1);
clearInterval();
clearInterval(myInterval);
}
var myInterval = window.setInterval(function(){
updateSlide(+1),updateText(+1),updateLink(+1); },
8000);
body {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background-color: lightblue;
}
.images {
background-color: #4047c9;
flex: 0 0 80%;
min-height: 70vh;
border-radius: 10px;
position: relative;
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: center;
color: white;
}
#links {
text-decoration: none;
color: white;
border: solid 2px white;
border-radius: 3px;
padding: 5px 10px;
}
#links:hover {
background-color: #000238;
}
a {
color: white;
text-decoration: none;
}
.dots {
display: flex;
margin-top: 120px;
margin-bottom: 50px;
}
#dot1, #dot2, #dot3 {
width: 20px;
height: 20px;
background-color: rgb(147, 151, 249);
border-radius: 50%;
margin: 0px 5px;
cursor: pointer;
}
#dot1:active, #dot2:active, #dot3:active {
background-color: #fff;
}
.btn {
display: inline-block;
background: white;
color: black;
padding: 10px;
border: none;
cursor: pointer;
}
.prevBtn {
position: absolute;
top: 50%;
left: 0;
transform: translate(-50%, -50%);
}
.nextBtn {
position: absolute;
top: 50%;
right: 0;
transform: translate(50%, -50%);
}
.btn:active {
background-color: grey;
color: white;
}
.btn:hover {
background-color: grey;
color: white;
}
<body>
<div class="images">
<button type="button" class="btn prevBtn">Prev Btn</button>
<button type="button" class="btn nextBtn">Next Btn</button>
<h1 id="offers">Changing text</h1>
Links
<div class="dots">
<span id="dot1"></span>
<span id="dot2"></span>
<span id="dot3"></span>
</div>
</div>
</body>
First off, according to
https://developer.mozilla.org/en-US/docs/Web/CSS/:active
The :active CSS pseudo-class represents an element (such as a button) that is being activated by the user.
So if you want your dots to be active, you’ll have to write a different way of giving them an active state since they are currently <span> tags, I would recommend giving them a class of .active, and adding in Javascript code to add that class on to them, or adding in that style programmatically within the Javascript function.
Based on your other request though, you will most likely also have to make the dots an <a> tag or something along those lines so you can add functionality on to them to let clicking on the dots bring you to any slide. Something probably along the lines of:
function dot1Click() {
updateSlide(1);
updateText(1);
updateLink(1);
dot1.style.backgroundColor = #fff;
}
Then you should have something along the lines of what you want. I'll return to this question when I have more time to iron out a code snippet, but I wanted to give you something to help you get started!