I have a dynamically added list that gets its values from an object array. However, when looking at the inspector, it shows the list elements after the closing ul tag, but the styles are still applying as though the list elements are inside the ul element. Why is this happening?
HTML
<div id="portfolio_page" class="page">
<aside>
</aside>
</div>
JS
var Portfolio = {
Completed: ["SPARTAN", "Custom Select Menu", "Popup"],
Ongoing: ["Hello"],
Future_Projects: [],
Clients: []
}
for(var key in Portfolio){
var item = key.replace("_", " ");
$("#portfolio_page aside").append("<h1>" +item +"</h1><ul>");
for(var i = 0; i < Portfolio[key].length; i++){
$("#portfolio_page aside").append("<li>" + Portfolio[key][i]+"</li>");
console.log(key[i]);
}
$("#portfolio_page aside").append("</ul>");
}
FIDDLE
You need to create the unordered list first add items to it then append it to the main container ...
$("#portfolio_page aside").append("<h1>" +item +"</h1>");
var $ul = $("<ul></ul>");
for(var i = 0; i < Portfolio[key].length; i++){
$ul.append("<li>" + Portfolio[key][i]+"</li>");
console.log(key[i]);
}
$("#portfolio_page aside").append($ul);
You are getting the elements outside the unordered list because when you were opening it outside the loop [With the closing tag]. The automatically created the ul element [because of the browser attempting to correct the markup closures].
When the list elements were appended it was not being appended to the list [because it was already closed by the browser], but to the main container because it was properly referenced since it was existing before.
Hence, whenever a new container containing individual items need to be appended, make sure you getting a reference to it, add items to it and finally add the element to the main container where you want it to be displayed.
Hope it helps!
Related
I am making a packing list for my assignment and we choose any place and any 3 items to bring. What is supposed to happen is the user presses the 1, 2, or 3 buttons and a prompt will appear telling the user what they want to swap the chosen item with. Then, once they hit OK, the new item will appear in the list. So, if they press 1 and type an item, the new item will be in the 1. The same should happen if the user wanted to swap the item in 2 or the item in 3. However, the problem I am having is that it does not show the new item. It allows me to press 1, 2, or 3 and then type what I want to swap whichever item with, but then adds to the list with a 4 appearing at the end of the list. To swap the items, the assignment says to use the replaceChild() method. Did I put in something wrong in the coding or forget something? Help is appreciated.
<!DOCTYPE html>
<html lang="en">
<head>
<title>CIS 223 Chapter 10 Program</title>
<meta charset="utf-8">
<script>
var list;
function createDOM()
{
list = document.getElementById("items");
var newElement = document.createElement("LI");
var nodeText = document.createTextNode("Sunscreen");
newElement.appendChild(nodeText);
list.appendChild(newElement);
var newElement = document.createElement("LI");
var nodeText = document.createTextNode("Water");
newElement.appendChild(nodeText);
list.appendChild(newElement);
var newElement = document.createElement("LI");
var nodeText = document.createTextNode("Swim suits");
newElement.appendChild(nodeText);
list.appendChild(newElement);
}
//Swap items function.
function registerKey(keyEvent)
{
var keyValue = keyEvent.key;
if (keyValue < "1" || keyValue > "3")
{
alert("Only press 1, 2, or 3");
return;
}
var userInput;
var newInput;
var newElement = document.createElement("LI");
//Prompt user for new entry.
userInput = prompt("Enter a new item for slot "+keyValue,"");
//Check input for valid entry.
if (userInput != null && userInput != "")
{
//Write Your code to Pass string input to Text Node.
// .......
newInput = document.createTextNode("");
//Write Your code to Attach Text Node to Element.
// .......
newElement.appendChild(newInput);
list.appendChild(newElement);
var whichNode = parseInt(keyValue); // which item to be replaced
//Write Your code to Replace existing node with new node.
// .......
nodeText.replaceChild(newInput,nodeText.childnodes[LI]);
}
}
</script>
</head>
<body onload=createDOM() onkeypress=registerKey(event)>
<div id="wrapper">
<header>
<h1>Summer Vacation</h1>
</header>
<main>
<h2>We're going to Cancun, Mexico!</h2>
<p>Let's pack a few <em>essentials</em> for the trip.</p>
<ol id="items">
</ol>
<p>These provisions were carefully selected for this trip.<br><br>
However, you can swap out any of the three items by pressing:<br>
<strong>1</strong>, <strong>2</strong>, or <strong>3</strong> on your keyboard.</p>
</main>
</div>
</body>
</html>
As this is an assignment, I won't give you the fixed code, but here are the main issue that need to be fixed:
You need to create the text node using the input that the user entered. Currently newInput = document.createTextNode(""); creates a text node with an empty string "". You need to pass your user's input into this.
nodeText doesn't exist inside of your registerKey function. The variable defined within a function body {} only exist within that function body (scope). So anywhere you're trying to access nodeText inside of your registerKey function you'll get an error - you can read more about scope here.
You don't need to use list.appendChild(newElement);. This appends/adds your new item to your list at the end of the list. This isn't the behavior you want. Instead, your script will add the item to your list using .replaceChild(). So you can remove this line.
nodeText.replaceChild(newInput,nodeText.childnodes[LI]); isn't correct either. The idea of .replaceChild() is to pass in the new element you want to replace the old element with. The use case is as so:
parentNode.replaceChild(newChild, oldChild);
In your case the new element is newElement, which is the <li> element you've created containing the user's input value, and not the text node newInput. Again, here nodeText doesn't exist, so you need to use list instead of nodeText as that is the (parent) element that contains your list items/<li> elements as children.
nodeText.childnodes[LI] is also going to give you issues. As established above, nodeText needs to be list. Moreover, childnodes needs to be changed to childNodes. This will give you a NodeList of child items in your list, which is similar to an array. Recall that the item you need to pass as the second argument to replaceChild() needs to be the item you want to replace/remove from your list, so this item needs to be obtained from the node list using the whichNode number. You can take a look at NodeList.item() to help you achieve this. Note that you have created your HTML element with a space:
<ol id="items">
</ol>
meaning that using .childNodes on your empty <ol> will give you an element at index 0 of the NodeList. I suggest you remove this blank space and use <ol id="items"></ol> instead:
var items = document.getElementById("items1");
console.log(items.childNodes); // empty NodeList
var items = document.getElementById("items2");
console.log(items.childNodes); // NodeList with 1 item (undesirable)
<ol id="items1"></ol>
<ol id="items2">
</ol>
I have a page where the user can enter as many divs with a specific class they want (filterDiv). I want to have a Load More button display if the number if items is more than nine.
Problem is I am trying to access the divs with class filterDiv after the ninth iteration and add a hide class.
Here is my code:
const htCount = document.querySelectorAll('.filterDiv').length;
if (htCount > 9){
document.querySelector('#loadMore').classList.add('show'); // load more button shows
};
How would I add a code to hide divs 10,11,12 etc. until the Load More button is clicked?
If you have a document with divs that look like this:
<div class=“myDiv”> content </div>
You can first get all the divs:
var myDivs = document.getEmementsByClassName(“myDiv”);
Then loop through them and hide some of them by specifying their style attribute like this:
for(var i = 9; i < myDivs.length; i++) {
myDivs[i].style.display = “none”
}
So we are looping through indexes from 9 till the end of array and making them invisible.
The direct style property of item has higher priority than css of class, so elements will hide and you can specify all the properties of visible elements in css.
Then when a button is clicked, you can do the same loop and just change to .style.display = “block”
for(var i = 9; i < myDivs.length; i++) {
myDivs[i].style.display = “block”
}
My header on my website has 5 tabs (they are div tags all named "dropdown". Under each tab are clickable links (the anchor tags). I am trying to write some code that will print text in the console when a link is click and tell the user about where it was clicked (it uses the innerText). For instance, if a user clicks a link under the first tab, it will log "column 1| Link1" or if a user clicks on a link in the second tab "column 2| link 3". All I have is the nested for loop that will loop through the anchor tags under each div tag but I am not sure if its correct. This is what I have:
var dropdownDivs = document.getElementsByClassName('dropdown');
for(i = 0; i < dropdownDivs.length;i++) {
var lnks =
document.getElementsByClassName('dropdown').getElementsByTagName('a');
for(i = 0; i < dropdownDivs.length;i++){
for (var l in lnks) {
}};
In order to get the placement index of the DIV and the Link(anchor tag) on the page you're going to need to collect at least one of them into an array to grab their index using the indexOf method.
You can use querySelectorAll to more easily grab the elements needed to do the work.
Note: querySelectorAll returns an HTMLCollection, not an Array. They both have a forEach method so I just wanted to point this out.
// get all anchor elements within an element with the class dropdown
let collection = document.querySelectorAll(".dropdown a");
// iterate over links in elements with dropdown class
// parameters for forEach are (element, index)
collection.forEach((ele, ind) => {
// we get the parent node(the element with the dropdown class)
// then we figure out what number element(div) we're on by looking at the array
// of all elements with the dropdown class
let p = ele.parentNode;
let p_ind = Array.from(document.querySelectorAll('.dropdown')).indexOf(p);
//we add 1 to the indices because there is a 0 index and we
//would like to start at the first DIV/Link being a 1, not 0
ind++;
p_ind++;
//add an event listener to the links that alerts the results
//on click
ele.addEventListener('click', () => alert("link " + ind + " in div" + p_ind))
})
let collection = document.querySelectorAll(".dropdown a");
collection.forEach((ele, ind) => {
let p = ele.parentNode;
let p_ind = Array.from(document.querySelectorAll('.dropdown')).indexOf(p);
ind++;
p_ind++;
ele.addEventListener('click', () => alert("link " + ind + " in div" + p_ind))
})
div {
border: 1px solid black;
width: 75px;
margin: 10px;
padding: 10px;
}
<div class="dropdown">
hi
bye
</div>
<div class="dropdown">
hi
bye
</div>
<div class="dropdown">
hi
bye
</div>
<div class="dropdown">
hi
bye
</div>
<div class="dropdown">
hi
bye
</div>
From looking at your code, I'd suggest changing
document.getElementsByClassName('dropdown').getElementsByTagName('a')
with
dropdownDivs[i].getElementsByTagName('a')
I say this because document.getElementsByClassName('dropdown') will return an array again (an array you've already got by the way) rather than the element in question, which would be represented by
dropdownDivs[i]
I wan't to replace a list item of an unordered list and generate 'n' list items in its place after clicking on it (n is input by the user on prompt when a list item is clicked).
I referred to the following link, but still couldn't make out a solution for my case:
Generating list item x number of times, based on user input
This is what I tried but its not working:
HTML:
<ul id="list" onclick="inputNumber()">
<li>list-1</li>
<li>list-2</li>
<li>list-3</li>
</ul>
CSS:
li {
list-style: none;
display: inline;
}
JavaScript:
function inputNumber() {
var index = $(this).index();
// to record the index of the clicked item
var inputNum = prompt("Divide By:");
// input and store 'n' by the user
var oldList = document.getElementById("list");
// the main old list
var garbage= oldList.removeChild(oldList.childNodes[index+1]);
// remove the clicked item from the main list
var newList=document.createElement("li");
// create new list item
var i;
for (i=1;i<=inputNum;i++)
{
list.append(newList.clone().text('list-'+i));
// loop to clone and append 'n' list items in place of the previous one
}
}
JSfiddle :
https://jsfiddle.net/yoshi2095/k0jt8n7x/44/
Please help. Thanks.
edit:
only the 'clicked' item should get replaced by the 'n' items input by the user at the same place of that 'clicked' item. Other list items should remain at their respective places as before.
JSFiddle
There were a few issues you had in your code. One was you were forgetting $() around functions that were only for jQuery. You also were not removing the child elements correctly, the jQuery functions $(this) and .remove() and JavaScript .children together work to remove only the clicked element. You were also only creating one new element to add to the list, but you needed to create multiple, so I added that inside the for loop.
$("li").click(function() {
var inputNum = prompt("Divide By:");
var oldList = document.getElementById("list");
for (var i=1;i<=inputNum; i++)
{
var newList=document.createElement("li");
$(this).parent().append($(newList).text('list-'+i));
}
$(this).remove();
});
I have a function that is successful in removing an element and appending it elsewhere on the page as successful. The problem is that as soon as the document is ready jQuery adds classes and attributes to the children that upon moving are lost. I need these classes and attributes to remain after removing and appending. I have thought about calling the original function that adds the classes, but the problem is they are key based and rely on their position prior to the move, calling it after changes the key and thus will add brand new and different classes.
The classes adding jQuery is pretty standard:
$(function(){
$("div").each(function(key){
if ($(this).hasClass("container")){
$(this).find("ul").addClass("parent" + key);
$(this).find(".container-item").attr("parentClass", ".parent" + key);
};
});
});
The remove/append function:
function copy_item(draggable, target){
var account = clone_items(draggable);
//$('#'+name.uid).remove();
$('#'+name.uid).hide();
target.append(make_div(name, true, true));
//$(draggable).children().attr("class", ($(draggable).children().attr("class")));
}
function make_div(name, drag, drop){
var newdiv = document.createElement('div');
newdiv.setAttribute('id', name.uid);
newdiv.appendChild(make_h3(name.username));
ul = document.createElement('ul');
ul.setAttribute("class", "domain_list");
newdiv.appendChild(ul);
for (j = 0; j < name.domains.length; ++j) {
ul.appendChild(make_li(name.domains[j], drag));
}
return newdiv;
}
The end result in the HTMl is basically:
<div class="container">
<ul class="parent0">
<li parentClass="parent0">
<li parentClass="parent0">
When recreating this structure, I need to have the class "parent0" and the parentClass attribute intact. Above you can see I've tried hiding the element, ensuring that it still stays a valid element with the correct classes/attributes, but in the end that still didn't work out. Ideally, I could remove the element entirely and recreate it with the correct classes.
If I am correct in my understanding of what you are trying to do, you do not need to .remove() and recreate the element in order to move it. You can just do this:
function copy_item(draggable, target) {
// not sure what this variable is for
// as you don't seem to be using it?
var account = clone_items(draggable);
// ...however, appending an existing
// element to another will 'move' it
// and preserve all of it's properties
target.append($('#' + name.uid));
}