How can I make this toggle switch responsive? - javascript

I'm trying to make (using React) a toggle switch I found responsive so that I can put it in my app but I'm having trouble refactoring it to resize based on content inside and device width/height.
import { useState } from "react";
import styles from "./ToggleSwitch.module.css";
export default function ToggleSwitch({ choice1, choice2 }) {
const [value, setValue] = useState(choice2);
function handleClick() {
setValue(value => (value === choice1 ? choice2 : choice1));
}
function styleToggleSwitch(value) {
if (value === choice1) {
return { clipPath: "inset(0 0 0 50%)", backgroundColor: "#eb9b30" };
} else {
return { clipPath: "inset(0 50% 0 0)", backgroundColor: "#1464cf" };
}
}
return (
<div id={styles["container"]} onClick={handleClick}>
<div className={styles["inner-container"]}>
<div className={styles["toggle"]}>
<p>{choice2}</p>
</div>
<div className={styles["toggle"]}>
<p>{choice1}</p>
</div>
</div>
<div
className={styles["inner-container"]}
style={
value === choice1
? styleToggleSwitch(choice1)
: styleToggleSwitch(choice2)
}
>
<div className={styles["toggle"]}>
<p>{choice2}</p>
</div>
<div className={styles["toggle"]}>
<p>{choice1}</p>
</div>
</div>
</div>
);
}
#import url("https://fonts.googleapis.com/css?family=Asap:400,500,700");
#container {
width: 160px;
height: 36px;
margin: auto;
position: relative;
border-radius: 0.5rem;
overflow: hidden;
user-select: none;
cursor: pointer;
font-family: "Asap", sans-serif;
}
.inner-container {
position: absolute;
left: 0;
top: 0;
width: inherit;
height: inherit;
text-transform: uppercase;
font-size: 0.6em;
letter-spacing: 0.2em;
}
.inner-container:first-child {
background: #e9e9e9;
color: #a9a9a9;
}
.inner-container:nth-child(2) {
background: dodgerblue;
color: white;
clip-path: inset(0 50% 0 0);
transition: 0.3s cubic-bezier(0, 0, 0, 1);
}
.toggle {
width: 50%;
position: absolute;
height: inherit;
display: flex;
box-sizing: border-box;
}
.toggle p {
margin: auto;
}
.toggle:nth-child(1) {
right: 0;
}
When I select the toggle switch buttons they get either the .first or .second class to change their styles. Sorry for using pastebin but Stack Overflow wouldn't allow me to post this much code. Thanks for the help in advance!

Related

Changing Header/Navbar Elements on Scroll

Currently struggling to add the .is-active class to my header via javascript. If you add "is-active" to the header classes it works well. But I can't seem to work it out in javascript.
I just want the class to be added as soon as you start scrolling, and then removed when returning to the top.
Appreciate all the help!
HTML:
<header class="header">
<div class="header-nav flex container">
<figure class="header-logo">
<a href="#">
<img class="header-logo-light" src="images/logoWhite.png" alt="San Miguel Services Logo">
<img class="header-logo-dark" src="images/logoDark.png" alt="San Miguel Services Logo">
</a>
</figure>
<nav class="header-menu flex">
<div class="header-menu-li">
WELCOME
SERVICES
ABOUT
PORTFOLIO
CONTACT
</div>
</nav>
<div class="header-btn">
<button href="#" class="button header-btn">REQUEST A QUOTE</button>
</div>
</div>
</header>
CSS:
.header {
position: fixed;
z-index: 1;
width: 100vw;
line-height: 18px;
}
.header .header-logo-dark {
opacity: 0;
display: none;
}
.header .header-logo-light {
opacity: 1;
display: block;
}
.header.is-active .header-logo-dark {
opacity: 1;
display: block;
}
.header.is-active .header-logo-light {
opacity: 0;
display: none;
}
.header.is-active .header-menu-li a {
color: $darkBlue;
&::before {
background: linear-gradient(to right, $mediumGreen, $lightGreen);
}
}
.header.is-active .header-btn button {
background: $mediumGreen;
color: $white;
transition: 300ms ease-in-out;
&:hover {
box-shadow: inset 0 0 0 2px $darkBlue;
color: $darkBlue;
background: transparent;
}
}
.header.is-active {
background: $white;
}
.header-nav {
padding: 20px 5.5%;
position: relative;
justify-content: space-between;
margin: auto;
}
.header-logo {
position: relative;
a img {
height: 46px;
}
}
.header-menu {
align-items: center;
}
.header-menu-li {
a {
position: relative;
margin: 0 0.625rem;
font-weight: 500;
font-size: $font-sm;
color: $white;
transition: color 300ms ease-in-out;
&::before {
content: "";
display: block;
position: absolute;
height: 5px;
background: $white;
left: 0;
right: 0;
bottom: -33px;
opacity: 0;
transition: opacity 300ms ease-in-out;
}
&:hover {
opacity: 0.95;
&::before {
opacity: 1;
}
}
}
}
.header-btn {
height: 46px;
font-size: $font-sm;
font-weight: 500;
button {
background: transparent;
border: 1px solid $white;
transition: 200ms ease-in-out;
&:hover {
background: $white;
color: $darkBlue;
}
}
}
Try this code:
window.addEventListener("scroll", function(){
var header = document.querySelector(".header");
header.classList.toggle("is-active", window.scrollY > 0);
})
Try to use more semantic HTML, this will help you in building the site.
<div class="menu">
<ul>
<li>Home</li>
</ul>
</div>
<section id="home">
<div class="wrapper">
</div>
</section>
You can also add some initial CSS settings to help you build your site, like :root, * {}, etc...
If you still don't know what a :root is, look it up, this will make it a lot easier.
I left some example code.
/* GENERAL */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
list-style: none;
text-decoration: none;
}
:root {
font-size: 62.5%; /* 1rem = 10px */
}
html {
scroll-behavior: smooth;
}
html,
body {
width: 100%;
height: 100%;
}
body {
font-family: " ";
font-size: 1.6rem;
text-align: center;
overflow: overlay;
background-color: var(--name-var);
}
.wrapper {
width: min(50rem, 100%);
margin-inline: auto;
padding-inline: 2.4rem;
}
window.addEventListener('scroll', onScroll)
onScroll()
function onScroll() {
showNavOnScroll ();
showBackToTopButtonOnScroll();
activateMenuAtCurrentSection(home);
}
function activateMenuAtCurrentSection(section) {
const targetLine = scrollY + innerHeight / 2
// check if the section has passed the line
// what data will I need?
const sectionTop = section.offsetTop
const sectionHeight = section.offsetHeight
const sectionTopReachOrPassedTargetLine = targetLine >= sectionTop
// check if the base is below the target line
// what data will I need?
const sectionEndAst = sectionTop + sectionHeight
const sectionEndPassedTargetLine = sectionEndAst <= targetLine
console.log('Did the session bottom go over the line?', sectionEndPassedTargetLine)
// section boundaries
const sectionBoundaries =
sectionTopReachOrPassedTargetLine && !sectionEndPassedTargetLine
const sectionId = section.getAttribute('id')
const menuElement = document.querySelector(`.menu a[href*=${sectionId}]`)
menuElement.classList.remove('active')
if (sectionBoundaries) {
menuElement.classList.add('active')
}
}
// This is so that when you scroll down your navigation bar goes out
function showNavOnScroll() {
if (scrollY > 0) {
navigation.classList.add('scroll');
} else {
navigation.classList.remove('scroll');
}
}
// Will make them appear smooth
ScrollReveal({
origin:'top',
distance: '30px',
duration: 700,
}).reveal(`
#home`);

Accordion Javascript plus sign doesn't work

I am doing an accordion in JavaScipt, in my HTML I have two separate divs for questions title and separate div for "+" sign. My goal is when I click on div with class accordionTitle I want "+" sign change to "-" and at the same time show the answer. I am pretty new to development, what the best approach here? Can you help me?
HTML:
<section class="qnaSection">
<h1 class="qnaTitle">FAQ</h1>
<div id="qnaContainer" class="qnaContainer"></div>
</section>
CSS:
.qnaSection {
background-color: rgb(35, 57, 94);
color: var(--brown);
}
.qnaTitle {
text-align: center;
margin: 10px 0;
font-size: 28px;
}
.qnaContainer {
width: 90%;
max-width: 1000px;
margin: 20px auto;
}
.accordionItem {
background-color: rgb(222, 222, 222);
color: black;
margin: 10px 0;
border-radius: 10px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.25);
}
.accordionItemTitle {
padding: 1rem;
min-height: 3.5rem;
width: 100%;
line-height: 1.25rem;
font-weight: bold;
display: flex;
justify-content: space-between;
position: relative;
cursor: pointer;
box-sizing: border-box;
}
.accordionItemPush {
/* content: '\002B'; */
font-size: 2rem;
/* position: absolute;
right: 1rem; */
transition: transform 0.2s ease-in-out;
min-width: 70px;
display: flex;
justify-content: space-around;
align-items: center;
}
.accordionItemPush.active::after {
/* content: '\2212'; */
transform: rotate(45deg);
}
.accordionContent {
max-height: 0;
overflow: hidden;
}
.accordionParagraph {
padding: 1rem;
line-height: 1.5rem;
border-top: 1px solid;
border-image: linear-gradient(to right, transparent, rgb(101, 101, 136), transparent) 1 ;
transition: max-heigth 0.2s ease-out;
}
.accordionItem.active + .accordionContent {
max-height: auto;
display: block;
}
JS:
const questions = [] // array with questions
const qnaContainer = document.getElementById("qnaContainer")
const generateTitleHtml = title => {
return `
<h3>${title}</h3>
`
}
const generateQuestionHtml = item => {
return `
<div class="accordionItem">
<div class="accordionItemTitle">
<p>${item.question}</p>
<div class="accordionItemPush">
+
</div>
</div>
<div class="accordionContent">
<p class="accordionParagraph">${item.answer}</p>
</div>
</div>
`
}
const listQ = questions => {
qnaContainer.insertAdjacentHTML('beforeend', generateTitleHtml(questions.title))
questions.items.forEach( question => {
qnaContainer.insertAdjacentHTML('beforeend', generateQuestionHtml(question))
})
}
listQ(questions.regularQuestions)
listQ(questions.membershipBilling)
const accordionItemHeaders = document.querySelectorAll(".accordionItemTitle");
const toggleBar = () => {
accordionItemHeaders.forEach(item => {
item.addEventListener("click", () => {
checkCurrentlyActiveItem(item)
toggleActive(item)
})
});
}
const checkCurrentlyActiveItem = (accordionItemHeader) => {
const currentlyActiveItem = document.querySelector(".accordionItemTitle.active");
if(currentlyActiveItem && currentlyActiveItem!==accordionItemHeader) {
currentlyActiveItem.classList.toggle("active");
currentlyActiveItem.nextElementSibling.style.maxHeight = 0;
}
}
const toggleActive = (accordionItemHeader, itemBtn) => {
accordionItemHeader.classList.toggle("active");
const accordionItemBody = accordionItemHeader.nextElementSibling;
if(accordionItemHeader.classList.contains("active")) {
accordionItemBody.style.maxHeight = accordionItemBody.scrollHeight + "px";
}
else {
accordionItemBody.style.maxHeight = 0;
}
}
toggleBar()
What you could do is: instead of putting the HTML code in the javascript, create elements with document.createElement("elementName") and then set its class name or id. Then you can set an onclick-event on the element and change its innerText or innerHTML to a "-". I think this link can be useful. If you have any questions, ask them :)

CSS Issue with a button placement in a container

I am currently making a practise eCommerce website and I have encountered a problem with my styling, I can't figure out how to attach the button to the very bottom of the product container so that other items don't overstretch.
I would ideally like the containers to stretch and balance out the content to the length of the longest item.
Here is a picture of the issue
CSS:
.card-container {
max-width: 80%;
width: 80%;
margin: auto;
display: flex;
justify-content: center;
flex-wrap: wrap;
height: 500px;
}
.product-name {
font-size: 130%;
font-weight: bold;
color: black;
}
.image-container {
width: 80%;
height: 250px;
}
.description {
padding: 0 20px;
}
.link {
text-decoration: none;
}
.item-info {
line-height: 200%;
}
.card {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
max-width: 300px;
margin: 2em auto;
text-align: center;
font-family: arial;
width: 100%;
min-width: 300px;
}
.card img {
max-width: 250px;
max-height: 200px;
}
.price {
font-size: 22px;
}
.card button {
border: none;
outline: 0;
padding: 12px;
color: white;
background-color: #000;
text-align: center;
cursor: pointer;
width: 100%;
font-size: 18px;
}
.card button:hover {
opacity: 0.7;
}
JSX:
import './Home.css'
import { Link } from 'react-router-dom';
import { useState, useEffect } from 'react';
import fetchItems from '../../utils/fetchItems';
export const Home = () => {
const [ products, setProducts ]= useState([]);
useEffect(() => {
(async () => {
const response = await fetchItems('/products');
setProducts(response);
})()
}, []);
return(
<div>
<h1>Welcome to Sahara</h1>
<div className='card-container'>
{products.map(product => {
return(
<div className="card">
<img src={product.image} alt='product' />
<div className='item-info'>
<Link to={`/products/${product.id}`} className='link'><h4 className='product-name'>{product.name}</h4></Link>
<p className='seller'>{product.seller}</p>
<p className='price'>${product.price}</p>
<Link to='/cart'><button>Add to Cart</button></Link>
</div>
</div>
)
})}
</div>
</div>
)
}
Any help would be greatly appreciated.
Thank you.
An easy way to do this would be to have the the .card position: relative; and the .card button be set to position: absolute; Something similar to the below:
.card {
position: relative;
}
.card button {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
}
You could use position: absolute; to align the button to the bottom. Add these values to the stylesheet:
.card {
position: relative;
}
.card button {
position: absolute;
bottom: 0;
right: 0;
}
See this question for more information.
.card {
position: relative;
}
.card button {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
}

How do you start new row on left with dynamic content in Reactjs?

I am working on a personal portfolio page using ReactJS. I am currently trying to create a project page that has rows of flipping cards, however, whenever the cards reach their max-width and move to the next line the card always starts in the center of the page when it needs to start on left. I have tried creating a row wrapper to float: left which breaks the application and a multitude of other ways but I would like to see the best way to create dynamic rows and columns from a mapped array and start new rows from the left. added an image to highlight the issue
Projects.js
import React, { Fragment } from 'react';
import Card from '../common/Card/Card';
import { PROJECTS } from "../../shared/constants/projects";
import "./projects.scss";
const Projects = () => {
return (
<Fragment>
<div className="grid">
{
PROJECTS.map((project, i) => {
return (
<Card animatedCard={true} project={project} key={i} />
)
})
}
</div>
</Fragment>
)
}
export default Projects;
projects.scss
.grid {
width: 60%;
text-align: center;
margin: 0 20% 0 20%;
}
Card.js
import React, { Fragment } from 'react';
import CardFront from './CardFront';
import "./card.scss";
import CardBack from './CardBack';
const Card = props => {
return (
<Fragment>
{props.animatedCard ?
<div className="card-container">
<div className="card-body">
<CardBack project={props.project}/>
<CardFront project={props.project} />
</div>
</div> : null
}
</Fragment>
)
}
card.scss
.button-wrapper {
height: 200px;
bottom: 0;
}
.card-container {
display: inline-block;
position: relative;
z-index: 1;
margin: 50px 25px 0px 25px;
width: 300px;
height: 400px;
perspective: 1000px;
}
.card-body {
width: 100%;
height: 100%;
background-color: white;
transform-style: preserve-3d;
transition: all 0.5s linear;
}
.card-container:hover .card-body {
transform: rotateY(180deg);
}
.card-container:hover > .card-body > .side-front {
opacity: 0;
visibility: hidden;
transition: opacity 1s ease-in, visibility 0.75s linear;
}
.card-side {
position: absolute;
top: 0;
overflow: hidden;
width: 100%;
height: 100%;
color: #8e8d8a;
backface-visibility: hidden;
font-family: sans-serif;
text-align: center;
box-shadow: 0 10px 35px rgba(50, 50, 93, 0.1), 0 2px 15px rgba(0, 0, 0, 0.07);
}
.card-header-img {
max-width: 100%;
max-height: 123px;
margin: 0;
padding: 0;
}
.card-technologies {
list-style: none;
width: 70%;
}
.card-technologies-item {
border-bottom: 1px solid #eee;
margin-bottom: 15px;
padding: 0.75rem;
}
.description {
margin: 30px 20px 20px 20px;
font-size: 18px;
font-weight: 400;
height: 240px;
}
.side-front [class^="col-xs"]:first-of-type {
padding-left: 0;
}
.side-back {
z-index: 2;
text-align: center;
transform: rotateY(180deg);
}
This is happening since you are using text-align: center; in your .grid css.
If you remove it all the cards in the row should start at the left end of the row:
.grid {
width: 60%;
margin: 0 20% 0 20%;
}
Another alternative would be making your .grid a flexbox:
.grid {
width: 60%;
margin: 0 20% 0 20%;
display: flex;
flex-wrap: nowrap;
}

Toggle between 2 divs - visibility and css animation

In my site, I have two divs within a container. One div has text in English and the other has text in mandarin I have a button on the side that I want the user to toggle and control the visibility of each div/language they are comfortable with. I'm using JS to add/remove class visibility (opacity and display). By default, I have the English one in view. My sketch works halfway, when a user clicks the button, the English div fades but the mandarin one doesn't appear. Code below-
HTML -
<div class="textSection">
<div class="eng about" id="eng">
<p>SHEK LEUNG
</p>
</div>
<div class="mandarin about" id="man">
<p>
「為Samson畢業後在倫敦創立的品牌
</p>
</div>
</div>
<button class="langChange">⥃</button>
css -
.textSection {
width: 50vw;
height: 80vh;
position: relative;
top: 10vh;
left: 30vw;
display: flex;
justify-content: center;
align-items: center;
}
.about {
position: absolute;
width: 100%;
height: 100%;
opacity: 1;
box-sizing: border-box;
transition: all 1s;
}
.eng {
border-radius: 10px;
background: url("72ppi/Asset\ 3.png");
background-size: 100% 100%;
font-family: Helvetica, sans-serif;
font-weight: bold;
font-size: 1.2rem;
line-height: 1.7;
text-align: justify;
text-transform: uppercase;
color: white;
padding: 3rem;
opacity: 1;
display: block;
}
.mandarin {
font-family: Hiragino Sans GB;
font-size: 1.3rem;
line-height: 2;
text-align: justify;
text-transform: uppercase;
font-weight: bold;
color: black;
padding: 3rem;
opacity: 1;
border-radius: 10px;
border: solid 2px black;
opacity: 0;
display: none;
}
.hidden {
display: none;
}
.visuallyhidden {
opacity: 0;
}
.seen {
display: block;
}
.visual {
opacity: 1;
}
.langChange {
position: absolute;
border: none;
padding: 1rem 2rem;
border-radius: 10px;
margin: 0;
text-decoration: none;
font-size: 2rem;
left: 20vw;
cursor: pointer;
background-color: transparent;
color: black;
}
JS -
let engBox = document.getElementById('eng'),
manBox = document.getElementById('man')
langbtn = document.querySelector('.langChange');
langbtn.addEventListener('click', function () {
console.log(engBox.classList);
if (engBox.classList.contains('hidden')) {
engBox.classList.remove('hidden');
setTimeout(function () {
engBox.classList.remove('visuallyhidden');
}, 20);
} else {
engBox.classList.add('visuallyhidden');
engBox.addEventListener('transitionend', function (e) {
engBox.classList.add('hidden');
}, {
capture: false,
once: true,
passive: false
});
}
}, false);
langbtn.addEventListener('click', function () {
console.log(manBox.classList);
if (manBox.classList.contains('seen')) {
manBox.classList.remove('seen');
setTimeout(function () {
manBox.classList.remove('visual');
}, 20);
} else {
manBox.classList.add('seen');
manBox.addEventListener('transitionend', function (e) {
manBox.classList.add('seen');
}, {
capture: false,
once: true,
passive: false
});
}
}, false);
Start simple and build up. Here is a minimal working visibility toggle. Position changes, layout, and most timing can be added to the CSS piece by piece until you have what you want.
const engBox = document.getElementById('eng');
const manBox = document.getElementById('man');
const langbtn = document.querySelector('.langChange');
langbtn.addEventListener('click', function () {
engBox.classList.toggle('transparent');
manBox.classList.toggle('transparent');
});
.about {
overflow: hidden;
transition: all 2s;
}
.transparent {
opacity: 0;
}
<div class="textSection">
<div class="eng about" id="eng">
<p>SHEK LEUNG
</p>
</div>
<div class="mandarin about transparent" id="man">
<p>
「為Samson畢業後在倫敦創立的品牌
</p>
</div>
</div>
<button class="langChange">⥃</button>
</div>

Categories