Javascript loop of if statement - javascript

I'm trying to shorten my function that contains a number of if statements into a loop function but just can't seem to get it to work. It is currently working in Google Tag Manager as a Custom Javascript Macro.
I'm not a developer or very good at Javascript so any help will be greatly appreciated.
Code:
function()
{
if(document.getElementById('bfPage1').style.display !== "none")
{return document.getElementById('bfPage1').getAttribute('id');
}
else
if(document.getElementById('bfPage2').style.display !== "none")
{return document.getElementById('bfPage2').getAttribute('id');
}
else
if(document.getElementById('bfPage3').style.display !== "none")
{return document.getElementById('bfPage3').getAttribute('id');
}
else
{return document.getElementById('bfPage4').getAttribute('id')
}
}
Thanks in advance.

TLDR:
// with jQuery
var allPages = $('[id^=bfPage]')
var activeId = allPages.filter(':visible').attr('id')
// with ES6
const allPages = [].slice.apply(document.querySelectorAll('[id^=bfPage]'));
let activeId = allPages.find( e => e.style.display !== "none").id;
First of all you do not need to nest your ifs using else statements, because you are using return statement inside each of them. So after first refactoring you are going to have:
function()
{
if(document.getElementById('bfPage1').style.display !== "none")
{return document.getElementById('bfPage1').getAttribute('id');
}
if(document.getElementById('bfPage2').style.display !== "none")
{return document.getElementById('bfPage2').getAttribute('id');
}
if(document.getElementById('bfPage3').style.display !== "none")
{return document.getElementById('bfPage3').getAttribute('id');
}
if(document.getElementById('bfPage4').style.display !== "none")
{return document.getElementById('bfPage4').getAttribute('id')
}
}
Next step is to notice that that each time you are quering element twice:
function()
{
var page
page = document.getElementById('bfPage1')
if(page.style.display !== "none")
{
return page.getAttribute('id');
}
page = document.getElementById('bfPage2')
if(page.style.display !== "none")
{
return page.getAttribute('id');
}
page = document.getElementById('bfPage3')
if(page.style.display !== "none")
{
return page.getAttribute('id');
}
page = document.getElementById('bfPage4')
if(page.style.display !== "none")
{
return page.getAttribute('id')
}
}
Now you clearly see that you can use loop instead of repeating statements
function()
{
var pages = ['bfPage1','bfPage2','bfPage3','bfPage4']
for (var i in pages) {
page = document.getElementById(pages[i])
if(page.style.display !== "none")
{
return page.getAttribute('id');
}
}
}
Oh and you already know id
function()
{
var pages = ['bfPage1','bfPage2','bfPage3','bfPage4']
for (var i in pages) {
page = document.getElementById(pages[i])
if(page.style.display !== "none")
{
return pages[i];
}
}
}
Which is quite nice.
You can use jquery and make one-liner out of it:
$('[id^=bgPage]:visible]').attr('id')
This is good result but lets think why do we need to do this in the first place?
You have some logic to show and hide elements of the page and you want to check which one is visible.
Instead of doing that with styles of elements, lets do two things.
add new class for every element .bfPage
lets add class visible or active to the only visible element
This way your code to get id will be changed to:
$('.bfPage.active').attr('id')
Which in case of real application will be split on two parts:
somewhere in the beginning you will have
var allPages = $('.bfPages')
and somewhere where you need to find that element
var activeId = allPages.filter('.active').attr('id')
Of course if it is not the case you still can improve performance caching all bfPages
var allPages = $('[id^=bfPage]')
...
var activeId = allPages.filter(':visible').attr('id')

You could create an array of all your element ids and loop over that and return the id of the first visible one:
var pageIds = ['bfPage1', 'bfPage2', 'bfPage3', 'bfPage4'];
for(var i=0;i<pageIds.length;i++){
var elemId = pageIds[i];
if(document.getElementById(elemId).style.display !== 'none')
{
return elemId;
}
}

Is this what you're trying to do?
function(){
for (var i = 0; i < 4; i++){
var id = "bfPage" + i;
if(document.getElementById(id).style.display !== "none"){
return id;
}
}
}

Related

Unable to toggle div display using Javascript

I have written some vanilla JS in order to hide/display some divs, depending on the value of one of the fields in them.
My problem is that, while hiding them appropriately is working by default, trying to make them appear again, I am having issues as using getElementById is returning nulls.
I've googled and found numerous examples where similar code has worked and I cannot figure out why mine isn't actually working.
The JS I've written is below:
var hidden = false
document.addEventListener("keypress", function(event) {
if (event.key == '`') {
if (hidden == false) {
resultEntries = document.getElementsByClassName('result-row');
for (i = 0; i < resultEntries.length + 1; i++) {
var x = document.getElementById('root_cause_' + i)
if (x != null) {
var value = x.options[x.selectedIndex].value;
console.log('value' + value)
if (value == '') {
row = document.getElementById('line_' + i)
row.style.display = 'none';
}
}
}
hidden = true
} else {
resultEntries = document.getElementsByClassName('result-row');
for (i = 0; i < resultEntries.length + 1; i++) {
row = document.getElementById('line_' + i) // This is what is returning null
console.log(row)
row.style.display = 'block';
}
hidden = false
}
}
});
You overshoot your elements with the + 1 in the for loops
If you need this 1 based (not recommended) it is
for (let i = 1; i <= resultEntries.length; i++)
Also I think you can simplify this. Here is my guess without seeing the HTML
const resultEntries = document.querySelectorAll('.result-row');
document.addEventListener("keypress", function(event) {
if (event.key !== '`') return;
resultEntries.forEach((res, i) => {
let x = document.getElementById('root_cause_' + i)
if (x) {
let value = x.value;
console.log('value' + value)
document.getElementById('line_' + i).hidden = value === '';
}
})
});
Answering to save people from spending more time on this:
The issue was that my loop was starting from 0 while the elements were starting from line_1, meaning I was later on trying to alter the style of a null element.

EventListener doesnt work for all elements

My problem is that I'm stuck on trying to make several elements move onclick. When I click on any of my tags only the first one actually moves. I know I've probably made a mistake somewhere in the code but I can't find it, would be great if you guys can help me:) My code basically looks something like this:
var my_atag0 = document.createElement('a');
my_atag0.classList.add('nav_button');
var my_atag1 = document.createElement('a');
my_atag1.classList.add('nav_button');
var my_atag2 = document.createElement('a');
my_atag2.classList.add('nav_button');
var my_atag3 = document.createElement('a');
my_atag3.classList.add('nav_button');
function navToggle() {
var navStatus = false;
if (navStatus == false){
document.querySelector(".nav_button").style.marginTop = "100px";
navStatus = true;
}
else if (navStatus == true) {
document.querySelector(".nav_button").style.marginTop = "0px";
navStatus = false;
}
}
document.querySelectorAll(".nav_button").forEach(a => {a.addEventListener("click", function() {
navToggle()
})})
querySelector method can get only first element of selected ones.
querySelectorAll can get NodeList object of selected elements.
this is an example for you
if(...){
document.querySelectorAll(".nav_button").forEach(x => x.style.marginTop = "100px");
navStatus = true;
}
and in extra, you can use this way instead of setting value in if ~ else scope
if(boolean){
...
// when true
}else {
...
// when false
}
boolean = !boolean;
// toggles true and false
if (navStatus == false){
document.querySelector(".nav_button").style.marginTop = "100px";
navStatus = true;
}
else if (navStatus == true) {
document.querySelector(".nav_button").style.marginTop = "100px";
navStatus = false;
}
here, you are using queryselector to target all the elements. im sure it doesnt work because using querySelector to target a class will return an array containing the elements
try using querySelectorAll and loop through them to change the styles instead
const navButtons = document.querySelectorAll(".nav_button");
let navStatus = false;
if (navStatus === false){
for (var i = 0; i < navbuttons.length; i++) {
navbuttons[i].style.marginTop = 100px;
}
navStatus = true;
}
else if (navStatus === true) {
for (var i = 0; i < navbuttons.length; i++) {
navbuttons[i].style.marginTop = 100px;
}
navStatus = false;
}
sidenote : its not good to use var, use const and let instead
and put let navStatus = false; outside the function because sometimes putting it in the function doesn't work

At mobile device too late to use display none, block

At Pc it works well.
but mobile it is too late.
is any other faster method or the others?
function country_change(country,countries)
{
var cls = document.getElementsByClassName("country_events");
if(document.readyState == "loading")
{
alert('not loading.');
} else
{
for (n=0; n < cls.length; n++)
{
var elem = cls[n];
var div_elem = elem.getElementsByTagName('div').length;
for (m=1; m < div_elem; m++)
{
if (elem.getAttribute('name') == country)
{
if (elem.getElementsByTagName('div')[m].style.display == "none")
{
elem.getElementsByTagName('div')[m].style.display="block";
increaseHeight()
}
}
else
{
elem.getElementsByTagName('div')[m].style.display="none";
increaseHeight()
}
}
}
}
}
at pc it works about 1~3 seconds, but mobile it takes almost 10~20 sec.
i think display is not good method, but there is no other way isn't it?
The collection returned by:
var cls = document.getElementsByClassName("country_events");
is live, so any modification to the DOM may require the browser to refresh the collection. That may happen a lot more often that you think (and much more in some browsers than others), so you may want to convert that to an array or use querySelectorAll (which returns a static collection) instead:
var cls = document.querySelectorAll(".country_events");
Then you have:
var elem = cls[n];
var div_elem = elem.getElementsByTagName('div').length;
so it is good to cache elem, but getElementsByTagName also returns a live collection so use querySelectorAll again:
var div_elem = elem.querySelectorAll('div').length;
div_elem seems an inappropriate name, perhaps divCount would be better?
Then:
if (elem.getAttribute('name') == country)
you can save a method call by accessing the property directly:
if (elem.name == country)
And then the awfully wastefull:
if (elem.getElementsByTagName('div')[m].style.display == "none")
{
elem.getElementsByTagName('div')[m].style.display="block";
increaseHeight()
You got the list of divs earlier, so cache it there
var divs = elem.querySelectorAll('div');
var divCount = divs.length;
Now reuse it:
if (divs[m].style.display == 'none') {
divs[m].style.display = '';
} else {
divs[m].style.display = 'none';
}
increaseHeight();
which can be shortened to:
divs[m].style.display == divs[m].style.display == 'none'? '' : 'none';
increaseHeight();
However the conditional operator is usually slower than the equivalent if..else.
So the function becomes:
function country_change(country,countries) {
var cls = document.querySelectorAll('.country_events');
if(document.readyState == "loading") {
alert('not loading.');
} else {
for (var n=0, nLen=cls.length; n<nLen; n++) {
var elem = cls[n];
var divs = elem.querySelectorAll('div');
var divCount = divs.length;
for (var m = 1; m < divCount; m++) {
if (elem.name == country) {
if (divs[m].style.display == 'none') {
divs[m].style.display = '';
} else {
divs[m].style.display = 'none';
}
increaseHeight()
}
}
}
}
}
Note that querySelectorAll is not supported by IE 7 and lower.
Setting element.style.display to '' (empty string) allows it to return to it's default or computed style and means you don't have to know what that is for each different type of element or what is included in CSS rules.

Stating a universal ID in Javascript function

I have the following function for changing the CSS display on a given DIV
function(){
if(document.getElementById('kitstory').style.display === "none")
{
document.getElementById('kitstory').style.display = "block";
} else{
document.getElementById('kitstory').style.display = "none";
}
}
I'm using onclick in a link to call the function as and when required.
This works fine on my DIV with the ID "kitstory", but is there a way I can use this function for more than one DIV (I'll have several articles on 1 page with different DIV's I want the function to effect)? I've tried leaving the id blank but it doesn't run.
There are a number of ways of achieving this. The easiest ways I can think of are using getElementsByTagName or getElementsByClassName and iterate over the list, or using a framework like jQuery
But of course! For example:
function toggleSingle(elementId) {
var el = document.getElementById(elementId);
if (el.style.display === 'none') {
el.style.display = 'block';
}
else {
el.style.display = 'none';
}
}
This function may be used by another function, which works with collections:
function toggleMultiple(elementIds) {
if (! (elementIds && elementIds[0]) ) {
// elementIds is not an array, should exit (and warn the developer as well perhaps)
return;
}
for (var i = 0, l = elementIds.length; i < l; ++i) {
toggleSingle(elementIds[i]);
}
}
Why did I write this as two functions and not one? Ok, that probably IS an overkill in this case, but in general I often separate the 'individual' item processing into a separate function: it makes my code more readable, and my tests more simple. Still, one can write an omnipotent function with something like this:
function toggleSmart() {
for (var i = 0, l = arguments.length; i < l; ++i) {
var el = document.getElementById(arguments[i]);
if (el.style.display === 'none') {
el.style.display = 'block';
}
else {
el.style.display = 'none';
}
// and another way to do it: more concise, but less readable for some:
// var display = el.style.display;
// display = display === 'none'
// ? 'block'
// : 'none';
}
}
Make a named function and send the id as a parameter to the function:
function Toggle(id){
var element = document.getElementById(id);
if (element.style.display === "none") {
element.style.display = "block";
} else{
element.style.display = "none";
}
}
Usage:
Toggle('kitstory');
You could use one of the following:
document.getElementsByClassName('some-class');
document.getElementsByTagName('div');
For instance:
var elements = document.getElementsByClassName('kitstory');
for(var i = 0, l = elements.length; i < l; i++) {
elements[i].style.display = (elements[i].style.display == 'none') ? 'block' : 'none';
}
If class name or tag name do not immediately help you target the desired elements, you could also change document to something else:
var elements = document.getElementById('my-container').getElementsByTagName('div');
Try this:
function yourAction() {
if(this.style.display === "none") {
...
}
}
var i, len;
for(i = 0, len = document.getElementsByTagName("div"); i < len; i++) {
document.getElementsByTagName("div")[i].onclick = yourAction;
}
try this
function change(id){
if(document.getElementById(id).style.display === "none")
{
document.getElementById(id).style.display = "block";
} else{
document.getElementById(id).style.display = "none";
}
}
then call function with change(divID);

Jquery function implementation

I write this function for getting xpath location:
JQuery (this function get xpath):
function getXPath(node, path) {
path = path || [];
if(node.parentNode) {
path = getXPath(node.parentNode, path);
}
if(node.previousSibling) {
var count = 1;
var sibling = node.previousSibling
do {
if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {count++;}
sibling = sibling.previousSibling;
} while(sibling);
if(count == 1) {count = null;}
} else if(node.nextSibling) {
var sibling = node.nextSibling;
do {
if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {
var count = 1;
sibling = null;
} else {
var count = null;
sibling = sibling.previousSibling;
}
} while(sibling);
}
if(node.nodeType == 1) {
path.push(node.nodeName.toLowerCase() + (node.id ? "[#id='"+node.id+"']" : count > 0 ? "["+count+"]" : ''));
}
return path;
};
and I have an iframe:
<iframe id="frameID" width="100%" src="http://www.example.com"></iframe>
But Now I have a problem how to integrate my jquery function and where to when i click somewhere in Iframe get xpath result?
You wrote that function? -- That code was written nearly 5 years ago on DZone: http://snippets.dzone.com/posts/show/4349 -- This includes instructions for usage for the function.
You can only do that when the iframe location is in the same domain as the page embedding it.
If so, get the Document from the iframe (document.getElementById('frameId').contentDocument, see MDN) and add a click event listener to it. The event you're receiving will contain a reference to the target node.

Categories

Resources