How do you properly write a JavaScript "for" loop? - javascript

The GitHub Repository that I have linked will not be used for any other questions (it will not be changed except to help THIS question.... IT WILL ONLY BE USED FOR THIS QUESTION
Note: I have done my research and I believe that my code should work
Ok, so if you require the rest of my code to make a good judgement on this, feel free to go to: My Github Repository which is only going to be used for this 1 question. In the GitHub Repository, there is also a CSS file, but there is no problem with it, just included it so that you can see ALL the code.
Yes, I know that many people on this website do not like it when people include "GitHub" links; however, it is easier for me to explain if I do not have all the code sitting here making a mess (it is easier if I can narrow down what code is giving me an error
Ok, so this "for" loop:
var dropdown = document.getElementsByClassName("dropdown-btn");
var i;
for (i = 0; i < dropdown.length; i++) { //this is not getting called for some reason
dropdown[i].addEventListener("click", something());
alert("Please work");
}
is not actually running. When I put an "alert" above the for loop like:
var dropdown = document.getElementsByClassName("dropdown-btn");
var i;
alert("This works");
for (i = 0; i < dropdown.length; i++) { //this is not getting called for some reason
dropdown[i].addEventListener("click", something());
alert("This does not work");
}
The alert is called as soon as the method that the "for" loop is in, is called. I guess to explain my question, is there anything wrong with my "for" loop? (This works in a different .html file, so I am not sure why it is not working in my current workspace.)
UPDATE (February 18, 2019):
Ok so I found what is causing the error.
For the person that commented and suggested that I use "console.log(dropdown.length);", this brought up an unexpected error:
function something(){
this.classList.toggle("active");
var dropdownContent = this.nextElementSibling;
if (dropdownContent.style.display === "block") {
dropdownContent.style.display = "none";
} else {
dropdownContent.style.display = "block";
}
}
As I originally said, this works in another file, but for some reason, it says that in "this.classList.toggle("active);", "toggle" is undefined. Is this supposed to be defined in this file, or is it like i think and a default "function"? Through all of my research, and through all the knowledge I have of the language of JavaScript, I am confident that it is a default "function", and unless someone can prove me wrong, I don't understand why this is not working.

The problem is that you are passing a function in as a parameter without wrapping it in a calling function.
There are two ways you would make this work.
Remove the () from the something function and call the alert inside of something
for (i = 0; i < dropdown.length; i++) { //this is not getting called for some reason
dropdown[i].addEventListener("click", something)
}
something () {
alert('this is working')
...
}
Put a single function call in your event handler and place both function calls inside
for (i = 0; i < dropdown.length; i++) { //this is not getting called for some reason
dropdown[i].addEventListener("click", function () {
something();
alert("This works");
})
}
Here is an example:

You are calling something instead of just passing the callable function when trying to add the eventListener. Remove the parentheses.
var dropdown = document.getElementsByClassName("dropdown-btn");
var i;
for (i = 0; i < dropdown.length; i++) { //this is not getting called for some reason
dropdown[i].addEventListener("click", something);
}
function something() {
this.classList.toggle("active");
var dropdownContent = this.nextElementSibling;
if (dropdownContent.style.display === "block") {
dropdownContent.style.display = "none";
} else {
dropdownContent.style.display = "block";
}
}
<a class="dropdown-btn" href="#">Button 1</a>
<div style="display:none;">Dropdown Content Here</div>
<br />
<a class="dropdown-btn" href="#">Button 2</a>
<div style="display:none;">Dropdown Content Here</div>

I was watching the repository on GitHub, and I saw that there are a lot of errors in your html part, so that the JS part can not work.
Just about this loop, the current html part is:
<button class="dropdown-btn" onclick="drop()">OUR STORY
<i class="fa-caret-down"></i>
</button>
So that the first time the drop () function creates a new event listener on the button, but does not call it.
If there is a second click on this button then 2 actions are then launched: the first adds again a new event listener click on the button,
the second is the triggering of the event listener created the first time.
There are 2 possible solutions
Solution 1
<button class="dropdown-btn" onclick="something()">OUR STORY
<i class="fa-caret-down"></i>
</button>
Solution 2
<button class="dropdown-btn" >OUR STORY
<i class="fa-caret-down"></i>
</button>
With in the part JS = ( ! remove function drop() declaration ! )
var dropdown = document.getElementsByClassName("dropdown-btn");
for (let i = 0, iMax=dropdown.length ; i < iMax ; i++) {
dropdown[i].addEventListener("click", something);
}
for helping understand why your code is wrong, please try this sample code
var Compteur = 0;
function something(){
console.log('call on something()', ++Compteur);
}
function drop(){
var dropdown = document.getElementsByClassName("dropdown-btn");
for (let i = 0; i < dropdown.length; i++) {
dropdown[i].addEventListener("click", something);
console.log( 'adding event Listener on one dropdown-btn ', ++Compteur);
}
}
<button class="dropdown-btn" onclick="drop()">OUR STORY </button>

Here is your problem.
Ask yourself why are you getting this.classList as undefined?
If you looked further you would find that this is the window object which has no classList . Now ask yourself why the window object?
It is because something() is an event listener that should be called in response to an event. In the line dropdown[i].addEventListener("click", something()); , you aren't assigning something() as an event handler, you are calling the method, without an event, so there is no this.classList as this is the window object.
As other answers have mentioned you need to change this to:
dropdown[i].addEventListener("click", something);
Which will assign the event something() to the click event, without calling it.
Complete Example
function something(){
console.log(this); //For Debug prurposes
this.classList.toggle("active");
var dropdownContent = this.nextElementSibling;
if (dropdownContent.style.display === "block") {
dropdownContent.style.display = "none";
} else {
dropdownContent.style.display = "block";
}
}
/* Loop through all dropdown buttons to toggle between hiding and showing its dropdown content - This allows the user to have multiple dropdowns without any conflict */
function drop(){
var dropdown = document.getElementsByClassName("dropdown-btn");
var i;
for (i = 0; i < dropdown.length; i++) { //this is not getting called for some reason
dropdown[i].addEventListener("click", something);
console.log("Please wokrk");
}
}
.sidenav a, .dropdown-btn {
padding: 6px 8px 6px 16px;
text-decoration: none;
font-size: 20px;
color: #818181;
display: block;
border: none;
background: none;
width: 100%;
text-align: left;
cursor: pointer;
outline: none;
}
.active {
background-color: green;
color: white;
}
.dropdown-container {
display: none;
background-color: #262626;
padding-left: 8px;
}
<button class="dropdown-btn" onclick="drop()">OUR STORY
<i class="fa-caret-down"></i>
</button>
<div class="dropdown-container">
ABOUT US
COMMUNITY
HUMANITARIAN ACTION
</div>
On a side note, it is quite unusual to use inline events like onClick=drop() to then bind other event listeners to the same element. It is actually best practice to avoid inline javascript all together.
In the future, please include all code relevant to the question, in the question itself. As I mentioned in a comment it makes it easier for us to help you.

Related

JavaScript For Loop not returning anything (not running)?

Ok so, I've got this for loop in a script tag on my EJS page. The current code looks like this:
<script async>
var removeCartItemButtons = document.getElementsByClassName('btn-danger')
console.log(removeCartItemButtons)
var i;
for (i = 0; i < removeCartItemButtons.length; i++){
console.log('elem')
var button = removeCartItemButtons[i]
button.addEventListener('click', function() {
console.log('clicked')
})
}
</script>
The console.log for the removeCartItemButtons works but the console.log('elem') doesn't run. There are no errors in my cmd nor on the developer tools. I've looked online at different forums to find people doing similar things to me and their's work fine.
I've tried multiple things, and an extra notice is that this is inside the html file and not external.
The end goal of this for loop is to loop through every element with class 'btn-danger' and log when they are clicked.
Any help would be amazing!
try running document.getElementsByClassName('btn-danger') in the console.
Additional tip: there is a for of loop in js
check this: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Statements/for...of
basically:
for(let button of removeCartItemButtons) {
button.addListener
}
I tried this code in Codepen and it worked. I rewrite your code but technically, just used a different syntax. "async" in your script tag can cause the problem, I can't be sure.
var buttons = document.querySelectorAll('.btn-danger');
[...buttons].forEach(button => {
console.log('elem')
button.addEventListener('click', function() {
console.log('clicked')
})
})
There must be an issue with your HTML code. Run the snippet below, it's working fine
var removeCartItemButtons = document.getElementsByClassName('btn-danger')
console.log(removeCartItemButtons)
var i;
for (i = 0; i < removeCartItemButtons.length; i++) {
console.log('elem')
var button = removeCartItemButtons[i]
button.addEventListener('click', function() {
console.log('clicked');
alert('clicked');
})
}
<button class='btn-danger'>Button</button>
<br/>
<button class='btn-danger'>Button</button>
<br/>
<button class='btn-danger'>Button</button>
<br/>
<button class='btn-danger'>Button</button>
removeCartItemButtons.length is 0 because when the snippet ran the DOM was not fully loaded.
Remove async and put your script at the end of your html.
Another option is to use an EventListener like onload to make sure your buttons exist before changing them.

Mouse over the <a> tag

There is a prompt in the lower left corner When I put the mouse over the tag,
and can u tell me how to forbid this phenomenon.enter image description here
As commented, the behaviour that you wish to stop is a browser's feature.
To avoid this, you will have to simulate anchor's behaviour on your own but as you said you have many anchors and you cannot manually convert them to buttons, you can try following code:
function maskAnchors() {
var els = document.querySelectorAll('a[href]');
console.log("Anchors Found: ", els.length)
for (var i = 0; i < els.length; i++) {
els[i].setAttribute("data-url", els[i].getAttribute('href'));
els[i].removeAttribute("href");
els[i].addEventListener("click", handleClick)
}
}
function handleClick() {
var url = this.getAttribute('data-url');
window.open(url)
}
document.getElementById('btnAdd').addEventListener("click", function() {
var container = document.querySelector('.content');
var link = document.createElement('a')
link.href = "www.google.com";
link.textContent = "This is a newly added link";
container.append(link)
})
document.getElementById('btnMask').addEventListener("click", maskAnchors)
window.addEventListener('load', maskAnchors)
.maskedAnchor {
color: -webkit-link;
text-decoration: underline;
cursor: auto;
}
<div class="content">
Google
Facebook
StackOverflow
YouTube
Example
<a>blabla</a>
</div>
<button id="btnAdd">Add Anchor</button>
<button id="btnMask">Run masking</button>
Note:
Removing href will change styling. You will also have to do that manually.
This will not handle any anchors added dynamically after execution of this function. You will have to call this function again. I have optimised function to only fetch anchors that has href
Hope it helps!

Event target should be anchor but is image instead

I am working on a dialog script in Vanilla JS. I ran into a problem with the click event on the video image. Even tough the image is surrounded with an anchor tag it shows the image as the event.target on the "trigger-dialog-open" event.
Here is the HMTL:
<a class="trigger-dialog--open thumbnail" data-dialog-id="dialog-video" href="javascript:;">
<figure>
<img src="http://i.ytimg.com/vi/id/sddefault.jpg" alt="" />
</figure>
</a>
And this is the event in JS:
var openTriggers = document.getElementsByClassName('trigger-dialog--open');
for (var i = 0; i < openTriggers.length; i++) {
openTriggers[i].addEventListener("click", function (event) {
this.openDialog(event.target.getAttribute('data-dialog-id'));
}.bind(this), false);
}
The event handler wants to know the dialog-id from the anchors data attribute. It can't be found because it thinks the image is the event.target, not the actual anchor. How can I correct this? Thanks!
Use event.currentTarget. The event.target is supposed to be the img element since that is what the user has clicked on. The click then bubbles up through the image's containers. event.currentTarget gives you the element that the click handler was actually bound to.
(Or if you didn't bind this to some other object you could use this within the click handler and it should also be the current target.)
I have a few questions is the var openTriggers supposed to be a part of a module hash? Because if it's global then you don't use a this, you only add a this, if it's referencing a variable that the function is also contained in. For example:
var aThing = {
openTriggers: document.getElementsByClassName('trigger-dialog--open'),
openModal: null,
openDialog: function(clickedThingAttr){
if(this.openModal !== null){
this.openModal.style.display = 'none';
}else{
this.openModal = document.getElementById(clickedThingAttr);
}
this.openModal = document.getElementById(clickedThingAttr);
this.openModal.style.display = 'block';
},
setEventListenersNStuff: function(){
for (var i = 0, n = this.openTriggers.length;i < n; i++) {
this.openTriggers[i].addEventListener("click", function (event) {
this.openDialog(event.target.getAttribute('data-dialog-id'));
});
};
}
};//end of aThing hash object
aThing.setEventListenersNStuff();
There are a few issues here:
1. why are you using .bind I think that is a jQuery thing, you want to pass a string to another function when an object is clicked, there no need for binding at all.
2. Also make sure that if you want to do something like open a modal, there is no need to call another method unless it's kinda complex.
3. What about other potential dialogs, it seems that when a .trigger-dialog--open is clicked you're just showing that one one modal with the embedded id, but what about others? Make sure all modals are closed before you open a new one, unless you want to have like 10 modals are open.
A thing to note: I added the line var i = 0, n = openTriggers.length;i < n; i++, now in this case it's silly optimization, and I heard for modern browsers this doesn't apply, but to explain why I added it, is because i < openTriggers.length would count and integrate the array N times. (This may be an outdated optmiziation).
If you meant global
Below I added a different set of code, just in case you meant that var openTriggers is global, kinda like you wrote above. Also I used querySelectorAll for this which is like jQuery's $('.thing') selector.
anyhoo, I also added
var openTriggers = document.querySelectorAll('.trigger-dialog--open');
var n = openTriggers.length;
function openDialog(ddId){
for (var i = 0;i < n; i++) {
openTriggers[i].style.display = 'none';
};
document.getElementById(ddId).style.display = 'block';
};
for (var i = 0;i < n; i++) {
openTriggers[i].addEventListener("click", function (event) {
openDialog(event.target.getAttribute('data-dialog-id'));
});
}
}
So for the question of hiding already open modals I would suggest you could either cache the open Dialog within a module, or you could toggle a class, which would be less efficient since it would require an extra DOM search. Additionally you could add a if this.openModal.id === clickedThingAttr to hide if open, that way you got a toggle feature.
Anyways I suggest you read up on this stuff, if you want to use plain JS but would like the features of jQuery: http://blog.romanliutikov.com/post/63383858003/how-to-forget-about-jquery-and-start-using-native
Thank you for your time.
You can use a closure
var openTriggers = document.getElementsByClassName('trigger-dialog--open');
for (var i = 0; i < this.openTriggers.length; i++) {
(function(element) {
element.addEventListener("click", function (event) {
element.openDialog(event.target.getAttribute('data-dialog-id'));
}, false)
})(openTriggers[i]);
}

Javascript - prompt keeps coming back after Ok is pressed

I have the following JavaScript configuration. It works well when it loads and keeps working well the first time I click on the button with id buttonKernel. This should run the function onPageLoad, which resets a grid presented on screen and reload the event handlers. The second time I click the button thought, after clicking OK on the prompt, it keeps prompting again. If I insist, it will eventually work.
I'm new to JavaScript, so that I don't know if this self calling function can be at the root of the problem.
$(document).ready(function(){
onPageLoad(16);
});
function onPageLoad(size){
genDiv(size);
$('.gridSquare').hover(function(){
$(this).css('background-color', 'red');
});
$('#buttonKernel').click(function(){
$(".gridSquare").css('background-color', 'yellow');
var input = prompt ("Enter a size for the grid", "16");
size = checkInp(input);
var list = document.getElementsByClassName("row");
for (var i = list.length - 1; 0 <= i; i--)
{
if (list[i] && list[i].parentElement)
list[i].parentElement.removeChild(list[i]);
};
onPageLoad(size);
});
}
every time you click, you add another click event handler.
Seems to be working fine for me?
http://jsfiddle.net/8atqub5k/1/
There are two unknown functions... genDiv(size) and checkInp(input) I have no clue what those do. Note: if there is a javascript error in either of those functions, generally javascript won't function at all on the rest of the site.
Are you aware of the Chrome browser Developer Tools? Way awesome tool for troubleshooting javascript (or html and CSS ) errors...
<style>
.gridSquare{
width: 100px;
height: 100px;
background-color: blue;
}
</style>
<button id="buttonKernel">Click here</button>
<div class="gridSquare">Test</div>
<script>
$(document).ready(function(){
onPageLoad(16);
});
function onPageLoad(size){
//genDiv(size);
$('.gridSquare').hover(function(){
$(this).css('background-color', 'red');
});
$('#buttonKernel').click(function(){
$(".gridSquare").css('background-color', 'yellow');
var input = prompt ("Enter a size for the grid", "16");
size = checkInp(input); // what is this function?
var list = document.getElementsByClassName("row");
for (var i = list.length - 1; 0 <= i; i--)
{
if (list[i] && list[i].parentElement)
list[i].parentElement.removeChild(list[i]);
};
// onPageLoad(size); // don't need this!
});
}
</script>

Elements By ClassName Failing

I am creating sliding menus in JavaScript, and the following is my init() function:
function init() {
var menus = new Array();
var allElems = document.getElementsByTagName("*");
for (var i = 0; i < allElems.length; i++) {
alert(allElems[i]);
if (allElems[i].className == "navG") {
alert(allElems[i]);
menus.push(allElems[i]);
}
}
/* assign the openMenu function to the onclick event for each
Menus item */
for (var i = 0; i < menus.length; i++) {
alert(menus[i]);
menus[i].onclick=openMenu;
}
document.getElementById("logo").onclick = closeMenu;
document.getElementById("linkList").onclick = closeMenu;
document.getElementById("main").onclick = closeMenu;
}
The problem seems to be in the first for loop. This is definitely the correct class name..just for reference, this is the type of HTML that I am referring to:
<div class="navG" id="gallery1" style="position: absolute; top: 180px; left: -150px; " >
Is there an obvious, or not so obvious reason, that this is not adding the elements to menus?
You got a bug here
document.getElementById("logo").onclick = closeMenu();
document.getElementById("linkList").onclick = closeMenu();
document.getElementById("main").onclick = closeMenu();
You calling closeMenu, not assigning it.
Needs to be
document.getElementById("logo").onclick = closeMenu;
document.getElementById("linkList").onclick = closeMenu;
document.getElementById("main").onclick = closeMenu;
In the page you linked to, in your family.js script, this line:
window.onLoad = init();
says to run the init function immediately and assign its return value to window.onLoad. Because it is running immediately the actual document hasn't been parsed yet so it doesn't find any of your elements. You need to say this:
window.onload = init;
which assigns a reference to the init function to window.onload so that that function will be run later after all of the elements have been parsed.
Also onload should have a lowercase l.
(There are some other problems in your code, e.g., you don't seem to have elements with the ids "linkList" or "main", but I think what I said above is the main problem with the part you are asking about.)
Whoops, you're missing quotes around that "navG" in the first loop.

Categories

Resources