Add class to an element without an id - javascript

I have a list of items:
<div class="crew-item>
<div class="crew-grid"></div>
<div class="crew-detail></div>
</div>
<div class="crew-item>
<div class="crew-grid"></div>
<div class="crew-detail></div>
</div>
<div class="crew-item>
<div class="crew-grid"></div>
<div class="crew-detail></div>
</div>
When I click on a selected 'crew-grid' I'd like to add a class ('active') to its 'crew-item' parent, but I have no idea how to achieve that using vanilla js or jQuery.
The goal is to reveal the 'crew-detail' part, with active class added to its parent.

Like this?:
$('.crew-grid').on('click', function () {
$(this).closest('.crew-item').addClass('active');
});
Basically, starting from the clicked element, get the closest ancestor element which matches that selector. You don't need an id to target an element, just a way to identify it based on the information you have (in this case the clicked element).
If you want to de-activate other elements at the same time:
$('.crew-grid').on('click', function () {
$('.crew-item').removeClass('active');
$(this).closest('.crew-item').addClass('active');
});

Using jQuery :
$('.crew-grid').click(function() {
$(this).closest('.crew-item').addClass('active');
});

Use Document.querySelectorAll()
var crews = document.querySelectorAll('.crew-item');
if (crews) {
for (var i = 0; i < crews.length; i++) {
var grid = crews[i].querySelector('.crew-grid');
grid.addEventListener('click', toggleActive, false);
}
}
function toggleActive() {
var grids = document.querySelectorAll('.crew-item');
for (var i = 0; i < grids.length; i++) {
if (grids[i].classList.contains('active')) {
grids[i].classList.remove('active');
}
}
this.parentNode.classList.add('active');
}
.crew-item.active {
background: #DDD;
}
.crew-grid:hover {
cursor: pointer;
background: #eee;
}
<div class="crew-item active">
<div class="crew-grid">crew-grid</div>
<div class="crew-detail">crew-detail</div>
</div>
<br>
<div class="crew-item">
<div class="crew-grid">crew-grid</div>
<div class="crew-detail">crew-detail</div>
</div>
<br>
<div class="crew-item">
<div class="crew-grid">crew-grid</div>
<div class="crew-detail">crew-detail</div>
</div>

Related

Jquery append while wrapping every x element

I'm trying to create HTML like this:
<div class="container">
<div class="wrap">
<div class="el"></div>
<div class="el"></div>
<div class="el"></div>
</div>
<div class="wrap">
<div class="el"></div>
<div class="el"></div>
<div class="el"></div>
</div>
<div class="wrap">
<div class="el"></div>
<div class="el"></div>
<div class="el"></div>
</div>
</div>
The component used to add el element:
<input type="text" name="elements" />
el elements will appended to the container based on what number is added in the input. Every 3 elements should be wrapped in wrap div.
What I have so far:
$("input[name=elements]").on("keydown keyup", function() {
var amount = parseInt($(this).val());
for(i = 0; i < amount; i++) {
$(".container").append('<div class="el"></div>');
}
});
It adds the el divs but I'm not sure how to simultaneously wrap every 3 in wrap. Also is it possible to also remove el divs? If say I first type 8 in the input then I type 3, 11 divs will be added instead having just 3. In other words, the number of el divs in the HTML should alway be equal to the number in the input value. Would it make sense just to clear out the HTML first every time on input type?
You could first create an array of elements based on number of input value, append it to container and then wrap every nth element into wrap element.
const container = $('.container')
$("input").on('keyup', function() {
const val = parseInt($(this).val()) || 0;
const html = Array.from(Array(val), () => (
$("<div>", {
'class': 'el',
'text': 'element'
})
))
container.html(html)
for (let i = 0; i < val; i += 3) {
container
.find('.el')
.slice(i, i + 3)
.wrapAll("<div class='wrap'></div>");
}
})
.wrap {
border: 1px solid green;
margin: 10px 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="text">
<div class="container"></div>

Javascript on click event for multiple buttons with same class

I have a few buttons across a site I am building, certain buttons have one class while others have another. What I am trying to do is find the best way to find the clicked button without having an event listener for each individual button. I have come up with the below 2 for loops to find all the buttons with class button-1 and class button-2. Being fairly new to javascript i just don't want to get into bad habits so would appreciate any advice on the best way to achieve this.
<section>
<div class="button--1"></div>
<div class="button--1"></div>
<div class="button--2"></div>
</section>
<section>
<div class="button--2"></div>
<div class="button--1"></div>
<div class="button--2"></div>
</section>
var button1 = document.querySelectorAll('.button--1');
var button2 = document.querySelectorAll('.button--2');
for (var a = 0; a < button1.length; a++) {
button1[a].addEventListener('click',function(){
//do something
});
}
for (var b = 0; b < button2.length; b++) {
button1[b].addEventListener('click',function(){
//do something
});
}
If you plan to have multiple other classes like button--3, …4 … …15,
You must want to target all div elements which class starts (^=) with "button":
(Note that you can do it in the CSS too!)
var allButtons = document.querySelectorAll('div[class^=button]');
console.log("Found", allButtons.length, "div which class starts with “button”.");
for (var i = 0; i < allButtons.length; i++) {
allButtons[i].addEventListener('click', function() {
console.clear();
console.log("You clicked:", this.innerHTML);
});
}
/* Some styling */
section {
margin: 8px 0;
border: 1px solid gray;
}
section div {
border: 1px solid lightgray;
display: inline-block;
margin-left: 8px;
padding: 4px 8px;
width: 30px;
}
section div[class^=button] {
background: lightgray;
cursor: pointer;
}
<span>You can click on the buttons:</span>
<section>
<div class="button--1">s1-1</div>
<div class="button--2">s1-2</div>
<div class="button--3">s1-3</div>
<div class="button--4">s1-4</div>
</section>
<section>
<div class="button--1">s2-1</div>
<div class="button--2">s2-2</div>
<div class="button--3">s2-3</div>
<div class="button--4">s2-4</div>
</section>
<section>
<div class="not-a-button">not1</div>
<div class="not-a-button">not2</div>
<div class="not-a-button">not3</div>
<div class="not-a-button">not4</div>
</section>
Hope it helps.
Try using event delegation
(function() {
document.body.addEventListener("click", clickButtons);
// ^ one handler for all clicks
function clickButtons(evt) {
const from = evt.target;
console.clear();
if (!from.className || !/button--\d/i.test(from.className)) { return; }
// ^check if the element clicked is one of the elements you want to handle
// if it's not one of the 'buttons', do nothing
console.log("you clicked " + from.classList);
}
}())
.button--1:before,
.button--2:before {
content: 'BTTN['attr(class)']';
}
.button--1,
.button--2 {
border: 1px solid #999;
background: #eee;
width: 220px;
padding: 3px;
text-align: center;
}
<section>
<div class="b1 button--1 section1"></div>
<div class="b2 button--1 section1"></div>
<div class="b3 button--2 section1"></div>
</section>
<section>
<div class="b4 button--2 section2"></div>
<div class="b5 button--1 section2"></div>
<div class="b6 button--2 section2"></div>
</section>
You can use multiple selectors in the string of querySelctorAll() by separating them with a ,
var button1 = document.querySelectorAll('.button--1');
var button2 = document.querySelectorAll('.button--2');
var allButtons = document.querySelectorAll('.button--1, .button--2');
console.log(button1.length);
console.log(button2.length);
console.log(allButtons.length);
<section>
<div class="button--1"></div>
<div class="button--1"></div>
<div class="button--2"></div>
</section>
<section>
<div class="button--2"></div>
<div class="button--1"></div>
<div class="button--2"></div>
</section>
My suggestion is to use jQuery so that you can do it something like this:
$(document).on('click', '.button--1', function() {
// Do something
});
$(document).on('click', '.button--1', function() {
// Do something
})
But a clean approach for pure Javascript is to create a function that binds a callback for the event.
function bindEvent(callback, eventType, targets) {
targets.forEach(function(target) {
target.addEventListener(eventType, callback);
});
};
var button1 = document.querySelectorAll('.button--1');
var button2 = document.querySelectorAll('.button--2');
bindEvent(function() {
// do something
}, 'click', button1);
bindEvent(function() {
// do something
}, 'click', button2);
The click event is fired when a pointing device button (usually a mouse's primary button) is pressed and released on a single element.
This documentation will help you to understand how it works MDN - Click event

Javascript only addEventListener to parent style both parent and child differently

Currently have a div that controls the width of an element as well as the background color. That div has a child div which has the content which is semi-transparent. Which is why I need the first div. So the background is solid.
Now, I added an event listener to the parent which expands the width of one and decreases the width of the other 2 so they all fit. However, when I click on the parent div I would like the child of that specific div to add a class and remove a class from the other 2. Which I can't seem to figure out. Here's the code. Sorry if my formatting is poor, first time posting on stack overflow and I've googled and searched everything for an answer and can't seem to find one.
var purchaseStepCont = document.querySelectorAll(".step-container");
var purchaseStep = document.querySelectorAll(".step");
for (var i = 0; i < purchaseStepCont.length; i++) {
purchaseStepCont[i].addEventListener("click", function() {
for (var i = 0; i < purchaseStepCont.length; i++) {
purchaseStepCont[i].classList.remove("stepContActive");
purchaseStepCont[i].classList.add("stepContDeactive");
this.classList.add("stepContActive");
this.classList.remove("stepContDeactive");
}
});
}
<div class="step-container">
<div class="step">
<h1>01.</h1>
<h3>words</h3>
<p>wods</p>
</div>
</div>
<div class="step-container">
<div class="step">
<h1>01.</h1>
<h3>words</h3>
<p>wods</p>
</div>
</div>
<div class="step-container">
<div class="step">
<h1>01.</h1>
<h3>words</h3>
<p>wods</p>
</div>
</div>
You're very close. But if you want to add the class to the .step, you need this.firstElementChild.classList.add(...) rather than this.classList.add(...) (since this will be the .step-container, not the .step; but the .step is its first element child). Or for more markup flexibility, you could use this.querySelector(".step").
You can also use just a single event handler function rather than recreating it in the loop:
var purchaseStepCont = document.querySelectorAll(".step-container");
var purchaseStep = document.querySelectorAll(".step");
function clickHandler() {
var thisStep = this.firstElementChild; // Or this.querySelector(".step") would be more flexible
for (var i = 0; i < purchaseStep.length; i++) {
if (purchaseStep[i] === thisStep) {
purchaseStep[i].classList.add("stepContActive");
purchaseStep[i].classList.remove("stepContDeactive");
} else {
purchaseStep[i].classList.remove("stepContActive");
purchaseStep[i].classList.add("stepContDeactive");
}
}
}
for (var i = 0; i < purchaseStepCont.length; i++) {
purchaseStepCont[i].addEventListener("click", clickHandler);
}
.stepContActive {
color: blue;
}
.stepContDeactive {
color: #ddd;
}
<div class="step-container">
<div class="step">
<h1>01.</h1>
<h3>words</h3>
<p>wods</p>
</div>
</div>
<div class="step-container">
<div class="step">
<h1>01.</h1>
<h3>words</h3>
<p>wods</p>
</div>
</div>
<div class="step-container">
<div class="step">
<h1>01.</h1>
<h3>words</h3>
<p>wods</p>
</div>
</div>
clickHandler could be a bit shorter if you don't need to support IE11:
function clickHandler() {
var thisStep = this.firstElementChild; // Or this.querySelector(".step") would be more flexible
for (var i = 0; i < purchaseStep.length; i++) {
purchaseStep[i].classList.toggle("stepContActive", purchaseStep[i] === thisStep);
purchaseStep[i].classList.toggle("stepContDeactive", purchaseStep[i] !== thisStep);
}
}
But IE11 doesn't support the second argument to classList.toggle.

Add class if child element not present

If this works when a class is present...
$('.priority').children('.no-priority').addClass('normal');
How would I add a class if the child class .no-priority is missing?
Tried code below which does not work
if (!$('.priority').children('.no-priority')) {
$('.priority').addClass('prioritised');
}
Neither does
$('.priority').not('.no-priority').addClass('prioritised');
You can use :not(), :has()
$(".priority:not(:has(.no-priority))").addClass("normal")
.normal {
color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="priority">
<div class="does-not-have-no-priority">
does not have .no-priority
</div>
</div>
<div class="priority">
<div class="no-priority">
has .no-priority
</div>
</div>
Use filter
$('.priority').filter(function() {
return $(this).find('.no-priority').length === 0;
}).addClass('normal');
If I understand your question, you can do this in vanilla javascript:
var priority = document.querySelectorAll('.priority');
for (var i = 0; i < priority.length; i++) {
if (priority[i].querySelector('.no-priority')) {
priority[i].classList.add('normal');
} else {
priority[i].classList.add('prioritised');
}
}
.priority.normal {
color: red;
}
.priority.prioritised {
color: blue;
}
<div class="priority">
<div class="no-priority">no priority</div>
</div>
<div class="priority">
<div class="foo">priority</div>
</div>

Change child element of array item (syntax)

I have a few divs which are using the same class.
Inside the divs are three more divs with identical classes.
<div class="plane">
<div class="win1">Lorem ipsum</div>
<div class="win2">Dolor sit</div>
<div class="win3">amet.</div>
</div>
<div class="plane">
<div class="win1">Lorem ipsum</div>
<div class="win2">Dolor sit</div>
<div class="win3">amet.</div>
</div>
var allPlanes = $('.plane');
for (var i = 0; i < allPlanes.length; i++) {
var onePlane = allPlanes[i];
var baseHeight = 10;
$(onePlane + " .win1").css("height", parseInt(baseHeight*1));
$(onePlane + " .win2").css("height", parseInt(baseHeight*2));
$(onePlane + " .win3").css("height", parseInt(baseHeight*3));
}
(Don't mind about the names. It's just an example...)
Now I made an array with the outside divs and I can select the single divs inside. But I did not get the right syntax for the child divs inside.
Can anyone help?
My Fiddle: http://jsfiddle.net/SchweizerSchoggi/559xvww6/
Change you script to this:
var allPlanes = $('.plane');
var baseHeight = 10;
$(".plane > .win1").css("height", parseInt(baseHeight*1)+"px");
$(".plane > .win2").css("height", parseInt(baseHeight*2)+"px");
$(".plane > .win3").css("height", parseInt(baseHeight*3)+"px");
You don't need the for loop in such a case.
A prettier way:
var baseHeight = 10;
for (var i = 1; i <= 3; i++) {
$(".plane > .win"+i).css("height", parseInt(baseHeight*i)+"px");
}
http://jsfiddle.net/559xvww6/3/
If you don't want to use a for loop and want to dinamically configure from an array:
var baseHeight = 10;
$.map([1,2,3], function(i) {
$(".plane > .win"+i).css("height", parseInt(baseHeight*i)+"px");
});
http://jsfiddle.net/559xvww6/10/
Edit:: Just a side note: all these approachs are valid, but that doesn't mean that they are the best / most efficient ones. Feel free to use the one you like the most, understand it and try to use it or adapt it to your very personal situation. The "easiest" approach is surely the first one, but it is also the longest one.
isn't this one is better:
var base = 10;
$('.plane > div').css('height', function(){
return base*($(this).index()+1)
});
.plane {
background-color: #ccc;
border: solid 1px #cdcdcd;
margin-bottom: 15px;
}
.plane > .win1 { background-color: #ddd; }
.plane > .win2 { background-color: #eee; }
.plane > .win3 { background-color: #fff; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="plane">
<div class="win1">Lorem ipsum</div>
<div class="win2">Dolor sit</div>
<div class="win3">amet.</div>
</div>
<div class="plane">
<div class="win1">Lorem ipsum</div>
<div class="win2">Dolor sit</div>
<div class="win3">amet.</div>
</div>
You cannot use + operator between a jQuery object and a string.
The correct way to do it is this:
$(".win1", onePlane).css("height", parseInt(baseHeight*1));
$(".win2", onePlane).css("height", parseInt(baseHeight*2));
$(".win3", onePlane).css("height", parseInt(baseHeight*3));
Each of these queries translates to: select all elements with .winX that are inside the jQuery object onePlane.
I would use all the same class names inside the nest and then just do $('.plane:eq(0) .win:eq(2)').html()
alert( $('.plane:eq(0) .win:eq(2)').html() );
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="plane">
<div class="win">Lorem ipsum</div>
<div class="win">Dolor sit</div>
<div class="win">amet.</div>
</div>
<div class="plane">
<div class="win">Lorem ipsum</div>
<div class="win">Dolor sit</div>
<div class="win">amet.</div>
</div>
if your classes are fixed then you can do with this code
$(".win1", $(".plane")).css("height", parseInt(baseHeight*1));
$(" .win2", $(".plane")).css("height", parseInt(baseHeight*2));
$(" .win3", $(".plane")).css("height", parseInt(baseHeight*3));
You can do using each loop of plane class.
$('.plane').each(function(){
baseHeight = 10;
$(this).find(".win1").css("height", parseInt(baseHeight*1));
$(this).find(".win2").css("height", parseInt(baseHeight*2));
$(this).find(".win3").css("height", parseInt(baseHeight*3));
});
Demo

Categories

Resources