Stating a universal ID in Javascript function - javascript

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);

Related

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

JavaScript - Uncaught TypeError when trying to get info from DOM elements

Very new to javascript and can't figure out what I'm doing wrong. Trying to assign the className of a <div> element to a var, and I get this error.
scripts.js:30 Uncaught TypeError: Cannot read property '0' of undefined
at checkWinner (scripts.js:30)
at HTMLDivElement.buttonClick (scripts.js:25)
When I try to figure out if the property even exists, the console is leading me to believe it does. This seems like conflicting info to me.
winLines[0][0].className
"xButt"​
Any help is appreciated. I'm sure it's something basic. Here's the code just in case.
var turns = 0;
var gameButtons = Object.values(document.querySelectorAll('.gameButton'));
var winLines= [
[gameButtons[0], gameButtons[1], gameButtons[2]]
/* other arrays go hear */
];
for (let i = 0; i < gameButtons.length; i++) {
gameButtons[i].textContent = null;
gameButtons[i].addEventListener('click', buttonClick);
}
function buttonClick(e) {
console.log(e.target.id + ": You clicked me!");
if (turns % 2 == 0) {
e.target.className = 'xButt';
e.target.textContent = 'X';
e.target.style.backgroundColor = 'green';
} else {
e.target.className = 'oButt';
e.target.textContent = 'O';
e.target.style.backgroundColor = 'blue';
}
turns++;
checkWinner();
}
function checkWinner() {
for (let i = 0; i <= winLines.length; i++) {
let markOne = winLines[i][0].className;
let markTwo = winLines[i][1].className;
let markThree = winLines[i][2].className;
if (markOne === markTwo && markOne === markThree) {
alert("Awww sh********t!");
}
}
}
Your loop has more iterations than your array has elements.
Change the loop like so:
for (let i = 0; i < winLines.length; i++)
The undefined error comes because you're trying winLines[1][0] which doesn't exist because winLines only has one element (at index 0)

How to pass HTML style classes into a Javascript function argument

Is there a way that I can pass all style classes into a function?
The style classes represent a table row and i'm trying to hide all rows with the exception of the one that is being clicked. (code below, I apologise if it isn't in the code tags)
function toggle_visibility(id, param2, param3)
{
var getClasses = document.getElementsByClassName(id);
var getClasses2 = document.getElementsByClassName(param2);
var getClasses3 = document.getElementsByClassName(param3);
for (var i = 0; i < getClasses.length; i++)
{
if(getClasses[i].style.display == 'none')
{
getClasses[i].style.display = '';
}
}
for (var i = 0; i < getClasses2.length; i++)
{
if(getClasses2[i].style.display == '')
{
getClasses2[i].style.display = 'none';
}
}
for (var i = 0; i < getClasses3.length; i++)
{
if(getClasses3[i].style.display == '')
{
getClasses3[i].style.display = 'none';
}
}
}
The code works but isn't scalable currently so I would like to pass in every class and then search the classes with an 'if' statement. is this possible?
Thanks
You can use the arguments variable :
function toggle_visibility()
{
var first = true;
var i,j, getClasses;
for (i = 0; i < arguments.length; i++) {
getClasses = document.getElementsByClassName(arguments[i]);
for (j = 0; j < getClasses.length; j++)
{
if(getClasses[j].style.display == (first ? 'none' : ''))
{
getClasses[j].style.display = (first ? '' : 'none');
}
}
first = false;
}
}
With this you can call your function with any number of arguments, the first will be shown and all the other hidden
Create an array of all the classes available in the DOM and pass it as an argument to the function.
var All= document.getElementsByTagName("*");
var allClasses=[];
for(var i=0; i< All.length;i++){
if(All[i].className){
allClasses.push(All[i].className);
}
}
function toggle_visibility(allClasses)
{
//do whatever you want!
}

Javascript loop of if statement

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;
}
}
}

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.

Categories

Resources