Need a better way to mass modifying css Class - javascript

I have a huge collection of list elements.
the concept is that the user can select only two items from that collection.
I am showing a check/Uncheck as an image infront of the list item, just for visual purposes that the list is selected or not.
The image is defined in a class, so I have to switch classes to show selected or unselected.
This is they way I am currently modifying the class but I think it might be too heavy.
function showAsSelected(selectedArr, selectedCat) {
var allLinks = document.getElementsByClassName("linkRef");
var len = allLinks.length;
for (var i = 0; i < len; i++) {
allLinks[i].setAttribute('class', 'linkRef subCategLink');
}
for (var i = 0; i < selectedArr.length; i++) {
selectedArr[i].setAttribute('class', 'linkRef subCategLinkChkd');
}
}
'allLinks' gets all the elements having class "linkRef". counting above 100 sometimes. The first loop modifies class to 'linkRef subCategLink'. This means it will remove 'subCategLinkChkd' from two elements (Running a loop on hundreds only to modify two).
The second loop sets the class only on the two elements which are referenced in the "selectedArr" array.

I assuming that you have a similar HTML structure (and if so) you can try something like this.
jsFiddle
(function () {
"use strict";
var list = document.getElementById("list"),
selectedInputs = [],
shifted = null;
list.addEventListener("change", function (e) {
var target = e.target,
index = selectedInputs.indexOf(target);
if (target.nodeName.toLowerCase() === "input" &&
target.type.toLowerCase() === "checkbox" &&
target.classList.contains("linkRef")) {
if (target.checked && index === -1) {
target.setAttribute('class', 'linkRef subCategLinkChkd');
selectedInputs.push(target);
} else if (target.checked === false && index !== -1) {
selectedInputs.splice(index, 1);
target.setAttribute('class', 'linkRef subCategLink');
}
if (selectedInputs.length > 2) {
shifted = selectedInputs.shift();
shifted.setAttribute('class', 'linkRef subCategLink');
shifted.checked = false;
}
}
}, false);
}());
Updated

Related

JS Array output Trouble

I have an array of "active classes" as each class becomes active it gets added to my array. I would like to take that array test to make sure each class is only in it ONCE and then append its text to the dom as a li.
var filtersArray = [],
activeFilters = state.activeFilter.replace('.', ' ');
//by default .mix and none is in activeFilters
//.hatch,.sedan,.suv get added later -> these are the only ones I want
// activeFilters really equals -> activeFilters = ['.mix','.hatch','.sedan','.suv'];
if (activeFilters != ".mix" || activeFilters != "none") {
if ($.inArray(activeFilters, filtersArray) == -1) {
filtersArray.push(activeFilters);
for (index = 0; index < filtersArray.length; ++index) {
//console.log(filtersArray[index]);
$('.filter-result-list').append('<li>' + filtersArray + '<i class="close">x</i></li>');
}
}
}
jsFiddle: http://jsfiddle.net/im_cr/8zyhhftL/11/

write this line of jQuery in vanilla javascript

Can someone help me write this line of jQuery in javascript. It applies a single rule of styling to a class.
$('.dataCard').css('visibilty', 'visible !important');
As !important doesn't apply when setting styles with javascript, it would be something like this
var elems = document.querySelectorAll('.dataCard');
for (var i=elems.length; i--;) {
elems[i].style.visibility = 'visible';
}
If you want to make a general purpose function that can replace what you had in jQuery, you could do this:
function setStyle(elemOrSelector, prop, val) {
var items;
if (typeof elemOrSelector === "string") {
// run selector query
items = document.querySelectorAll(elemOrSelector);
} else if (elemOrSelector.nodeName) {
// must be single DOM object
items = [elemOrSelector];
} else if (elemOrSelector.length)
// must be an array or nodeList
items = elemOrSelector;
} else {
// don't know what it is
return;
}
for (var i = 0; i < items.length; i++) {
items[i].style[prop] = val;
}
}
setStyle('.dataCard', "visibility", "visible");
This general purpose function allows you to pass either a DOM element, an array like list of DOM elements or a selector string.
If you don't want the general purposeness, then you can just use this:
function setStyle(selector, prop, val) {
var items = document.querySelectorAll(selector);
for (var i = 0; i < items.length; i++) {
items[i].style[prop] = val;
}
}
setStyle('.dataCard', "visibility", "visible");

How to remove child (html div) from parentnode with for loop

So im trying to remove HTML div's from it's parent div.
I have a div which contains the div that need to be removed, selectedDivs.
However my current function refuses to remove more then 1 item from it's parent div...
Here's what i tried:
Console output: http://pastebin.com/KCeKv1pG
var selectedDivs = new Array();
canvas.innerHTML += "<div id="+currDev+" class='DRAGGABLE' onClick='addBorder(this)>" + "<img src='/devices/" + device + ".gif'></img></div>";
function addBorder(e) {
if (ctrlBeingpressed == true) {
selectedDivs.push(e);
e.style.border = "2px dotted black";
}
}
function deleteSelected() {
console.log(selectedDivs);
var len = selectedDivs.length;
for (var i = 0, len; i < len; i++){
console.log("before html remove: " + selectedDivs.length);
var node = selectedDivs[i];
node.parentNode.removeChild(node);
console.log("after html remove: " + selectedDivs.length);
for (var i in racks)
{
console.log(i);
if(node.id == racks[i].refdev)
{
console.log("Found in rack");
for (z = 1; z < racks[i].punkt.length; z++)
{
if(racks[i].punkt[z] != undefined)
{
if(racks[i].punkt[z].y.indexOf("S") > -1) //If it's an already defined point at an S card
{
//Clearing the TD
$("#sTab tr:eq("+(cardsS.indexOf(racks[i].punkt[z].y)+1)+") td:eq("+(racks[i].punkt[z].x-1)+")").html(" ");
$("#sTab tr:eq("+(cardsS.indexOf(racks[i].punkt[z].y)+1)+") td:eq("+(racks[i].punkt[z].x-1)+")").css("background-color","#E6E6E6");
}
else // Then it must be a P or V card
{
$("#pvTab tr:eq("+(cardsPV.indexOf(racks[i].punkt[z].y)+1)+") td:eq("+(racks[i].punkt[z].x-1)+")").html(" ");
$("#pvTab tr:eq("+(cardsPV.indexOf(racks[i].punkt[z].y)+1)+") td:eq("+(racks[i].punkt[z].x-1)+")").css("background-color","#E6E6E6");
}
}
}
console.log("Found in rack, breaking this loop");
delete racks[i];
break;
}
}
}
As discussed in the comments, there's a problem with resetting the value of the i variable within the nested loop. I took the liberty of editing the code to the way I would write it. I jQueried up some things since you're already using it anyway. (This code assumes you can target IE 9 or later and thus use Array.prototype.forEach and also that racks is an array, which seemed to be the case from the original.)
var selectedDivs = [];
$(canvas).append("<div id="+currDev+" class='DRAGGABLE' onClick='markSelected(this)'><img src='/devices/" + device + ".gif'></img></div>");
function markSelected(div) {
if (ctrlBeingpressed == true) {
selectedDivs.push(div);
$(div).css("border", "2px dotted black");
}
}
function deleteSelected() {
var i, z, deletedDivIDs = [];
console.log(selectedDivs);
selectedDivs.forEach(function(selectedDiv, index, selectedDivs) {
console.log("Removing", selectedDiv, "at index", index);
divIDs.push(selectedDiv.id);
selectedDiv.parentNode.removeChild(selectedDiv);
});
racks.forEach(function(rack, index, racks) {
console.log(i);
if(deletedDivIDs.indexOf(rack.refdev) !== -1) {
console.log("Found in rack");
for (z = 1; z < rack.punkt.length; z++) {
if(rack.punkt[z] !== undefined) {
if(rack.punkt[z].y.indexOf("S") > -1) {//If it's an already defined point at an S card
//Clearing the TD
$("#sTab tr:eq("+(cardsS.indexOf(rack.punkt[z].y)+1)+") td:eq("+(rack.punkt[z].x-1)+")").css("background-color","#E6E6E6").empty();
}
else { // Then it must be a P or V card
$("#pvTab tr:eq("+(cardsPV.indexOf(rack.punkt[z].y)+1)+") td:eq("+(rack.punkt[z].x-1)+")").css("background-color","#E6E6E6").empty();
}
}
}
racks[rack] = undefined;
}
});
}
I didn't have a chance to test this in real code since we still don't know what racks looks like, but hopefully this gets you further down the road.
you have created nested for loops with the same var i=0, It could be your problem.
And the other point I like to point out is, if racks is an array you'd better not use for(var i in racks) because it would scan all other prototype attributes in your Array.prototype, which depends on what libraries you have used in your page. and If racks is not an array, it would scan all other properties in your Object.prototype, what I mean is, if it is just a iteration using for(var i in racks) is not safe, because adding a new Javascript library could mess with your code.

Using labels like HTML5 placeholder

I am trying to use <label> elements in my html contact form like the HTML5 placeholder attribute for inputs. I have written the following JavaScript to to act as a reusable function witch will provide the following functionality.
Find the input by name.
Get the value of the input.
Find the label belonging to the input.
Change the label style depending on the state of the input.
Change the label style depending on the value of the input.
However it is not working and I don't know why as no errors appear in the console. What am I doing wrong? here is a JS Fiddle with code
function placeholder(field_name) {
// Get the input box with field_name
// Then get input value
var box = document.getElementsByName(field_name);
var i;
for (i = 0; i < box.length; i++) {
var value = document.getElementById(box[i].value);
}
// Get the labels belonging to each box using the HTML for attribute
var labels = document.getElementsByTagName('LABEL');
for (i = 0; i < labels.length; i++) {
if (labels[i].htmlFor !== '') {
var elem = document.getElementById(labels[i].htmlFor);
if (elem) {
box.label = labels[i];
}
}
}
// Colors
var focusColor = "#D5D5D5";
var blurColor = "#B3B3B3";
// If no text is in the box then show the label grey color
box.onblur = function () {
box.label.style.color = blurColor;
};
// If input focuses change label color to light grey
box.onfocus = function () {
box.label.style.color = focusColor;
};
// If there is text in the box then hide the label
if (box.value !== "") {
// Quick do something, hide!
box.label.style.color = "transparent";
}
}
// Call the function passing field names as parameters
placeholder(document.getElementsByName("email"));
placeholder(document.getElementsByName("firstName"));
placeholder(document.getElementsByName("lastName"));
This might be considered a little overkill on the number of listeners I've used, feel free to remove any you think unnecessary, but I've tried to employ your HTML structure as you have it and give you all desired effects. It should work for either the <label>s for matching the <input>s id OR matching it's <name> (given no id matches). I'll always say prefer using an id over name. I believe this JavaScript should also work in all browsers too, except the addEventListener for which you'd need a shim for old IE versions (let me know if it doesn't in one/the error message).
Demo
var focusColor = "#D5D5D5", blurColor = "#B3B3B3";
function placeholder(fieldName) {
var named = document.getElementsByName(fieldName), i;
for (i = 0; i < named.length; ++i) { // loop over all elements with this name
(function (n) { // catch in scope
var labels = [], tmp, j, fn, focus, blur;
if ('labels' in n && n.labels.length > 0) labels = n.labels; // if labels provided by browser use it
else { // get labels from form, filter to ones we want
tmp = n.form.getElementsByTagName('label');
for (j = 0;j < tmp.length; ++j) {
if (tmp[j].htmlFor === fieldName) {
labels.push(tmp[j]);
}
}
}
for (j = 0; j < labels.length; ++j) { // loop over each label
(function (label) { // catch label in scope
fn = function () {
if (this.value === '') {
label.style.visibility = 'visible';
} else {
label.style.visibility = 'hidden';
}
};
focus = function () {
label.style.color = focusColor;
};
blur = function () {
label.style.color = blurColor;
};
}(labels[j]));
n.addEventListener('click', fn); // add to relevant listeners
n.addEventListener('keydown', fn);
n.addEventListener('keypress', fn);
n.addEventListener('keyup', fn);
n.addEventListener('focus', fn);
n.addEventListener('focus', focus);
n.addEventListener('blur', fn);
n.addEventListener('blur', blur);
}
}(named[i]));
}
};
placeholder("email"); // just pass the name attribute
placeholder("firstName");
placeholder("lastName");
http://jsfiddle.net/cCxjk/5/
var inputs = document.getElementsByTagName('input');
var old_ele = '';
var old_label ='';
function hide_label(ele){
var id_of_input = ele.target.id;
var label = document.getElementById(id_of_input + '-placeholder');
if(ele.target == document.activeElement){
label.style.display = 'none';
}
if (old_ele.value == '' && old_ele != document.activeElement){
old_label.style.display = 'inline';
}
old_ele = ele.target;
old_label = label;
}
for(var i = 0; i < inputs.length; i++){
inputs[i].addEventListener('click', hide_label);
}
I will point out a couple things, you will have to find away around the fact that the label is inside the input so users now can't click on half of the input and actually have the input gain focus.
Also I guess you want to do this in IE (otherwise I would strongly advise using the html5 placeholder!) which means you would need to change the ele.target to ele.srcElement.

Toggling a class on and off for list elements - CSS and ONLY Javascript

For this particular challenge I am required to toggle membership of an already created class for all <li> elements in two given lists (at the same time). If a <li> element in either list is not currently assigned the class, it is assigned the class; if a <li> element in either list is currently assigned the class, the class is removed. Everytime a button is clicked, the class is added and removed (e.g on first click, the class could be added - then on second click, the class could be removed, etc).
I have been asked to do this particular task specifically in normal JavaScript. While I know that jQuery would be the easier option, I have been asked to undertake this task with just normal JavaScript.
When I press the button, the css class is being applied as expected (here, the font-family, font-size, and the font-stretch properties are being altered); however, when I click on the button the second time, nothing changes (e.g the class being removed), and everything going back to normal.
If anyone can point me towards a non-jQuery way of adjusting my current code, let me know.
Thanks for your help :).
Here is the relevant HTML, CSS, and JavaScript for this task:
HTML:
<ul id="newZealandList">
<li>Auckland</li>
<li>Wellington</li>
<li>Christchurch</li>
<li>Tauranga</li>
<li>Dunedin</li>
</ul>
<ul id="usaList">
<li>Los Angeles</li>
<li>San Francisco</li>
<li>San Diego</li>
<li>Denver</li>
<li>Boulder</li>
</ul>
<button id="modifyListsToggle">Change Lists - Toggle</button>
CSS:
.modifyListElements{
font-family:"Comic Sans MS", cursive;
font-size:24px;
font-stretch:extra-expanded;
}
JavaScript
var newZealandListItems = document.getElementById("newZealandList").getElementsByTagName("li");
var usaListItems = document.getElementById("usaList").getElementsByTagName("li");
function addClass(obj)
{
obj.className="modifyListElements";
}
function removeClass(obj)
{
obj.className = "";
}
function toggleClass()
{
for (var i = 0; i < newZealandListItems.length; i++) {
if(i.className != "modifyListElements") {
//newZealandListItems[i].className = "modifyListElements";
addClass(newZealandListItems[i]);
}
else
{
//newZealandListItems[i].className = "";
removeClass(newZealandListItems[i]);
}
}
for (var i = 0; i < usaListItems.length; i++) {
if(i.className != "modifyListElements") {
//usaListItems[i].className = "modifyListElements";
addClass(usaListItems[i]);
}
else
{
//usaListItems[i].className = "";
removeClass(usaListItems[i]);
}
}
}
var modifyListsToggle = document.getElementById("modifyListsToggle");
modifyListsToggle.onclick = toggleClass;
Issue is here
for (var i = 0; i < newZealandListItems.length; i++) {
if(i.className != "modifyListElements") {
//con....
and here
for (var i = 0; i < usaListItems.length; i++) {
if(i.className != "modifyListElements") {
//con....
i is just the loop counter variable, you need to use it access the item at that index so should be
for (var i = 0; i < newZealandListItems.length; i++) {
if (newZealandListItems[i].className != "modifyListElements") {
//con..
and
for (var i = 0; i < usaListItems.length; i++) {
if (usaListItems[i].className != "modifyListElements") {
//con..
On another note, this code will potentially have an issue if multiple classes are used, as the .className property will return all the classes on an element. If that may be an issue in the future, I would pursue using a className.replace('modifyListElements','') for remove (that way it will only remove that class and not other ones if there). And the tests for one class on className will also not work if multiple classes are there. In this case pursuing a test and then a .replace would probably be the solution.
A small bug in your code checking classname . I modified your code it working fine . Please check it
http://jsfiddle.net/kRva7/ .
var newZealandListItems = document.getElementById("newZealandList").getElementsByTagName("li");
var usaListItems = document.getElementById("usaList").getElementsByTagName("li");
function addClass(obj)
{
obj.className="modifyListElements";
}
function removeClass(obj)
{
obj.className = "";
console.log(obj.className);
}
function toggleClass()
{
for (var i = 0; i < newZealandListItems.length; i++) {
var item = newZealandListItems[i];
if(item.className != "modifyListElements") {
//newZealandListItems[i].className = "modifyListElements";
addClass(item);
}
else
{
//newZealandListItems[i].className = "";
removeClass(item);
}
}
for (var i = 0; i < usaListItems.length; i++) {
var item = usaListItems[i];
if(item.className != "modifyListElements") {
//usaListItems[i].className = "modifyListElements";
addClass(item);
}
else
{
//usaListItems[i].className = "";
removeClass(item);
}
}
}
var modifyListsToggle = document.getElementById("modifyListsToggle");
modifyListsToggle.onclick = toggleClass;
​
The easiest way you could do this (although IE support is problematic) is like this:
var btn = document.getElementById('modifyListsToggle'),
lists = document.querySelectorAll('ul');
btn.addEventListener('click', function(e) {
for(var i = 0; i < lists.length; i++) {
var items = lists[i].querySelectorAll('li');
for(var j = 0; j < items.length; j++) {
items[j].classList.toggle('modifyListElements');
}
}
}, false);
demo
querySelectorAll() is only supported by IE8+
addEventListener() is only supported by IE9+
classList is only supported by IE10

Categories