is there any better way to do this js code? - javascript

I am making these panels to be resized to fit screen height as I click 'this' element. I feel like I hard coded those javascript, and I believe there must be better way. But couldn't really sort it out.
when one panel is clicked, its size is going to get bigger, and rest of pannels gonna get smaller
I would very much appreciate any suggestion of it.
I've tried making this function reusable, but then couldn't really come up with better solution as I am a begginer.
const panels = document.querySelectorAll('.panel');
const panelsArr = Array.from(panels);
panelsArr.forEach(panel => panel.addEventListener('click',getCurrentName))
function getCurrentName(element) {
const panel1 = document.querySelector('.panel1');
const panel2 = document.querySelector('.panel2');
const panel3 = document.querySelector('.panel3');
const panel4 = document.querySelector('.panel4');
console.log(this);
if(this) {
this.classList.toggle('active');
if(this === panel1) {
panel2.classList.toggle('inactive');
panel3.classList.toggle('inactive');
panel4.classList.toggle('inactive');
} else if (this === panel2) {
panel1.classList.toggle('inactive');
panel3.classList.toggle('inactive');
panel4.classList.toggle('inactive');
} else if (this === panel3) {
panel1.classList.toggle('inactive');
panel2.classList.toggle('inactive');
panel4.classList.toggle('inactive');
} else if (this === panel4) {
panel1.classList.toggle('inactive');
panel2.classList.toggle('inactive');
panel3.classList.toggle('inactive');
}
}
}
.panel {
background-color: #002712;
box-shadow: inset 0 0 0 .5rem rgba(255,255,255,0.1);
min-height: 22.5vh;
color: #fff;
text-align: center;
font-size: 2rem;
background-size: cover;
background-position: center;
line-height: 8rem;
transition:
min-height .5s linear,
font-size .2s linear .5s,
line-height .2s linear .5s;
}
.panel1 { background-image: url("../images/steake.png"); }
.panel2 { background-image: url("../images/sundayRoast.png"); }
.panel3 { background-image: url("../images/image1(1).png"); }
.panel4 { background-image: url("../images/cannonbury.png"); }
.active {
min-height: 37vh;
line-height: 15rem;
font-size: 2.3rem;
}
.inactive {
min-height: 15vh;
font-size: 1.2rem;
}
<main>
<section class="intro">
<div class="intro-panels">
<section class="panel panel1">
<p>most original,</p>
</section>
<section class="panel panel2">
<p>best beer,</p>
</section>
<section class="panel panel3">
<p>grilled food</p>
</section>
<section class="panel panel4">
<p>Islington</p>
</section>
</div>
</section>
</main>
I expect simplified javascript code to achieve the same goal.
Here's how I've sorted it out by your answer. Thank you for your help guys.
const panels = document.querySelectorAll('.panel');
const panelsArr = Array.from(panels);
panelsArr.forEach(panel => panel.addEventListener('click', getCurrentName))
function getCurrentName(element) {
if(this) {
panelsArr.forEach(panel => panel.classList.toggle('inactive'));
this.classList.toggle('inactive');
this.classList.toggle('active');
}
}

You could mark them all as inactive and re-mark the current one as active.
Array.from(document.querySelectorAll('.panel')).forEach(element => {
element.classList.remove('active');
element.classList.add('inactive')
});
this.classList.remove('inactive');
this.classList.add('active');
You could also filter out this from the array, but it wouldn't change the outcome.

You can toggle inactive class on all class elements but undo it again on the target element:
const panels = document.querySelectorAll('.panel');
const panelsArr = Array.from(panels);
panelsArr.forEach(panel => panel.addEventListener('click',getCurrentName))
function getCurrentName(element) {
console.log(this);
if(this) {
// Toggle inactive on all elements
for (i = 0; i < panels.length; ++i) {
panels[i].classList.toggle('inactive');
}
//But undo for selected element again
this.classList.toggle('inactive');
this.classList.toggle('active');
}
}

Try something like this. Event delegation
document.addEventListener('click', (e) => {
if(e.target.matches('.sizable')) {
document.querySelectorAll('.sizable').forEach(div => {
div.classList.remove('active');
div.classList.add('inactive');
});
e.target.classList.add('active');
}
});
.sizable {
display: inline-block;
border: 1px solid black;
margin: 5px;
}
.inactive {
width: 50px;
height: 50px;
}
.active {
width: 150px;
height: 150px;
}
<div class="sizable">Stuff</div>
<div class="sizable">Stuff</div>
<div class="sizable">Stuff</div>
<div class="sizable">Stuff</div>
<div class="sizable">Stuff</div>
<div class="sizable">Stuff</div>

Related

How to add Prev and next buttons to change on outer divs

I have this sample navigation that I'm trying to create. What I want to achieve is when you clicked on prev or next class. The active class will be added to map-inr and the scale_text will also be added to the global_map_location class. I believe that only the eq() function will be used in this part.
Here's my js Code:
// Open Popup
$(".map-inr").on("click", function () {
let myIndex = $(this).closest(".global-map").index() - 1;
$('.map-inr').removeClass('active');
$(this).addClass('active');
$('.global_map_location').removeClass('scale_text');
$(this).closest(".global-map").find('.global_map_location').addClass('scale_text');
if ($(".map-item.is--show").length) {
$(".map-item").removeClass("is--show");
setTimeout(function () {
$(".map-item").eq(myIndex).addClass("is--show");
}, 600);
} else {
$(".map-item").eq(myIndex).addClass("is--show");
}
});
//Next function
$('.next').click(function(){
if ($('.is--show').next('.map-item').length) {
$('.is--show').removeClass('is--show')
.next('.map-item')
.addClass('is--show');
}
});
//Prev function
$('.prev').click(function(){
if ($('.is--show').prev('.map-item').length) {
$('.is--show').removeClass('is--show')
.prev('.map-item')
.addClass('is--show');
}
});
.global-map {
display: inline-block;
vertical-align: top;
margin-right: 20px;
margin-bottom: 20px;
}
.map-inr {
background: red;
width: 150px;
height: 150px;
cursor: pointer;
}
.map-inr.active {
background: yellow;
}
.global_map_location.scale_text {
font-weight: 600;
}
.contain {
width: 100%;
max-width: 1000px;
margin: 50px auto 0;
padding: 0;
}
.map-item {
display: inline-block;
vertical-align: top;
padding: 20px;
border-radius: 20px;
text-align: center;
}
.map-item.is--show {
background: yellow;
}
.slider-arrow-wrapper {
margin-bottom: 20px;
}
.slider-arrow-wrapper .prev,
.slider-arrow-wrapper .next {
display: inline-block;
vertical-align: top;
text-decoration: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="global-map">
<div class="map-inr active"></div>
<p class="global_map_location scale_text">map1</p>
</div>
<div class="global-map">
<div class="map-inr"></div>
<p class="global_map_location">map2</p>
</div>
<div class="global-map">
<div class="map-inr"></div>
<p class="global_map_location">map3</p>
</div>
<div class="contain">
<div class="map-item is--show">
<div class="slider-arrow-wrapper">
prev
next
</div>
1
</div>
<div class="map-item">
<div class="slider-arrow-wrapper">
prev
next
</div>
2</div>
<div class="map-item">
<div class="slider-arrow-wrapper">
prev
next
</div>
3</div>
</div>
I have tried this on next button:
//Next function
$('.next').click(function(){
let nextIndex = $(this).closest(".map-item").index() + 1;
console.log("This next is " + nextIndex);
$(".global-map").eq(nextIndex).find('.map-inr').addClass("active");
$(".global-map").eq(nextIndex).find('.global_map_location').addClass("scale_text");
if ($('.is--show').next('.map-item').length) {
$('.is--show').removeClass('is--show')
.next('.map-item')
.addClass('is--show');
}
});
But it just keeps adding classes to next div. How to remove the classes from prev sections/divs? Is there any proper way to do it?
When user click on Prev and Next anchor tag which have Yellow background then only it should work. It is not feasible solution to make all the prev and next anchor tag click work. Only yellow background prev and next click should work.
Below is the code which match up the scenario which is explained above and also it will give you the user friendly standard view:
$(".next").on('click', function(e) {
e.preventDefault();
if($(this).closest(".map-item.is--show").next().length > 0)
{
$(".contain .is--show").removeClass('is--show').next().addClass("is--show");
$(".map-inr.active").removeClass('active').parent().next().children(".map-inr").addClass("active");
}
});
$(".prev").on('click', function(e) {
e.preventDefault();
if($(this).closest(".map-item.is--show").prev().length > 0)
{
$(".contain .is--show").removeClass('is--show').prev().addClass("is--show");
$(".map-inr.active").removeClass('active').parent().prev().children(".map-inr").addClass("active");
}
Please replace it with your prev and next click jQuery.
Let me know if you have any issues or modification request.

Function to Open and Close the Overlay using single button element

I would like to open and close overlay using single button, so when the button is clicked an additional class is added, when closed the class is removed and overlay is closed.
So far I wrote the code that opens overlay and add/remove the class to the button.
Also I've created the method to close the overlay but I'm struggling to create a proper event to actually close it, so I would be happy if anyone can guide me a bit.
I think there should be an 'if' statement within the events() checking if the button have added class, if so, the overlay will be closed using this function element.classList.contains("active");
Also the button is animated, so when class is added 3 bars (hamburger icon) becomes X and this is the main reason I don't want to have separate buttons to open and close, I already achieved that but this is not what I'm looking for.
class OverlayNav {
constructor() {
this.injectHTML()
this.hamburgerIcon = document.querySelector(".menu-icon")
this.events()
}
events() {
this.hamburgerIcon.addEventListener("click", () => this.overlayOpen())
}
overlayOpen() {
document.getElementById("myNav").style.width = "100%";
this.hamburgerIcon.classList.toggle("menu-icon--close-x")
}
overlayClose() {
document.getElementById("myNav").style.width = "0%";
}
injectHTML() {
document.body.insertAdjacentHTML('beforeend', `
<div id="myNav" class="overlay">
<p>My Overlay</p>
</div>
`)
}
}
export default OverlayNav
You can make a function with a if statement handle Opening and closing the overlay
Here is your code edited
class OverlayNav {
constructor() {
this.injectHTML();
this.hamburgerIcon = document.querySelector(".menu-icon");
this.events();
}
events() {
this.hamburgerIcon.addEventListener("click", () => this.overlayHandle());
}
overlayOpen() {
document.getElementById("myNav").style.width = "100%";
this.hamburgerIcon.classList.toggle("menu-icon--close-x");
}
overlayClose() {
document.getElementById("myNav").style.width = "0%";
}
overlayHandle() {
if (element.classList.contains("active")) {
this.overlayClose();
} else {
this.overlayOpen();
}
}
injectHTML() {
document.body.insertAdjacentHTML(
"beforeend",
`
<div id="myNav" class="overlay">
<p>My Overlay</p>
</div>
`
);
}
}
export default OverlayNav;
You can add a property that keeps track of the state of the nav bar.
constructor() {
this.injectHTML()
this.hamburgerIcon = document.querySelector(".menu-icon")
this.events()
this.overlayVisible=true;
}
Then add a method that toggles the state and calls the right open/close-method:
toggleOverlay() {
if (this.overlayVisible)
this.overlayOpen();
else
this.overlayClose();
this.overlayVisible=!this.overlayVisible;
}
Finally make the events method call toggleOverlay() instead of overlayOpen().
events() {
this.hamburgerIcon.addEventListener("click", () => this.toggleOverlay())
}
Alternativly, a pure HTML + CSS solution, using only the details element and the [open] CSS attribute selector.
.overlay > p {
padding: 1rem;
margin: 0;
display: flex;
flex-direction: column;
width: 25vw
}
.overlay summary {
padding: 1rem 0.5rem;
cursor: pointer;
max-height: 90vh;
overflow: auto;
font-size: 4em;
list-style: none;
}
.overlay[open] summary {
background: black;
color: white;
padding: 0.5rem;
font-size: 1em;
}
.overlay[open] {
position: fixed;
/* top: calc(50% - 25vw); */
left: calc(50% - 15vw);
outline: 5000px #00000090 solid;
border: 5px red solid;
border-radius: 0.5rem;
font-size: 1em
}
.overlay[open] summary::after {
content: '❌';
float: right;
}
<details class="overlay">
<summary>☰</summary>
<p>
Hello world!
</p>
</details>

Sliding animation when hiding/showing a div

I have two divs, top and bottom. Both divs have dynamic height, the top div will show or hide depending on a variable.
I would like to add in a sliding animation to the top div when showing or hiding, but the bottom div should stick with the top div and slide with it too.
var hide = true;
var trigger = document.getElementById("trigger");
var topdiv = document.getElementById("topdiv");
trigger.addEventListener('click', function() {
if (hide) {
topdiv.classList.add('hide');
} else {
topdiv.classList.remove('hide');
}
hide = !hide;
});
div {
position: relative;
display: block;
width: 100%;
padding: 10px;
}
.top {
background: #999;
}
.body {
background: #555;
}
.hide {
display: none !important;
}
<div id="topdiv" class="top hide">
<p>Top</p>
</div>
<div class="body">
<p>Body</p>
<button id="trigger">
Trigger
</button>
</div>
I tried adding transform animations, but the effect is only applied to the top div while the bottom div remains unanimated.
#keyframes topDivAnimate {
from {
transform: translateY(-100%);
}
to {
transform:translateY(0%);
}
}
Help is much appreciated.
I would use CSS transition rather than animation. I've found it easiest to do by animating the lower div rather than the upper one, and changing its position so that it covers the top one (or, of course, not). See demonstration below, I've made as minimal changes as I could to the CSS and JS:
var cover = true;
var trigger = document.getElementById("trigger");
var bottomdiv = document.getElementsByClassName("body")[0];
trigger.addEventListener('click', function() {
if (cover) {
bottomdiv.classList.add('cover');
} else {
bottomdiv.classList.remove('cover');
}
cover = !cover;
});
div {
position: relative;
display: block;
width: 100%;
padding: 10px;
}
.top {
background: #999;
}
.body {
background: #555;
transform: translateY(0%);
transition: transform 0.5s;
}
.cover {
transform: translateY(-100%);
}
<div id="topdiv" class="top hide">
<p>Top</p>
</div>
<div class="body">
<p>Body</p>
<button id="trigger">
Trigger
</button>
</div>
Are you looking something like this? Then please try this:
var trigger = document.getElementById("trigger");
var topdiv = document.getElementById("topdiv");
trigger.addEventListener('click', function() {
if ($('#topdiv').css('display') == 'none') {
$(topdiv).slideDown();
} else {
$(topdiv).slideUp();
}
});
div {
position: relative;
display: block;
width: 100%;
padding: 10px;
}
.top {
display: none;
background: #999;
}
.body {
background: #555;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="topdiv" class="top hide">
<p>Top</p>
</div>
<div class="body">
<p>Body</p>
<button id="trigger">
Trigger
</button>
</div>
Try this code and see if that's the effect you wanted. It uses the Animate.css library so you'll need to link that in your <head></head>
function animateCSS(element, animationName, callback) {
const node = document.querySelector(element)
node.classList.add('animated', animationName)
function handleAnimationEnd() {
node.classList.remove('animated', animationName)
node.removeEventListener('animationend', handleAnimationEnd)
if (typeof callback === 'function') callback()
}
node.addEventListener('animationend', handleAnimationEnd)
}
var hide = false;
var trigger = document.getElementById("trigger");
var topdiv = document.getElementById("topdiv");
trigger.addEventListener('click', function() {
if (!hide) {
topdiv.classList.remove('hide');
animateCSS('.body', 'slideInDown');
animateCSS('#topdiv', 'slideInDown');
} else {
animateCSS('#topdiv', 'slideOutUp', function() {
topdiv.classList.add('hide');
})
animateCSS('.body', 'slideOutUp');
}
hide = !hide;
});
Working Codepen demo of my solution
Here's some more explanation on how to use the Animate.css library.

onclick change border colour of a html header permanently until another is clicked

Currently I have a number of clickable boxes that when I hover over them, they change colour. When I click and hold on a specific box, it changes colour.(by using :active in css.
Is there anyway I can make the colour of the border change permanently until a different box is clicked? E.G the same as the :active property except I don't have to keep the mouse held in?
My Code:
flight-viewer.html
<h3>Flights </h3>
<div>
<ul class= "grid grid-pad">
<a *ngFor="let flight of flights" class="col-1-4">
<li class ="module flight" (click)="selectFlight(flight)">
<h4>{{flight.number}}</h4>
</li>
</a>
</ul>
</div>
<div class="box" *ngIf="flightClicked">
<!--<flight-selected [flight]="selectedFlight"></flight-selected>-->
You have selected flight: {{selectedFlight.number}}<br>
From: {{selectedFlight.origin}}<br>
Leaving at: {{selectedFlight.departure || date }}<br>
Going to: {{selectedFlight.destination}}<br>
Arriving at: {{selectedFlight.arrival || date}}
</div>
flight-viewer.css:
h3 {
text-align: center;
margin-bottom: 0;
}
h4 {
position: relative;
}
ul {
width: 1600px;
overflow-x: scroll;
background: #ccc;
white-space: nowrap;
vertical-align: middle;
}
div
{
position:absolute;
top:50%;
left:50%;
transform:translate(-50%,-50%);
}
li {
display: inline-block;
/* if you need ie7 support */
*display: inline;
zoom: 1
}
.module {
padding: 20px;
text-align: center;
color: #eee;
max-height: 120px;
min-width: 120px;
background-color: #607D8B;
border-radius: 2px;
}
.module:hover {
background-color: #EEE;
cursor: pointer;
color: #607d8b;
}
.module:active {
border: 5px solid #73AD21;
}
.box {
text-align: center;
margin-bottom: 0;
margin: auto;
width: 600px;
position:absolute;
top: 180px;
right: 0;
height: 100px;
border: 5px solid #73AD21;
text-align: center;
display: inline-block;
}
flight-viewer-component.ts:
import { Component } from '#angular/core';
import { FlightService } from './flight.service';
import { Flight } from './flight.model'
#Component({
selector: 'flight-viewer',
templateUrl: 'app/flight-viewer.html',
styleUrls: ['app/flight-viewer.css']
})
export class FlightViewerComponent {
name = 'FlightViewerComponent';
errorMessage = "";
stateValid = true;
flights: Flight[];
selectedFlight: Flight;
flightToUpdate: Flight;
flightClicked = false;
constructor(private service: FlightService) {
this.selectedFlight = null;
this.flightToUpdate = null;
this.fetchFlights();
}
flightSelected(selected: Flight) {
console.log("Setting selected flight to: ", selected.number);
this.selectedFlight = selected;
}
flightUpdating(selected: Flight) {
console.log("Setting updateable flight to: ", selected.number);
this.flightToUpdate = selected;
}
updateFlight(flight: Flight) {
let errorMsg = `Could not update flight ${flight.number}`;
this.service
.updateFlight(flight)
.subscribe(() => this.fetchFlights(),
() => this.raiseError(errorMsg));
}
selectFlight(selected: Flight) {
console.log("Just click on this flight ", selected.number, " for display");
this.flightClicked = true;
this.selectedFlight = selected;
}
private fetchFlights() {
this.selectedFlight = null;
this.flightToUpdate = null;
this.service
.fetchFlights()
.subscribe(flights => this.flights = flights,
() => this.raiseError("No flights found!"));
}
private raiseError(text: string): void {
this.stateValid = false;
this.errorMessage = text;
}
}
Thanks!
I'm quite sure that this has already been answered.
Make your DIVs focusable, by adding tabIndex:
<div tabindex="1">
Section 1
</div>
<div tabindex="2">
Section 2
</div>
<div tabindex="3">
Section 3
</div>
Then you can simple use :focus pseudo-class
div:focus {
background-color:red;
}
Demo: http://jsfiddle.net/mwbbcyja/
You can use the [ngClass] directive provided by angular to solve your problem.
<a *ngFor="let flight of flights" class="col-1-4">
<li [ngClass]="{'permanent-border': flight.id === selectedFlight?.id}" class ="module flight" (click)="selectFlight(flight)">
<h4>{{flight.number}}</h4>
</li>
</a>
This will add the css class permantent-border to the <li> element, if the id of the flight matches the id with the selectedFlight (Assuming you have an id proberty specified, or just use another proberty which is unique for the flight)

Img resize on scroll

How can i resize a logo( es width: 100px ) in a header on mouse scrolling?
$('.logo').scroll(function() {
$(this).width(100);
$(this).off(); //removes the handler so that it only resizes once...
})
.header {
background-color: black;
}
.logo {
height:100px;
width: 100%;
background-image: url("http://unika.myarmah.it/skin/frontend/sns_simo/default/images/logo.png");
background-repeat: no-repeat;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="header">
<div class="logo"></div>
</div>
Just use javascript:
Why? - Because its just as short as using jQuery.
Update #1 -
after seeing the comments to the previous answer from the author, I have adjusted my example to include animation and reset when at the top of the page. Again - just use javascript, and for better performance benefits use CSS classes so that all paints are done in one cycle.
Update #1 jsfiddle - https://jsfiddle.net/113dn29z/16/
var logo = document.querySelector('.logo');
var handleResize = function(e) {
if (document.body.scrollTop === 0) {
logo.classList.remove("resize");
} else {
logo.classList.add("resize");
}
};
document.addEventListener('scroll', handleResize);
<div class="header">
<div class="logo">
</div>
</div>
body {
height: 9999px;
overflow: auto;
}
.header {
background-color: black;
}
.logo {
margin-top: 200px;
height:100px;
width: 100%;
background-color: red;
background-repeat: no-repeat;
transition: width 0.2s ease;
}
.logo.resize {
width: 100px;
}
old jsFiddle example - https://jsfiddle.net/113dn29z/10/
var logoHasResized = false;
$(document).on('scroll', function (e) {
if (window.scrollY == 0) {
$('.logo').animate({'width': '100%'}, 250);
logoHasResized = false;
} else if (logoHasResized == false) {
$('.logo').animate({'width': 100}, 250);
logoHasResized = true;
}
});
edit: Since you want it to go back when you scroll to the top of the page, i've added in a check to see if the animation has happened, as you don't want it to fire constantly.

Categories

Resources