How to iterate through multiple divs of (almost) same class - javascript

Let's say I got 12 divs of class subClass, each ending with individual number: subClass-1 , subClass-2 and so on. Also, for each of those classes I got same function, for example:
function toggleAreas1() {
var $hide = $('.toggle-areas-1 > .row:visible');
$hide.fadeOut(function() {
if ($hide.next().length) {
$hide.next().fadeIn();
} else {
$hide.prevAll().last().fadeIn();
}
});
}
But for obvious reasons, I don't want to use 12 different, yet almost identical functions, when I easily could use one, more universal.
I was thinking about something along these lines:
function toggleAreas1(index) {
for (i = 0; i < 12; i++) {
index = i++
var $hide = $('.toggle-areas-index > .row:visible');
}
$hide.fadeOut(function() {
if ($hide.next().length) {
$hide.next().fadeIn();
} else {
$hide.prevAll().last().fadeIn();
}
});
}
But of course it doesn't work, for my js skills are non-existent. What should I do to fix my problem?

function toggleAreas1() {
var $hide = $("[class^=toggle-areas] > .row:visible");
$hide.fadeOut(function() {
if ($hide.next().length) {
$hide.next().fadeIn();
} else {
$hide.prevAll().last().fadeIn();
}
});
}

try changing
var $hide = $('.toggle-areas-index > .row:visible');
to
var $hide = $('.toggle-areas-'+index+' > .row:visible');

You would need the + operator for gluing an integer variable to strings:
i.e.
var $hide = $('.toggle-areas-' + index + ' > .row:visible');

try this:
function toggleAreas1() {
var $hide = $('[class^=toggle-areas] > .row:visible').each(function(){
$(this).fadeOut(function() {
// do what you need
});
});

You can use string concatenation for building jQuery selectors:
for (i = 1; i <= 12; i++)
{
var $hide = $('.toggle-areas-' + i + ' > .row:visible'); // .toggle-areas-1, .toggle-areas-2 etc.
$hide.fadeOut(function() {
if ($hide.next().length) {
$hide.next().fadeIn();
} else {
$hide.prevAll().last().fadeIn();
}
});
}
However, why don't you just give them the one common class like toggle-areas? You can add this class in addition to your existent:
<div class="toggle-areas toggle-areas-1"></div>
<div class="toggle-areas toggle-areas-2"></div>
<div class="toggle-areas toggle-areas-3"></div>
Then, you will be able to do both:
// Affects all toogle-areas
$(".toogle-areas").fadeOut(function() {
if ($hide.next().length) {
$hide.next().fadeIn();
} else {
$hide.prevAll().last().fadeIn();
}
});
and
// Affects only toogle-areas-2
$('.toggle-areas-2 > .row:first').show().nextAll().hide();

What you are looking for are attribute substring selectors.
So if your class begins with subClass use: [class^=subClass]
$('[class^=subClass]');
Note though that this tests against the full attribute string so if there is something else in front it will not match, ie class="class1 subClass-1" in which case you can use the other two $=, *= selectors. The first one doing an ends with match, the second doing a contains match.
jQuery('[class^=subClass]').css({
background:'#ffff00',
});
[class^=subClass] {
width:48px;
height:48px;
margin-bottom:3px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="subClass-1"></div>
<div class="subClass-2"></div>
<div class="subClass-3"></div>

Related

Angular 2 add class conditionally

My target is to set or remove class depending on component boolean with Angular 2. For example: isRed = true > add class "red", if isRed = false > remove class "red". How is that possible? Code tried:
isRed: boolean;
constructor() {
$(document).scroll(function(){
var scrollTop = $(this).scrollTop();
if(window.location.hash) {
} else{
this.isRed = true;
}
if(scrollTop > 50) {
this.isRed = true;
}
else {
this.isRed = false;
}
});
}
and html:
[ngClass]="{red: isRed}"
The most concise way is IMHO
[class.red]="isRed"
update
The cause of your problem is function in
$(document).scroll(function(){
it should use arrow function
$(document).scroll(() => {
otherwise this within the callback won't point to the current class, but instead to the caller.
I'd suggest you try to avoid jQuery with Angular2. Use instead
class MyComponent {
constructor(private elRef:ElementRef) {}
isRed:boolean;
#HostListener('document:scroll', ['$event'])
onScroll(event:any) {
var scrollTop = this.elRef.nativeElement.scrollTop;
// or
$(this.elRef.nativeElement).scrollTop();
if(window.location.hash) {
} else{
this.isRed = true;
}
if(scrollTop > 50) {
this.isRed = true;
}
else {
this.isRed = false;
}
}
}
This is javascript so I would try something like:
isRed; // there's no need to initialize this variable
since the constructor has its own scope but hey,
do it if you wish so
Also it doesn't seem that you are working inside an object since you are using ; instead of , which means that you shouldnt use ":" but rather "="

How to give classes to the corresonding element of the counter?

if I want to add a class to the corresponding element of a counter, how would I do that?
I do have something like
var li_counter = 0;
$("#trigger_heritage").click(function () {
if(heritage_position>=heritage_versatz*13){
li_counter++;
$(".heritage_nav" li[li_counter]).addClass("activeheritage");
}
else{ // Something else
}
});
And I want to apply a class to that li-element of the heritage_nav-block
that has the corresponding counterposition. If the user has clicked 5 times, the 5th child-element of heritage_nav shall get the class....
Thanks!
var li_counter = 0;
$("#trigger_heritage").click(function () {
if(heritage_position>=heritage_versatz*13){
li_counter++;
$(".heritage_nav li:eq("+li_counter+")").addClass("activeheritage");
}
else{ // Something else
}
});
Maybe this will help you,
var li_counter = 0;
$("#trigger_heritage").click(function () {
if(heritage_position>=heritage_versatz*13){
li_counter++;
$(".heritage_nav").find("li:nth-child(" + li_counter + ")").addClass("activeheritage");
//if you want to add class to this div
$(".heritage_nav").addClass("your-class");
}
else{ // Something else
}
});

Simplify / elegantize this code?

Here's a bit of code which works fine but I think could be simplified / shortened. It's basically clicking on a list item, getting it's id and then showing / hiding / removing elements based on the id.
Suggestions on how to simplify this with a function or loop?
$("#btn_remove_event_type").click(function() {
var selectedId = $(".selected-type").attr("id");
if (selectedId == "temperature_event") {
$("#poplist_temp").show();
$(".temperature-params").hide();
$("#temperature_event").remove();
} else if (selectedId == "load_event") {
$("#poplist_load").show();
$(".load-params").hide();
$("#load_event").remove();
} else if (selectedId == "price_event") {
$("#poplist_price").show();
$(".price-params").hide();
$("#price_event").remove();
} else if (selectedId == "duty_event") {
$("#poplist_duty").show();
$(".duty-params").hide();
$("#duty_event").remove();
} else {
$("#poplist_program").show();
$(".program-params").hide();
$("#program_event").remove();
}
});
Something like this:
$("#btn_remove_event_type").click(function() {
var selectedId = $(".selected-type").attr("id");
switch(selectedId){
case "temperature_event":
$("#poplist_temp").show();
$(".temperature-params").hide();
$("#temperature_event").remove();
break;
case "load_event":
$("#poplist_load").show();
$(".load-params").hide();
$("#load_event").remove();
break;
case "price_event":
$("#poplist_price").show();
$(".price-params").hide();
$("#price_event").remove();
break;
case "duty_event"):
$("#poplist_duty").show();
$(".duty-params").hide();
$("#duty_event").remove();
break;
default:
$("#poplist_program").show();
$(".program-params").hide();
$("#program_event").remove();
}
});
Or even better:
$("#btn_remove_event_type").click(function() {
// Get id and split on "_"
var selectedId = $(".selected-type").attr("id").split("_");
// Set array of accepted input
var options = ["temperature","load","price","duty"];
// Handle default
if(options.indexOf(selectedId[0]) == -1){
selectedId = ["program"];
}
$("#poplist_" + selectedId[0]).show();
$("."+selectedId[0] + "-params").hide();
$("#"+selectedId[0] + "_event").remove();
});
For simplicity sake I would change the id of the buttons clicked to one word(tempeature,load,price,duty). You'll have to do some renaming of your reactionary ids/classes, but just looks cleaner to me. I also flipped some of your naming around to stay consistent.
You can switch it around to match your style, it just bothered me having selectors like $("#"+id+"-params").hide();
$("#btn_remove_event_type").click(function() {
var selectedId = $(".selected-type").attr("id");
var nonDefault = ["temperature","load","price","duty"];
if(nonDefault.indexOf(selectedId) > -1){
$("#poplist_"+selectedId).show();
$(".params-"+selectedId).hide();
$("#event_"+selectedId).remove();
} else {
$("#poplist_program").show();
$(".params-program").hide();
$("#event_program").remove();
}
});
I ended up changing my classes to ids and reordering them, so I ended up with this solution which doesn't require any if statements.
$("#btn_remove_event_type").click(function() {
var selectedId = $(".selected-type").attr("id").split("_");
$("#" + selectedId[0] + "_pop").show();
$("#" + selectedId[0] + "_params").hide();
$("#" + selectedId[0] + "_event").remove();
});
If you don't want to rely on the ID names matching other element names, you can use an object as a map to specify actions.
//using a map lets you easily add or remove cases without duplicating code, and doesn't require you to use standardized names for your elements and events.
var action_map = {temperature_event:{show:"#poplist_temp",
hide:".temperature-params",
remove:"#temperature_event"
},
load_event:{show:"#poplist_load",
hide:".load-params",
remove:"#load_event",
},
price_event:{show:"#poplist_price",
hide:".price-params",
remove:"#price_event",
},
duty_event:{show:"#poplist_duty",
hide:".duty-params",
remove:"#duty_event"
},
default_event:{show:"#poplist_program",
hide:".program-params",
remove:"#duty_event",
}
};
$("#btn_remove_event_type").click(function() {
var selectedId = $(".selected-type").attr("id");
if (!action_map[selectedId])
selectedId = "default_event";
$(action_map[selectedId].show).show();
$(action_map[selectedId].hide).hide();
$(action_map[selectedId].remove).remove();
}
});
Personally the cleanest and most configurable way i can see is using a map and looping of the methods. This is generic as different mappings can have different methods. You can even extend this further so you can have a method which will construct the mappings for you.. i.e. addNewType([{methodName:selector}, etc..]).
var configuration = {
temperature_event: {
show: "#poplist_temp",
hide: ".temperature-params",
remove: "#temperature_event"
},
load_event: {
show: "#poplist_load",
hide: ".load-params",
remove: "#load_event"
}
// etc etc.
};
$("#btn_remove_event_type").click(function() {
var fn, selector, typeConfig = $(".selected-type").attr("id");
for (fn in typeConfig)
$(typeConfig[fn])[fn]();
});
});

My signs aren't changing

I am new to JS just playing around to understand how it works.
Why isn't my sign (+,-) changing?
When the div expand it remains with a + sigh never goes to -
Thanks
$(document).ready(function(){
$(".expanderHead").click(function(){
$(this).next(".expanderContent").slideToggle();
if ($(".expanderSign").text() == "+"){
$(".expanderSign").html("−")
}
else {
$(".expanderSign").text("+")
}
});
});
Just guessing at the relationship, since you haven't shown your HTML, but you probably need something like this:
$(document).ready(function () {
$(".expanderHead:visible").click(function () {
var content = $(this).next(".expanderContent");
var sign = $(this).find(".expanderSign");
if (content.is(":visible")) {
content.slideUp();
sign.text("+");
} else {
var expanded = $(".expanderContent:visible");
if (expanded.length > 0) {
expanded.slideUp();
expanded.prev(".expanderHead").find(".expanderSign").text("+");
}
content.slideDown();
sign.text("-");
}
});
});
FIDDLE

Event doesn't get added in a for-loop

This is the html. If a link is clicked I want to replace the span-element in front of it with some text.
<p><span id="sp1">that1</span> Update1</p>
<p><span id="sp2">that2</span> Update2</p>
<p><span id="sp3">that3</span> Update3</p>
<p><span id="sp4">that4</span> Update4</p>
<p><span id="sp5">that5</span> Update5</p>
As you can see, my idea was to give the spans en the anchors identical id's and a number.
In my jquery-code I loop through all the anchor-elements, give them a click-event that causes the span-element in front of it to be replaced.
<script type="text/javascript" >
$(document).ready(function() {
var numSpans = $("span").length;
for (n=0;n<=numSpans;n++) {
$("a#update" + n).click(function(e){
$('span#sp' + n).replaceWith('this');
e.preventDefault();
});
}
});
</script>
For some reason this does not work.
What am I doing wrong?
The problem with your original code is that you're creating a closure on the variable n. When the event handler is called, it is called with the value of n at the point of invocation, not the point of declaration. You can see this by adding an alert call:
$(document).ready(function() {
var numSpans = $("span").length;
for (n = 1; n <= numSpans; n++) {
$("a#update" + n).click(function(e) {
alert(n); //Alerts '6'
$('span#sp' + n).replaceWith('this');
e.preventDefault();
});
}
})
One way to fix this is to create a closure on the value of n in each iteration, like so:
$(document).ready(function() {
var numSpans = $("span").length;
for (n = 1; n <= numSpans; n++) {
$("a#update" + n).click(
(function(k) {
return function(e) {
alert(k);
$('span#sp' + k).replaceWith('this');
e.preventDefault();
}
})(n)
);
}
})
However, this is messy, and you'd do better to use a more jQuery-y method.
One way to do this would be to remove the ids from your code. Unless you need them for something else, they're not required:
<p><span>that1</span> Update1</p>
<p><span>that2</span> Update2</p>
<p><span>that3</span> Update3</p>
<p><span>that4</span> Update4</p>
<p><span>that5</span> Update5</p>
jQuery:
$(function() {
$('a.update').live('click', function() {
$(this).siblings('span').replaceWith("Updated that!");
});
});
jsFiddle
Don't create functions in a loop. With jQuery, there's no need for an explicit loop at all.
$(function()
{
$('span[id^=sp]').each(function(n)
{
$('#update' + n).click(function(e)
{
$('#sp' + n).replaceWith(this);
return false;
});
});
});
Demo: http://jsfiddle.net/mattball/4TVMa/
You can do way better than that, though:
$(function()
{
$('p > a[id^=update]').live('click', function(e)
{
$(this).prev().replaceWith(this);
return false;
});
});
Demo: http://jsfiddle.net/mattball/xySGW/
Try this:
$(function(){
$("a[id^='update']").click(function(){
var index = this.id.replace(/[^0-9]/g, "");
$("span#sp" + index).replaceWith(this);
e.preventDefault();
});
});

Categories

Resources