I am following along a Javascript tutorial and I have to add in 100 buttons inside of a div using Javascript.
At first, I tried doing something like this:
const btn = document.createElement('button');
const div = document.querySelector('div');
btn.innerText = 'Hey!';
for (let i = 0; i < 100; i++) {
div.appendChild(btn);
}
And this ended up only making 1 button. However, if I repeat the same code it just appends buttons after the previous one. However, this approach:
for (let i = 0; i < 100; i++) {
const btn = document.createElement('button');
const div = document.querySelector('div');
btn.innerText = 'Hey!';
div.appendChild(btn);
}
does the job. I can't seem to understand why the 2nd one works and the 1st one doesn't.
From my understanding, the first approach seemed better because I wasn't making the same variables over and over again. However that only ends up in me making just one button instead of 100.
First example won't work, cause it is just how method appendChild works. According to MDN. It just moves the node/element from its current position to specified. So your first example is just renewing the position of only one node, cause you've created only one div
Second example creates div every time, so it is a new element, which is stored inside your parent element.
The code that creates element is document.createElement and You call it ones in first example, hence only one element is created.
appendChild does not creates an element, it appends already created element to some parent.
If it is already appended, then does nothing
because in the first case you are trying to append the same DOM object 100 times, but have to create a new one each time
Related
I need to change the ID of a div based on the buttons being clicked. This is what my buttons look like:
var id; // global variable
holding = "holder";
var counter = 1;
function sendData(valueId) {
id = valueId;
if (counter <= 1) {
document.getElementById(holding).setAttribute("id", id);
counter++;
holding = id;
} else {
document.getElementById(holding).setAttribute("id", id);
holding = id;
}
console.log(holding);
console.log(id);
}
<button id="republic" onclick="sendData('republic')">Republic</button>
<button id="ndtv" onclick="sendData('ndtv')">NDTV</button>
<button id="cnnnews18" onclick="sendData('cnnnews18')">CNN</button>
My div to be targeted is just an empty, <div id="holder"></div>. However, when I try to run this I see the div change the first time but it remains on the same ID for every other click. How can I fix this?
The trouble is that when you press one of the buttons, you're creating multiple elements with the same id. The "holder" div will be assigned the id of one of the buttons.
Ids are meant to be unique. When this isn't the case, the behavior of getElementById is undefined, so the browser likely just returns the first element it finds that qualifies. In your example, this will be one of your buttons, rather than the "holder" div.
To get the code to work, make sure the ids of your buttons and the valueId variables in your code are different.
Bladeski's suggestion to use
const holding = document.getElementById('holder');
is great. But you should also make sure the ids in your document stay unique, just to conform to the HTML standard and avoid any other undefined behavior.
Create a reference to the 'holding' div in a constant and the use this for changing the attribute. For example, at the start of the code:
const holding = document.getElementById('holder');
Then, whenever you need to update the element, use:
holding.setAttribute(...);
This also happens to be more efficient as it means that the DOM is not being queried every time the element needs to be accessed since the holding constant provides a direct reference to it.
You need to remove the ids from the buttons.
<button onclick="sendData('republic')">Republic</button>
<button onclick="sendData('ndtv')">NDTV</button>
<button onclick="sendData('cnnnews18')">CNN</button>
The first click only one element with id 'holder' would be available in dom. But in the next we would have two since both div and button would have republic/ndtv/cnnnews18 and getElementById returns the button and this goes on.
But instead of this having the reference holder div at root would be a better approach. The code would be:
var id; // global variable
holding = document.getElementById('holder');
var counter = 1;
function sendData(valueId) {
id = valueId;
if (counter <= 1) {
holding.setAttribute("id", id);
counter++;
} else {
holding.setAttribute("id", id);
}
console.log(id);
}
So I got into JavaScript and tried setting up the following scenario:
I have 2 Buttons on my Site (IDs are buttonWebdev and buttonUXUI), which should trigger an Action when they are hovered upon. If buttonWebdev is hovered upon, it should hide all p', h3's and imgs with the class "classWeb". I wrote this code to do it, but it doesn't work:
HTML:
<h3 class="classWeb">Editierbare Inhalte</h3>
<p class="classWeb">Test</p>
<button class="buttonImg" id="buttonWebdev"><img src="./img/buttonWebdev.png" /></button>
<script type="text/javascript">
var button = document.getElementById('buttonWebdev');
var classWeb = document.getElementsByClassName('classWeb');
button.onmouseover = function() {
classWeb.className = 'webdev';
}
CSS:
.classWeb.webdev {
display: none;
}
First, since there can be more than one element with a given class on a page, getElementsByClassName returns a list of elements instead of a single element. You’ll need to perform your action on every element of that list, with a for…of loop, for example:
for (let element of classWeb) {
element.className = 'webdev';
}
(for…of is relatively new, though, so you might have to use a regular for loop depending on your target browsers.)
After fixing this, you’ll run into another problem. When you assign to className like that, you’re setting the entire list of classes on an object. If the list of classes is 'webdev', it no longer includes 'classWeb'. Modern browsers support an API to add a class without affecting the rest:
for (let element of classWeb) {
element.classList.add('webdev');
}
The way to diagnose these sorts of problems is by opening up your browser’s developer tools, looking for JavaScript errors in the console, and looking at the state of the elements you’re trying to affect in the document tree.
document.getElementsByClassName('classWeb'); this gives collection & to add classes you need to iterate over them & then apply classes.
classWeb[0].className = 'webdev'; would reset class
either use classWeb[i].className += ' webdev'; or classWeb[i].classList.add('webdev');
See below working example
var button = document.getElementById('buttonWebdev');
var classWeb = document.getElementsByClassName('classWeb');
button.onmouseover = function() {
for (var i = 0; i < classWeb.length; i++)
classWeb[i].className += ' webdev';
}
.classWeb.webdev {
display: none;
}
<h3 class="classWeb">Editierbare Inhalte</h3>
<p class="classWeb">Test</p>
<button class="buttonImg" id="buttonWebdev">hover over me</button>
Firstly, the
document.getElementsByClassName('classWeb');
will give you a LIVE list of all the matched elements. That means that when you reassign the class like so:
classWeb[0].className = 'webdev';
the element will be removed from the list, as it no longer corresponds to the original command which was to find all elements with a specific class (which you overrode with 'webdev').
An easier and more friendly api is querySelectorAll which mimics the jQuery selector (which uses css selectors to find elements, thats why there is a # for an id and a . for a class name). The example below shows, how to use it.
var button = document.querySelector('#buttonWebdev');
var classWeb = document.querySelectorAll('.classWeb');
button.onmouseenter = function() {
for (var i = 0; i < classWeb.length; i++) {
classWeb[i].className = 'webdev';
}
}
ps. The querySelectorAll is not a live list, so items will not disappear after you change their class.
ps2. Use onmousenter instead of onmouseover as the onmouseenter is only called when the mouse starts hovering over an element, while onmouseover will be called on every mouse move over the element (even if already hovering).
Good luck!
I have a "projects" object. Each project has a div and another div inside it, which is hidden. I want to create multiple fadeToggle() effects, so that for each project - clicking the first div will make the one inside it show up.
I tried to do this with a simple for loop.
projects.display = function() {
for (var i = 0; i < projects.length; ++i) {
var projectID = "#projects-" + i;
var moreInfoID = "#projects-" + i + "-more";
// assign IDs for current project
$(projectID).click(function() {
$(moreInfoID).fadeToggle();
});
}};
The result of this code is that clicking any of the main div's triggers the hidden div of the LAST project to show up.
How can I make it so that clicking a main div will call fadeToggle() for the designated hidden div?
Do not believe for loop is necessary. You should be able to substitute attribute starts with and attribute ends with selectors, :has() or :not()
$("[id^=projects]:has([id$=more])").click(function() {
$("[id$=more]", this).fadeToggle()
})
/***************************************************** *
* Function: renderTodos
* Builds a list of todo items from an array
* rebuilds list each time new item is added to array
****************************************************** */
function renderTodos(arr) {
// erases all previous content
uList.innerHTML = '';
for ( var i = 0; i < tdArray.length; i++ ) {
// create a link element
var remex = document.createElement('span');
//link element didn't work so try a button
//var remex = document.createElement('input');
//remex.setAttribute('type','button');
remex.setAttribute('class', 'deletex');
remex.innerHTML="X";
// set attribute index and value
remex.setAttribute('index', i);
remex.addEventListener('click',removeTodo);
console.dir(remex);
// create <li> element
var li_element = document.createElement('li');
li_element.style.lineHeight = "20pt";
li_element.appendChild(remex);
li_element.innerHTML += tdArray[i];
// add item to list
uList.appendChild(li_element);
inField.value = '';
}
} // /renderTodos
This function builds a list based on text field inputs. Each time the the "add item" button is clicked, the event calls this function to add the item to the list. Everything works beautifully UNTIL I try to add the eventListener to the "x" that is appended to the li element prior to the list item text. The idea is that the "x" is clickable, and onClick it removes the list item entry from the list. But I have tried 6 ways to Sunday to attach an eventListener to the "x" object and nothing works. I have tried attaching the event listener to a span object, and a button object; I have moved "remex.addEventListener..." all around in the function, after it has been rendered, before it gets rendered, etc.; I have eliminated the CSS; I have tried changing the addEventListener to onClick; I have tried this code on our own Apache server, I have moved it to jsbin.com in hopes that some server setting was getting in my way; and probably a few more things I can't remember in the long list of things I have tried. As you see, I have tried it as a button and as a span, hence the commented code.
In short, no matter what I try, the eventListener will NOT attach to the "x". Any ideas? Do you need to see more of the code?
This line overrides the attached eventlistener:
li_element.innerHTML += tdArray[i];
Setting innerHTML replaces all the original elements within li_element. += is just a shortcut to li_element.innerHTML = li_element.innerHTML + tdArray[i];
If tdArray[i] contains just some text, you can add its content like this:
li_element.appendChild(document.createTextNode(tdArray[i]));
If tdArray[i] contains elements, you could append a wrapper element, and then set the innerHTML of the wrapper.
I want to toggle a div element (expand/collapse) when clicked.
I have many div elements, on click to new element, I want to collapse the previous one and expand the current clicked one.
I tried using static type variable to save the instance of previous div tag and compared with the current selection, but I don't know why is it not working.
Searching about this, I got similar code idea to collapse all div and then expand the current selected only, but I want to just toggle the previous one with new one, not collapse all div and expand the selected (though I would be using it if other way is not possible)
Can it be done using static variables of js?
At its simplest, you can simply do something like this:
var divs = document.getElementsByTagName('div'),
collapseClass = 'collapsed',
current = divs[0];
// Hide all elements except first, add click event hander
for(var i = 0, len = divs.length; i < len; i++){
divs[i].onclick = function(){
if(this !== current){
toggle(this, current);
current = this;
}
};
if(i > 0) toggle(divs[i]);
}
This will store the current element in a variable, then toggle it when another element is clicked. It also uses an if statement to check if the currently clicked element is the one currently visible element, and only toggles if its not.
See a working demo of this here: http://jsfiddle.net/GaxvM/
You can assign a unique ID to each of the elements and use document.getElementById to identify both elements, and then collapse one/expand the other.
If you number them sequentially (like div1, div2, div3, etc) you could do something like:
function colexp(div_id){
div_2_collapse = document.getElementById(div_id);
next_div = div_id.substr(0,3) + parseInt(div_id.substr(3))+1;
div_2_expand = document.getElementById(next_div);
hide(div_2_collapse);
show(div_2_expand);
}