I have below HTML
<div class="parent">
<span class="child">1</span>
<span class="child">2</span>
<span class="child">3</span>
<span class="child">4</span>
<span class="child">5</span>
</div>
and below CSS
.parent .child {
border : 1px solid black;
display :inline-block;
width:40px;
height:25px;
text-align:center;
cursor:pointer;
}
.mark {
background-color: green;
}
and simple click event for to see selected element a below.
$(".parent > .child").click(function(e){
if(e.shiftKey) {
$(".parent .child").each(function(){
$(this).addClass("mark");
});
}
else {
$(this).addClass("mark");
}
});
Edited : When I click one element and another element with shiftKey , between these two elements should be add class mark. But my code iterate all elements as $(".parent > .child").... I would like to avoid it (I mean if 2 elements between them , I would like to iterate 4 times (inclusive start and element) with my iteration).
My question is can I iterate between two selected elements (inclusive) instead of iterating from thier parent element (in my case I don't want to iterate from parent) ? I know the start and end elements. If so, why I need to iterate all elements and check their status as I want ? JSFiddle link.
For clear question ...
I have 10 HTML element ,assume 3 is start and 6 is end.I would like to iterate as
(for var i=3 ; i <=6 ; i++) {...}
instead of iterating all elements and check their status as
(for var i=1 ; i <=10 ; i++) {
// checking is it between start and end elements
}
Demo
Try this demo. Not sure if it accomplishes what you need. Comment if changes needed.
$(".parent .child").click(function () {
if($(".parent .child.mark:first").length == 1 && !$(this).hasClass('mark')){
firstIndex = $(".parent .child.mark:first").index();
thisIndex = $(this).index();
start = Math.min(thisIndex, firstIndex);
end = Math.max(firstIndex, thisIndex) + 1;
$('.parent .child').slice(start, end).each(function(){
$(this).addClass('mark');
})
} else {
$(this).addClass('mark');
}
});
Not clear from your question but are you saying something like this
Jquery for next element is :
$(".parent .child").click(function(){
$(this).addClass("mark");
$(this).next().addClass('mark');
if($(this).is(':last-child')){
$('.parent .child:first-child').addClass('mark');
}
});
If element is last child then have added class mark to first ,this one was my assumption so far.
Why don't you use this:
$( ".mark" ).nextUntil( ".mark" ).css( "color", "red" );
$( ".mark" )--> this is your first click item
.nextUntil( ".mark" )--> this one is your second click
Hope it will works
Related
I have a JS for loop that iterates over all elements with a specific class, and then removes the class. However, whilst the loop works for the first element found, it then stops. I cannot see any errors, I've tried it inside a try/catch, and can't see anything else that might be causing the problem. Does anyone have any suggestions? Thanks :)
let visibleTags = document.getElementsByClassName('show');
console.log(visibleTags.length) // length is 2
for (let index = 0; index < visibleTags.length; index++) {
console.log(index); // 0
visibleTags[index].classList.remove('show'); // removes 'show' from element 0
}
// element 1 still has the 'show' class and was not touched by the loop... ?
visibleTags is a "live" DOM query - the elements within it will change as the DOM changes.
Therefore, when you remove the show class from an element, it simultaneously disappears from visibleTags, since your query was for elements with the show class. Thus, as soon as you remove the class, visibleTags.length drops to 1, and your loop will exit because the loop counter is already at 1.
There's a number of ways to work with this:
One solution to this is to run the loop backwards, so that it starts at visibleTags.length and counts back to zero. This way, you can remove the elements and the length will drop, but you'll then move onto the previous one and the loop carries on.
Another option is to run the loop as a while loop and just keep removing the first item: ie:
while (visibleTags.length) {
visibleTags[0].classList.remove('show');
}
This would be my preferred solution.
Finally, you may opt to create a non-live array of the elements that you can loop through. You probably don't need to do this, but it may be a useful option if you need to loop through the same list of elements again later on (eg maybe to restore the show class).
You shouldn't use indexes, visibleTag is a live collection and you're modifying part of the selection criteria (the show class) so the collection itself will change. Since you want to remove show from everything that has the show class, using a while loop like this is better:
let shown = document.getElementsByClassName('show');
while(shown.length > 0) {
shown[0].classList.remove('show');
}
<div>
<div class="show">1</div>
<div class="show">2</div>
<div class="show">3</div>
<div class="show">4</div>
</div>
This is because document.getElementsByClassName() is referencing the actual array of elements matching your class.
So when iterating and changing its class, the element itself does not belongs anymore to the array, thus the index becomes index-1.
A workaround, if you haven't another path to reach the object, is to rely on another class/selector to retrieve the list of elements:
let visibleTags = document.getElementsByClassName('test');
console.log(visibleTags.length) // length is 2
for (let index = 0; index < visibleTags.length; index++) {
console.log(index); // 0
visibleTags[index].classList.remove('show'); // removes 'show' from element 0
}
.test {
width: 300px;
height: 300px;
border: 1px solid #ccc;
}
.show {
background-color: red;
}
<div>
<div class="show test">1</div>
<div class="show test">2</div>
</div>
Try to use this function:
function removeClassFromElements(className) {
document
.querySelectorAll(`.${className}`)
.forEach(el => el.classList.remove(className));
}
For your case:
removeClassFromElements('show');
You could use querySelectorAll to select all the element with class show.
The Document method querySelectorAll() returns a static (not live) NodeList representing a list of the document's elements that match the specified group of selectors. Read more about this selector here
function removeClass() {
let visibleTags = document.querySelectorAll(".show");
console.log("Number of selected Elements: ", visibleTags.length); // length is 2
for (let index = 0; index < visibleTags.length; index++) {
console.log("Index: ", index); // 0
visibleTags[index].classList.remove("show"); // removes 'show' from element 0
}
}
.show {
background-color: red;
}
<button onclick="removeClass()">Remove Class</button>
<br/>
<br/>
<div>
<div class="show test">1</div>
<div class="show test">2</div>
</div>
I am not quite sure if that's the correct way to phrase it, but here is my problem
As you can see, pretty simple code:
<div class="first"></div>
<div></div>
What I want to achieve is:
You click on the div with the first class, it will swap that class with the sibling element
You click the sibling element, and it swaps it back, so you just swap classes around 2 elements
The problem here is it works correctly only the first time, and the second time when the new element receives the class via addClass, jQuery doesn't recognize that it contains the class by the first page load? How can I resolve this?
P.S: I made a console.log(111); just to make sure, and sure enough it triggers ONLY when I click on the black div after the first swap (the one that SHOULD NOT have the first class anymore)
To achieve this behavior, you can use delegated events http://api.jquery.com/delegate/ on elements wrapper;
$(document).delegate('.first', 'click', function(e){
e.preventDefault();
console.log(123);
$(this).removeClass('first');
$(this).siblings().addClass('first');
})
A quick and simple way to do it is this:
$(document).ready(function() {
var first = $('.first');
var second = first.next();
first.click(function(e) {
e.preventDefault();
first.removeClass('first');
second.addClass('first');
});
second.click(function(e) {
e.preventDefault();
second.removeClass('first');
first.addClass('first');
});
});
div {
background-color: black;
height: 100px;
width: 100px;
margin-bottom: 10px;
}
.first {
background-color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="first"></div>
<div></div>
This way does not scale well.
Your problem was you only change when you click the $(first) which does not change when clicked it's still point to the first div.
A better way with vanilla javascript:
document.addEventListener('click', function(e) {
if (e.target.classList.contains('first')) {
e.target.classList.remove('first')
var sibling = getNextSibling(e.target) || getPreviousSibling(e.target)
if (sibling) {
sibling.classList.add('first')
}
}
})
function getNextSibling(elem) {
var sibling = elem.nextSibling
while(sibling && sibling.nodeType != 1) {
sibling = sibling.nextSibling
}
return sibling
}
function getPreviousSibling(elem) {
var sibling = elem.previousSibling
while(sibling && sibling.nodeType != 1) {
sibling = sibling.previousSibling
}
return sibling
}
All you need to do is push both items into an array, then flip between indexes on click.
var elems = [];
$(document).on("click", ".first", function(event) {
elems = elems.length == 0 ? [event.originalEvent.target, $(event.originalEvent.target).next()] : elems;
$(elems[event.originalEvent.target === elems[0] ? 1 : 0]).addClass("first");
$(elems[event.originalEvent.target === elems[0] ? 0 : 1]).removeClass("first");
});
.first {
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="first">x</div>
<div>y</div>
I have multiple elements in the dom with a class of .blockbadge if the value of any .block-badge is 0 then I want to add a class to that element in order to style it differently.
My JS adds the class to all of these elements if anyone of them equal 0. How do I make it only affect those elements which equal zero?
HTML
<span class="block-badge">1</span>
<span class="block-badge">0</span> // this element should have the class 'zero' added
<span class="block-badge">4</span>
JS
var blockBadgeVal = $('.block-badge').val();
if (blockBadgeVal < 0) {
$('.block-badge').addClass('zero');
}
The code in the OP will not work because $('.block-badge').html() will return the html of the first element with class block-badge so in this case return string 1, you should parse the returned value then compare it with the 0.
You could use filter() method instead.
Description: Reduce the set of matched elements to those that match the selector or pass the function's test.
$('.block-badge').filter(function(){
return parseInt($(this).text())==0;
}).addClass('zero');
Hope this helps.
$('.block-badge').filter(function(){
return parseInt($(this).text())==0;
}).addClass('zero');
.zero{
color: red;
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="block-badge">1</span>
<span class="block-badge">0</span>
<span class="block-badge">4</span>
Like this
$('.block-badge').each(function(){
if(parseInt($(this).html()) ===0){
$(this).addClass('zero');
}
});
You could use the jQuery :contains selector for that specific markup
$('.block-badge:contains(0)').addClass('zero');
it won't work if any other elements contains a zero, like 10, 101 etc. so if you need only 0, use a filter
FIDDLE
Try using .text(function(index, originalText) {}) where this is current element within collection , originalHtml is current textContent
$(document).ready(function() {
$(".block-badge").text(function(index, originalText) {
if (originalText <= 0) {
$(this).addClass("zero");
}
return originalText
});
});
jsfiddle https://jsfiddle.net/s2g3zpwr/3/
I need to find the first (and last) element in a div that has a CSS margin set (> 0 px). (important: this is not necessarily the first or last element!)
Basically, I need to do this so that I can remove the margin-top for the first element and the margin-bottom for the last. Now I know you'll probably say "why not use "p:last" CSS syntax?"
Because the first or last element can be something else as well, it could be a list (UL, OL), an image, a paragraph, etc.. I cannot simply do, ul:last, ol:last, p:last as that could result in multiple elements being matched (one per type).
I only want to apply this to a single element. So that's why I think jquery is the only solution. I would gladly be wrong on this though.
Since you want only the first/last child with a margin, I imagine you'll need to use jQuery/JavaScript:
var isfirst = 1, lastelm;
$("#divid").find("*").each(function() {
var cur = $(this);
if(parseInt(cur.css("margin-top")) > 0 && isfirst == 1) {
cur.css("margin-top", 0);
isfirst = 0;
}
if(parseInt(cur.css("margin-bottom")) > 0) {
lastelm = cur;
}
});
lastelm.css("margin-bottom", 0);
I would suggest the filter method in jquery(http://api.jquery.com/filter/).
Here is a sample i could think of
<html>
<body>
<p>
<p>A<p>
<p>B<p>
<p style="margin: 10px;">C<p>
<p style="margin: 10px;">C<p>
<p>D<p>
</p>
<script>
$(document).ready(function(){
$('p').filter(function(){
return this.style.margin != '';
}).last().css('color','red');
});
</script>
<body>
You might want to build upon the filter logic...
:last isn't actually a CSS selector outside of jQuery. But what you could use is :first-child as well as :last-child:
#my-element > *:first-child {
margin-top: 0;
}
#my-element > *:last-child {
margin-bottom: 0;
}
Edit: Too late. However I'd definitely use the > selector so as not to style every first child in #my-element (like the first li in a ul or a strong inside a p etc).
Im trying to figure out a simple way to enable me to select 2 DIV elements using JQuery - here is my attempt : http://jsfiddle.net/MarKP/5/
I need to limit the selections to just 2 and will use the class I add to get the selected objects.
Can anyone point me in a better direction
<div id="1">one</div>
<div id="2">two</div>
<div id="3">three</div>
<div id="4">four</div>
var selected = 0;
var prevSelect;
$('div').click(function() {
if (selected == 2) {
selected = 1;
console.log(prevSelect);
$('#' + prevSelect).removeClass('fill');
}
$(this).addClass('fill');
prevSelect = $(this).attr('id');
selected = selected +1;
});
div {
border: 1px solid blue;
height: 25px;
margin-bottom: 5px;
}
.fill {
background-color: red;
}
I updated your functionality to disallow any selection change if 2 divs are already selected unless you click a selected div to unselect it:
http://jsfiddle.net/MarKP/32/
$('div').click(function(e){
var $et = $(e.target);
if ($et.hasClass('fill')) {
$et.removeClass('fill');
} else {
if ($('.fill').length < 2) {
$et.addClass('fill');
}
}
});
Old solution: http://jsfiddle.net/MarKP/11/
What you want is something like this:
$('.divclass').click(function(){
var cnt=$('.divclass .selected').length;
if(cnt>2) return;
$(this).addClass('selected');
});
This will add the class selected to at most 2 divclass objects. To get the selected objects, you just call $('.divclass .selected').
If you always want to remove the oldest one clicked (unless it is already selected), I'd maintain my own array like this:
Example: http://jsfiddle.net/MarKP/17/
var selected = [];
$('div').click(function() {
if( $.inArray( this, selected ) > -1 ) return;
if( selected.length === 2 ) {
$(selected.shift()).removeClass('fill');
}
selected.push($(this).addClass('fill')[0]);
});
Only add a selected class if there are fewer than two divs returned from a selector for div.selected. Otherwise, remove the selected class. For instance:
$('div').click(function() {
if (!$(this).hasClass("selected") && $("div.selected").length < 2) {
$(this).addClass("selected");
} else {
$(this).removeClass("selected");
}
});