I have a problem with my JavaScript function, in the "for" part it doesn't recognize the HTML elements when I use i to refer to the list position, but when I use [0] or [1], for example, it does recognize it. So there must be a problem with the loop part but I can't figure out what is it, here is the code:
(function () {
"use strict";
window.animacion_click_menu = function (id) {
var i;
var menu = document.getElementById('menu').getElementsByTagName('LI');
var bloqueActual = document.getElementById(id);
for (i = 0; i <= menu.length; i++) { //recorre los LI devolviendolos a su posicion original
menu[i].style.marginLeft = -40;
menu[i].style.opacity = 1;
}
bloqueActual.style.marginLeft = 200;
bloqueActual.style.opacity = 0;
};
})();
and here's my html:
<head>
<meta charset="UTF-8">
<title>Mario Luque Marchena</title>
<meta charset="utf-8">
<link rel="stylesheet" href="css/estilos.css">
</head>
<body>
<center>
<h1 class="titulo">Bienvenid#</h1>
</center>
<div id="main-screen">
<ul id="menu">
<center>
<li id="sobremi" onclick="window.animacion_click_menu('sobremi');">Sobre mi</li>
<li id="portafolios" onclick="animacion_click_menu('portafolios');">Portafolios</li>
<li id="animacion" onclick="animacion_click_menu('animacion');">Animacion</li>
<li id="back-end" style="border-bottom-style: dashed;" onclick="animacion_click_menu('back-end');">Back-End</li>
</center>
</ul>
</div>
<script type="text/javascript" src="js/animaciones.js"></script>
</body>
if you have any suggestions to make the code better, are welcome too, i'm learning to code. thank you!, and sorry for the bad english in case it was
Your error is really on the for loop.
Take a look on:
for (i = 0; i <= menu.length; i++) {
it should be:
for (i = 0; i <= menu.length-1; i++) {
Otherwise, it will try to iterate from 0 to 5 while your menu array has only 4 items.
The result is that in the last iteration, when you try to access the element menu with the inexistent index (menu[5]) you get the error:
Uncaught TypeError: Cannot read property 'style' of undefined
Other possibility to overcome this is to change <= to < and work with the loop as:
for (i = 0; i < menu.length; i++) {
use window.onload() or
$('document').ready(function(){
//Put your code here
})
I think your code is getting executed before DOM creation.
Related
Good evening guys. I am learning javascript. After learning some basics, I decided to make TODO LIST and got the todo list code from the internet. But I have a question on my mind. My code:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<div>
<h2>ToDo</h2>
<input type="text" id="inputum" />
<button onclick="fonksiyonum()">Ekle</button>
</div>
<ul id="yeniUl"></ul>
<script>
function fonksiyonum() {
var liEkle = document.createElement('li');
var inputtaYazanlar = document.getElementById('inputum').value;
var textim = document.createTextNode(inputtaYazanlar);
liEkle.appendChild(textim);
document.getElementById('yeniUl').appendChild(liEkle);
document.getElementById('inputum').value = '';
var button = document.createElement('BUTTON');
var hiks = document.createTextNode('\u00D7');
button.appendChild(hiks);
liEkle.appendChild(button);
button.className = 'close';
var close = document.getElementsByClassName('close');
for (i = 0; i < close.length; i++) {
close[i].onclick = function () {
var div = this.parentElement;
div.style.display = 'none';
};
}
}
</script>
</body>
</html>
As I understand it, for (i = 0; i < close.length; i++) is used to get the button.close[i].onclick = function () shows the function that will run when the button is pressed. But why close[i] what does the letter 'i' mean here? After that there is var div = this.parentElement. I don't understand this.parentElement. I would be happy if someone could explain the concept of parent element to me.
close.length is actually List/Array of all the Buttons. You can tell by the Elements in getElementsByClassName.
So you are looping through all the Close Buttons and adding an onclick() method to each.
list = [btn0, btn1]
list.length // length = 2
// for i=0; i<list.length; i++
// 1st pass, i == 0:
list[0] // btn0
// 2st pass, i == 1:
list[1] // btn1
// 3rd pass, i == 2
// i is not less than length, 2, end.
For the parentElement I always look at the tabs, Just go 1 left tab over and up; that's the parent! That's why formatting is important, to make this easy!
<div> <!-- <- Parent -->
<h2>ToDo</h2>
<input type="text" id="inputum" />
<button onclick="fonksiyonum()">Ekle</button> <!-- <- Button --->
</div>
I'm trying to check paragraphs to see if it contains a certain word (Apple), and if so, add that to a list.
<p>Orange</p>
<p>Grape</p>
<p>Apple</p>
<ul id=ulist>
</ul>
<script>
var i;
var x = document.getElementsByTagName("P");
for (i = 0; i < x.length; i++) {
document.getElementById("ulist").innerHTML = "<li>" + x[i].innerHTML + "<li>";
}
</script>
I'm unsure how to check for words, but first I'm trying to go through each paragraph and add each word to the list, but it's only adding Apple. I don't understand why even though I set i=0.
Expected output:
- Apple
New to Javascript so I'd appreciate if the solution uses simple code even if it's long.
Try this code:
var paragraphs = [...document.getElementsByTagName("P")];
var list = document.getElementById("ulist")
paragraphs.forEach(el => {
if (el.innerText.toLowerCase().includes("apple")) {
var li = document.createElement("LI")
li.innerText = el.innerText
list.appendChild(li)
}
})
<p>Orange</p>
<p>Grape</p>
<p>Apple</p>
<p>Not Apple</p>
<ul id="ulist"></ul>
It checks if each <p> has the word apple in it, and if so, it dynamically adds it to the list.
If you want to check if it's Apple just by itself, use ==, and if you want to make the search case-sensitive, remove the .toLowerCase() and change the word apple to Apple.
If you want to add every element, simply remove the if condition.
You should use += instead of =. Also forgot to close the list element (should be </li>). Also, for better performance you can first generate string for all the items the update the DOM only once.
You can check the text of the current element in each iteration based on which you can create items.
Demo:
<p>Orange</p>
<p>Grape</p>
<p>Apple</p>
<ul id=ulist>
</ul>
<script>
var i;
var x = document.getElementsByTagName("p");
var elString = "";
for (i = 0; i < x.length; i++) {
if(x[i].textContent.toLowerCase().includes('apple')){ // check if text contains the word
elString += "<li>"+x[i].textContent+"</li>";
}
}
document.getElementById("ulist").innerHTML = elString;
</script>
Using Document.querySelectorAll(), NodeList.prototype.forEach(),Document.getElementById(),
,Document.createElement() and RegExp.prototype.test().
const fruits = document.querySelectorAll("p"),
lists = document.getElementById("ulist");
fruits.forEach((fruit) => {
if (/apple/i.test(fruit.textContent)) {
const list = document.createElement("li");
list.textContent = fruit.textContent;
lists.append(list);
}
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Fruits</title>
</head>
<body>
<p>Orange</p>
<p>Grape</p>
<p>Apple</p>
<ul id="ulist"></ul>
</body>
</html>
I'm a rookie. I try this example to test code that when click on each p ,the section text will replace by "yeah"
When click first p, first section is replaced
When click 2nd p, 2nd section is replaced ...
Code like this
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>p{text-align: center;}</style>
</head>
<body>
<p class="test">hello</p>
<p class="test">how</p>
<p class="test">are</p>
<p class="test">you</p>
<p class="test">today</p>
<p class="test">sir</p>
<section>good</section>
<section>great</section>
<section>excellent</section>
<section>well</section>
<section>best</section>
<section>better</section>
<script>
var x = document.getElementsByClassName("test")
var y = document.getElementsByTagName("section")
for(i=0;i<y.length;i++){
y[i].setAttribute("class","linktest")
}
var z = document.getElementsByClassName("linktest")
function place(a){
z[a-1].innerHTML="yeah"
}
for(i=0;i<x.length;i++){
x[i].addEventListener("click", function(){
place(i+1)})
}
</script>
</body>
</html>
how i wrong with for loop code of addEventListener
I try to write long code like this
x[0].addEventListener("click",function(){place(1)})
x[1].addEventListener("click",function(){place(2)})
x[2].addEventListener("click",function(){place(3)})
x[3].addEventListener("click",function(){place(4)})
x[4].addEventListener("click",function(){place(5)})
x[5].addEventListener("click",function(){place(6)})
And it worked but i want to short code because real work has mor than 100 items
Help plz
#Chaska's answer works fine but since you mentioned that there will be over 100 items it's important to avoid adding event listeners for each element for better performance. Instead wrap all the p tags in a div and add a single event listener to the div tag. Here is a slightly modified code.
var x = document.getElementsByClassName("test");
var y = document.getElementsByTagName("section");
for (i = 0; i < y.length; i++) {
//assuming equal no. of elements in x and y
y[i].setAttribute("class", "linktest");
x[i].setAttribute('data-index', i);
}
var z = document.getElementsByClassName("linktest");
function place(a) {
z[a].innerHTML = "yeah";
}
var testGroup = document.getElementById('test-group');
testGroup.addEventListener('click', function() {
place(parseInt(event.target.getAttribute('data-index')));
})
<div id="test-group">
<p class="test">hello</p>
<p class="test">how</p>
<p class="test">are</p>
<p class="test">you</p>
<p class="test">today</p>
<p class="test">sir</p>
</div>
<section>good</section>
<section>great</section>
<section>excellent</section>
<section>well</section>
<section>best</section>
<section>better</section>
You are calling place() function out of the for loop. So the value of i will always be 6.
Try this way to assign the index to each element first and pass it to the place() function.
var x = document.getElementsByClassName("test");
var y = document.getElementsByTagName("section");
for (i = 0; i < y.length; i++) {
y[i].setAttribute("class", "linktest");
}
var z = document.getElementsByClassName("linktest");
function place(a) {
z[a].innerHTML = "yeah";
}
for (i = 0; i < x.length; i++) {
x[i].setAttribute('data-index', i);
x[i].addEventListener("click", function() {
place(parseInt(this.getAttribute('data-index')));
})
}
<p class="test">hello</p>
<p class="test">how</p>
<p class="test">are</p>
<p class="test">you</p>
<p class="test">today</p>
<p class="test">sir</p>
<section>good</section>
<section>great</section>
<section>excellent</section>
<section>well</section>
<section>best</section>
<section>better</section>
The script below is meant to find all html comments in the page (there are 4) and return them as one string. I ran the script below and received a "Too Much Recursion" error.
Have I created an infinite loop or did I do something else?
function findComment()
{
var olElement = document.getElementById("everything");//this is the id for my body element
var comments = new Array();
if (olElement.nodeType == 8)
{
comments[comments.length] = olElement;
} else if(olElement.childNodes.length>0)
{
for (var i = 0; i<olElement.childNodes.length; i++)
{
comments = comments.concat(findComment(olElement.childNodes[i]));
}
}
alert(comments);
}
//window.onload = countListItems;
//window.onload = countTagItems;
//window.onload = getElements;
window.onload = findComment;
This is a rough cut version of how you could do it with a recursion. It is not really elegant but will do the work:
function fico(el){
if (el.nodeType==8) return [el.textContent.trim()]
else return [...el.childNodes].map(fico);
}
console.log(fico(document.querySelector("#everything")).toString().replace(/,+/g,", "));
<body id="everything">
<div>something <!-- comment1 -->
<div>with something inside
<!-- comment2 -->
<div>and something further<div>
<span>inside
<!-- comment3 --></span>
it
</div>
more regular text
<!-- comment4 --> and enough.
</div></body>
Depending on the html input the function will return an array of subarrays with further levels of subarrays. To flatten it I used the Array-method toString() and then replace() with a regular expression to throw out the multiple commas in the result. There is still a superfluous one at the beginning ;-)
And here is an alternative version that uses a global comments array like you used in your code:
var comments=[];
function fico(el){
if (el.nodeType==8) comments.push(el.textContent.trim());
else [...el.childNodes].forEach(fico);
}
fico(document.querySelector("#everything")); // collect comments ...
console.log(comments.join(', ')); // ... and display them
<body id="everything">
<div>something <!-- comment1 -->
<div>with something inside
<!-- comment2 -->
<div>and something further<div>
<span>inside
<!-- comment3 --></span>
it
</div>
more regular text
<!-- comment4 --> and enough.
</div></body>
Move the olElements variable outside the function and pass in the element you want to search. The recursion you have is always starting with 'everything';
var comments = new Array();
function findComment(element)
{
if (element.nodeType == 8)
{
comments[comments.length] = element;
} else if(element.childNodes.length>0)
{
for (var i = 0; i<element.childNodes.length; i++)
{
comments = comments.concat(findComment(element.childNodes[i]));
}
}
return comments;
}
var olElement = document.getElementById("everything");//this is the id for my body element
alert(findComment(olElement));
Update: I tried both methods above and received error that either "element" or "el" is null. So...progress. I've pulled together my full code and html and posted below:
<!DOCTYPE html>
<html>
<head>
<title>A Simple Page</title>
<script>
var comments = new Array();
function findComment(element)
{
if (element.nodeType == 8)
{
comments[comments.length] = element;
} else if(element.childNodes.length>0)
{
for (var i = 0; i<element.childNodes.length; i++)
{
comments = comments.concat(findComment(element.childNodes[i]));
}
}
return comments;
}
//window.onload = countListItems;
//window.onload = countTagItems;
//window.onload = getElements;
var olElement = document.getElementById("everything");//this is the id for my body element
window.onload = alert(findComment(olElement));
</script>
</head>
<body>
<div id="everything">
<h1>Things to Do</h1><!--this is a title-->
<ol id="toDoList"><!--this is a list-->
<li>Mow the lawn</li><!--this is a list item-->
<li>Clean the windows</li>
<li>Answer your email</li>
</ol>
<p id="toDoNotes">Make sure all these things are done so you can get some rest.</p>
</div>
</body>
</html>
I am trying to figure out a way to count words that are placed in multiple paragraph blocks in javascript. Right now I have a button that is connected to a function and that function is linked to an ID in the paragraph. Here is my code
function processText(elements) {
var count = 0;
for (var i = 0; i < elements.length; i++) {
count += elements[i].textContent.split(/\s/).length;
}
return count;
}
var wordsInParagraphs = processText(document.getElementsByTagName("data"));
<!DOCTYPE html>
<html>
<head>
<meta name="title" content="The Cask of Amontillado--Edgar Allan Poe (1809-1849)">
</head>
<body>
<p><button 1="processText(elements);">Process</button></p>
<p id="data"></p>
</body>
Is this what you're looking for? You just need to call the function on click and grab all the elements you want to count, you have the rest there (I'm using split instead of regex).
function processText() {
var elements = document.querySelectorAll(".data");
var count = 0;
for (var i = 0; i < elements.length; i++) {
count += elements[i].textContent.split(" ").length;
}
console.log(count)
}
<!DOCTYPE html>
<html>
<head>
<meta name="title" content="The Cask of Amontillado--Edgar Allan Poe (1809-1849)">
</head>
<body>
<p><button onclick="processText();">Process</button></p>
<p class="data">text in paragraph one</p>
<p class="data">text in paragraph two</p>
</body>
The markup has some problems, for example, 1="processText(elements);" probably you meant onClick="processText(elements);", however, you're passing a param called elements. Further, you have a tag with id="data" and you're trying to look for tag name those elements.
A better approach is using the function addEventListener for a better logic and you should mark those paragraphs using a class name class="data". Finally, for splitting by spaces use this regex /\s+/
function processText(elements) {
var count = 0;
for (var i = 0; i < elements.length; i++) {
count += elements[i].textContent.split(/\s+/).length;
}
return count;
}
document.getElementById('myButton').addEventListener('click', function() {
var wordsInParagraphs = processText(document.getElementsByClassName("data"));
document.getElementById('total').textContent = wordsInParagraphs;
});
<p><button id='myButton'>Process</button></p>
<p class="data">Ele from Stack</p>
<p class="data">Ele from Venezuela</p>
<p id='total'></p>