How to make this js repetitive code into a loop? - javascript

I have this code, repeating 20 times with only change of variables prefix. How can i possibly make a loop and iterate by it to avoid this huge block of code?
It is used in my website and I want to make the development process more clear.
var keepElements = document.getElementsByName("keep-type");
for(var i = 0; i < keepElements.length; i++){
keep = document.getElementById(keepElements[i].value);
if(keepElements[i].checked == true){
keep.style.display = "block";
}
else{keep.style.display = "none";}
}
var offworkshopElements = document.getElementsByName("offworkshop-type");
for(var i = 0; i < offworkshopElements.length; i++){
offworkshop = document.getElementById(offworkshopElements[i].value);
if(offworkshopElements[i].checked == true){
offworkshop.style.display = "block";
}
else{offworkshop.style.display = "none";}
}
var defworkshopElements = document.getElementsByName("defworkshop-type");
for(var i = 0; i < defworkshopElements.length; i++){
defworkshop = document.getElementById(defworkshopElements[i].value);
if(defworkshopElements[i].checked == true){
defworkshop.style.display = "block";
}
else{defworkshop.style.display = "none";}
}

Move the logic to a function:
function keepElements(name) {
var keepElements = document.getElementsByName(name);
for (var i = 0; i < keepElements.length; i++) {
keep = document.getElementById(keepElements[i].value);
if (keepElements[i].checked == true) {
keep.style.display = "block";
}
else { keep.style.display = "none"; }
}
}
keepElements("keep-type");
keepElements("offworkshop-type");
keepElements("defworkshop-type");

If I didn't miss any details, you're repeating the same block 3 times:
["keep-type", "offworkshop-type", "defworkshop-type"].forEach(name => {
var elements = document.getElementsByName(name);
for(var i = 0; i < elements.length; i++){
element = document.getElementById(elements[i].value);
if(elements[i].checked == true){
element.style.display = "block";
}
else{element.style.display = "none";}
}
});
I prefer the array / forEach syntax, because the block starts with the list of name. But you could use a for...of loop instead.
for (const name of ["keep-type", "offworkshop-type", "defworkshop-type"]) {
var elements = document.getElementsByName(name);
for(var i = 0; i < elements.length; i++){
element = document.getElementById(elements[i].value);
if(elements[i].checked == true){
element.style.display = "block";
}
else{element.style.display = "none";}
}
}

The answers saying you should put the common functionality into a single function are absolutely right.
I just wanted to add that there are also solutions on the query side of things:
you could have all the elements be collected in one javascript function, querySelectorAll()
const elements = document.querySelectorAll('[name="keep-type"], [name="offworkshop-type"], [name="defworkshop-type"]')
Alternatively you could give them all the same class and select by that class.
const elements = document.getElementsByClassName("requiredElements");
And this way you have all your elements in one array.

Related

Javascript passing info from one function to another

I've created a JS function that hides a certain amount of breadcrumbs if there are too many. They are replaced by a button (ellipsis), when you click the button the hidden breadcrumbs are revealed.
The Problem: I loop through the breadcrumbs to see if there are enough to hide. If there are I hide them. But I can't figure out how to then call the code to create the button. If I call the button code in the loop I get more than 1 button generated.
Right now the button will always appear whether there are enough breadcrumbs to hide or not.
In my mind, I would have the for loop with the if statement return true to what would then be the button function. But I can't figure out how to do this. Please offer any pointers for restructuring this code if you can.
Here's a Codepen: https://codepen.io/sibarad/pen/GRvpEbp
Basic HTML:
<nav aria-label="breadcrumb">
<ol class="c-breadcrumb mb-7 md:mb-8">
<li class="c-breadcrumb-item">
Breadcrumb 1
</li>
<li class="c-breadcrumb-item">
Breadcrumb 2
</li>
<li class="c-breadcrumb-item">
Longer Breadcrumb Name 03
</li>
</ol>
</nav>
Javascript:
function breadcrumb() {
// Target specific breadcrumbs, not 1st or last 2
let hiddenbreadcrumb = document.querySelectorAll('.c-breadcrumb-item:nth-child(1n+2):nth-last-child(n+3)');
// Loop through select breadcrumbs, if length is greater than x hide them.
for (var i = 0; i < hiddenbreadcrumb.length; i++) {
if(hiddenbreadcrumb.length >= 3) {
hiddenbreadcrumb[i].style.display = "none";
}
}
// This would be the button function, but I don't know how to engage this only if the if statement above was met.
let li = document.createElement('li');
li.className = 'c-breadcrumb-item';
let ellipbutton = document.createElement('button');
ellipbutton.type = 'button';
ellipbutton.innerHTML = '...';
ellipbutton.className = 'c-breadcrumb_btn u-btn-clear';
ellipbutton.onclick = function() {
console.log("clicked");
for (var i = 0; i < hiddenbreadcrumb.length; i++) {
hiddenbreadcrumb[i].style.display = "flex";
}
li.style.display = "none";
};
li.appendChild(ellipbutton);
let container = document.querySelector('.c-breadcrumb-item:first-child');
container.insertAdjacentElement("afterend", li);
}
breadcrumb();
We can refactor your code slightly to achieve this - the if statement which checks whether there are more than 3 breadcrumbs doesn't need to be inside the for loop - it's redundant to keep checking the same value multiple times.
If we move that outside the loop then it can
a) prevent unnecessary looping when there aren't enough breadcrumbs, and
b) wrap around the button creation code as well, which should solve your problem.
For example:
if (hiddenbreadcrumb.length >= 3) {
for (var i = 0; i < hiddenbreadcrumb.length; i++) {
hiddenbreadcrumb[i].style.display = "none";
}
let li = document.createElement('li');
li.className = 'c-breadcrumb-item';
let ellipbutton = document.createElement('button');
ellipbutton.type = 'button';
ellipbutton.innerHTML = '...';
ellipbutton.className = 'c-breadcrumb_btn u-btn-clear';
ellipbutton.onclick = function() {
console.log("clicked");
for (var i = 0; i < hiddenbreadcrumb.length; i++) {
hiddenbreadcrumb[i].style.display = "flex";
}
li.style.display = "none";
};
let container = document.querySelector('.c-breadcrumb-item:first-child');
container.insertAdjacentElement("afterend", li);
}
It looks like some small initialization issues. This should correct it:
Change this:
let hiddenbreadcrumb = document.querySelectorAll('.c-breadcrumb-item:nth-child(1n+2):nth-last-child(n+3)');
// Loop through select breadcrumbs, if length is greater than x hide them.
for (var i = 0; i < hiddenbreadcrumb.length; i++) {
if(hiddenbreadcrumb.length >= 3) {
hiddenbreadcrumb[i].style.display = "none";
}
}
to this:
let hiddenbreadcrumb = document.querySelectorAll('.c-breadcrumb-item');
if(hiddenbreadcrumb.length < 3)
return
for (var i = 1; i < hiddenbreadcrumb.length - 1; i++) {
hiddenbreadcrumb[i].style.display = "none";
}
Try this... it allows 3 li items as item1 ... item2ndLast, itemLast
(function () {
"use strict";
function breadcrumb() {
let hiddenbreadcrumb = document.querySelectorAll(".c-breadcrumb-item:nth-child(1n+2)");
if (hiddenbreadcrumb.length <= 3) return;
for (var i = 1; i < hiddenbreadcrumb.length - 1; i++) {
hiddenbreadcrumb[i].style.display = "none";
}
let li = document.createElement("li");
li.className = "c-breadcrumb-item";
let ellipbutton = document.createElement("button");
ellipbutton.type = "button";
ellipbutton.innerHTML = "...";
ellipbutton.className = "c-breadcrumb_btn u-btn-clear";
ellipbutton.onclick = function () {
console.log("clicked");
for (var i = 0; i < hiddenbreadcrumb.length; i++) {
hiddenbreadcrumb[i].style.display = "flex";
}
li.style.display = "none";
};
li.appendChild(ellipbutton);
let container = document.querySelector(".c-breadcrumb-item:first-child");
container.insertAdjacentElement("afterend", li);
}
breadcrumb();
})();

Create an array of a parents children in javascript

I am trying to create an array of a parent div's (id="lol") children and them fetch them to change display:none; except for the child with id="a". I've tried this but it doesn't work. How can I improve this to get it to work?
function myFunction() {
var x = document.getElementById('a');
var children = [].slice.call(document.getElementById('lol').getElementsByTagName('*'),0);
var arrayLength = children.length;
for (var i = 0; i < arrayLength; i++) {
var name = children[i].getAttribute('id');
var z = document.getElementById(name);
z.style.display = 'none';
}
x.style.display = 'block';
}
If every child has an id attribute than it will work. Otherwise, some children might not have id attribute, in that case variable z will be undefined and accessing style property over z which is undefined will give error. Simple fix would be just handling undefined variable:
if(z)
z.style.display = 'none';
Same goes with variable x, too.
function myFunction() {
var children = [].slice.call(document.getElementById('lol').getElementsByTagName('*'),0);
var arrayLength = children.length;
for (var i = 0; i < arrayLength; i++) {
children[i].style.display = 'none';
}
document.getElementById('a').style.display = 'block';
}
How about using jQuery?
$('#lol').children(':not(#a)').hide();
If jQuery is not an option you can do this:
var lol = document.getElementById('lol');
var children = lol.querySelectorAll(':not(#a)');
for(var i=0;i<children.length;i++) {
children[i].style.display = 'none';
}
Even more "low-level":
var lol = document.getElementById('lol');
var children = lol.childNodes;
for(var i=0;i<children.length;i++){
if(children[i].id != 'a') {
children[i].style.display = 'none';
}
}

How to delete td without any id

Hello i want to remove "Have you served in the military ?" and "No" if Answer is "No" but when it will "Yes" than it should show.
Whatever i have tried but it's not working
<script type="text/javascript">
(function(){
for(i = 0; (a = document.getElementsByTagName("td")[i]); i++){
if(document.getElementsByTagName("span")[i].innerHTML.indexOf('Have you served in the military') > -1){
document.getElementsByTagName("td")[i].style.display = "none";
}
}
})();
</script>
You could use child of the element or just do a find replace or even hide and show.
You can get all td elements like you already did and than get the span elements inside them:
var tds = document.getElementsByTagName('TD');
for (var i = 0, l = tds.length; i != l; ++i) {
var spans = tds[i].getElementsByTagName('SPAN');
for (var j = 0, l2 = spans.length; j != l2; ++j) {
var span = spans[j];
if ((span.textContent = span.innerText).indexOf('Have you served in the military') != -1) {
span.style.display = 'none';
break;
}
}
}
EDIT: OP wants to only delete the span if there is a td with the content "No" (also delete the td element)
var tds = document.getElementsByTagName('TD');
var tdsLength = tds.length;
var answerNoFound = false;
for (var i = 0; i != tdsLength; ++i) {
var td = tds[i];
if ((td.textContent = td.innerText) == 'No') {
td.style.display = 'none';
answerNoFound = true;
break;
}
}
if (answerNoFound)
for (var i = 0; i != tdsLength; ++i) {
var spanFound = false;
var spans = tds[i].getElementsByTagName('SPAN');
for (var j = 0, l = spans.length; j != l; ++j) {
var span = spans[j];
if ((span.textContent = span.innerText).indexOf('Have you served in the military') != -1) {
span.style.display = 'none';
spanFound = true;
break;
}
}
if (spanFound)
break;
}
It looks like you have an application form and document probably has more spans, some outside the td elements, so you don't get correct selection of spans versus td.
So when you are comparing span content, it is most likely not the span that is inside your looped td.
<script type="text/javascript">
(function(){
for(i = 0; (a = document.getElementsByTagName("td")[i]); i++){
if(a.getElementsByTagName("span")[0].innerHTML.indexOf('Have you served in the military') > -1){
a.style.display = "none";
}
}
})();
</script>
I changed the if statement to select span inside your looped td, that should do it.

What is the plain Javascript equivalent of .each and $(this) when used together like in this example?

What is the plain Javascript equivalent of .each and $(this).find when used together in this example?
$(document).ready(function(){
$('.rows').each(function(){
var textfield = $(this).find(".textfield");
var colorbox = $(this).find(".box");
function colorchange() {
if (textfield.val() <100 || textfield.val() == null) {
colorbox.css("background-color","red");
colorbox.html("Too Low");
}
else if (textfield.val() >300) {
colorbox.css("background-color","red");
colorbox.html("Too High");
}
else {
colorbox.css("background-color","green");
colorbox.html("Just Right");
}
}
textfield.keyup(colorchange);
}
)});
Here's a fiddle with basically what I'm trying to accomplish, I know I need to use a loop I'm just not sure exactly how to set it up. I don't want to use jquery just for this simple functionality if I don't have to
http://jsfiddle.net/8u5dj/
I deleted the code I already tried because it changed every instance of the colorbox so I'm not sure what I did wrong.
This is how to do what you want in plain javascript:
http://jsfiddle.net/johnboker/6A5WS/4/
var rows = document.getElementsByClassName('rows');
for(var i = 0; i < rows.length; i++)
{
var textfield = rows[i].getElementsByClassName('textfield')[0];
var colorbox = rows[i].getElementsByClassName('box')[0];
var colorchange = function(tf, cb)
{
return function()
{
if (tf.value < 100 || tf.value == null)
{
cb.style.backgroundColor = 'red';
cb.innerText = "Too Low";
}
else if (tf.value > 300)
{
cb.style.backgroundColor = 'red';
cb.innerText = "Too High";
}
else
{
cb.style.backgroundColor = 'green';
cb.innerText = "Just Right";
}
};
}(textfield, colorbox);
textfield.onkeyup = colorchange;
}
var rows = document.querySelectorAll('.rows');
for (var i=0; i<rows.length; i++) {
var row = rows[i];
var textfield = row.querySelector('.textfield');
var colorbox = row.querySelector('.box');
// ...
}
Note that you must use a for loop to iterate the rows because querySelectorAll() does not return an array, despite appearances. In particular, that means that .forEach() isn't valid on the returned list.

for loop failing to loop continuously

var _target=document.querySelectorAll('.post .content');
var isYT = /youtube|youtu.be/gi;
for (i = 0; i < _target.length; i++) {
var _tar = _target[i].children;
for (var j = 0; j < _tar.length; j++) {
var vidID;
if (_tar[j].tagName == "A") {
if (isYt.test(_tar[j].href) == true) {
_eles.push(_tar[j]);
}
}
if (_tar[j].tagName == "EMBED") {
if (isYt.test(_tar[j].src) == true) {
_eles.push(_tar[j]);
}
}
} //end for loop j
} //end for loop i
console.log(_eles);
The HTML looks sort of like this:
<div>
Video 1
Video 2
<embed src="www.youtube.com/v/239324"></embed>
</div>
<div>
Video 1
Video 2
<embed src="www.youtube.com/v/239324"></embed>
</div>
Though the returning array Object with my console logging is only showing one a element and one embed element. I have to continuously invoke this myself to get all the links and embeds to be placed into the array Object. Any one see any errors I've written, just been working on this issue for about 3 hours now and it is tiring me. Any help is greatly appreciated.
thank you
I have changed your code this way:
var _target = document.querySelectorAll("div");
var _eles = [];
var isYt=new RegExp("\youtube.com");
for (var i = 0; i < _target.length; i++) {
var _tar = _target[i].childNodes;
for (var j = 0; j < _tar.length; j++) {
var vidID;
if(_tar[j].nodeType != 1) continue;
if (_tar[j].tagName.toLowerCase() == "a") {
if (isYt.test(_tar[j].href)) {
_eles.push(_tar[j]);
}
}
if (_tar[j].tagName.toLowerCase() == "embed") {
if (isYt.test(_tar[j].src)) {
_eles.push(_tar[j]);
}
}
} //end for loop j
} //end for loop i
console.log(_eles);
and it works, check this DEMO
but my favorite way to do this is like this:
var _target = document.querySelectorAll("div>a, div>embed");
var _eles = [];
var isYt=new RegExp("\youtube.com");
for (var j = 0; j < _target.length; j++) {
var vidID;
if (_target[j].tagName.toLowerCase() == "a") {
if (isYt.test(_target[j].href)) {
_eles.push(_target[j]);
}
}
if (_target[j].tagName.toLowerCase() == "embed") {
if (isYt.test(_target[j].src)) {
_eles.push(_target[j]);
}
}
} //end for loop j
console.log(_eles);
for this check this one DEMO
and if your isYT regexp is just as simple as I have used in my answer instead of all these lines of code you can simply do:
var _eles = document.querySelectorAll("div>a[href*='youtube.com/'],"+
"div>embed[src*='youtube.com/']");

Categories

Resources