Why isn't the appendChild node not formatted by the class?

I have this ul of id=“dropdown1” defined by class=“dropdown-content”
If I add the li directly to the web page within ul tags, they are formatted correctly.
But if I add the li through appendChild, the formatting is not applied.
How do I get the nodes to assume the formatting?

HTML:

<script src="../js/app.js"></script>
<ul id="dropdown1" class="dropdown-content"></ul>
<script>makeDropdown();</script>

APP.JS:

function makeDropdown(){
    var node = document.createElement("li"); 
    var textnode = document.createTextNode('Home');
    textnode.href = '../index.html';
    node.appendChild(textnode);
    var drop = document.getElementById('dropdown1'); 
    drop.appendChild(node);
}

Please show the CSS that is styling the HTML including the list item.

From materialize.css:


.dropdown-content, .collapsible, .side-nav {
  box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
  }
  
.dropdown-content {
  background-color: #fff;
  margin: 0;
  display: none;
  min-width: 100px;
  max-height: 650px;
  overflow-y: auto;
  opacity: 0;
  position: absolute;
  z-index: 999;
  will-change: width, height;
}

.dropdown-content li {
  clear: both;
  color: rgba(0, 0, 0, 0.87);
  cursor: pointer;
  min-height: 50px;
  line-height: 1.5rem;
  width: 100%;
  text-align: left;
  text-transform: none;
}

.dropdown-content li:hover, .dropdown-content li.active, .dropdown-content li.selected {
  background-color: #eee;
}

.dropdown-content li.active.selected {
  background-color: #e1e1e1;
}

.dropdown-content li.divider {
  min-height: 0;
  height: 1px;
}

.dropdown-content li > a, .dropdown-content li > span {
  font-size: 16px;
  color: #26a69a;
  display: block;
  line-height: 22px;
  padding: 14px 16px;
}

.dropdown-content li > span > label {
  top: 1px;
  left: 3px;
  height: 18px;
}

.dropdown-content li > a > i {
  height: inherit;
  line-height: inherit;
}

What does app.js do? Is it significant here? If it is, can we have a URL to it so we can access it, too? So we can make a working demo.

What does the HTML look like when you add the list item by hand?

<ul id="dropdown1" class="dropdown-content">
            <li><a href="../index.html">Home</a></li>

App.js will host the JS for the appendChild, and any other JS needed on the pages.

I created a codepen, but the page is blank. Works fine on my computer.

That’s kinda the problem I’m having with the manual version… the page is blank.

Commenting out these properties makes the box and contents visible:

.dropdown-content {
/*  background-color: #fff;  /* */
  margin: 0;
/*  display: none;  /* */
  min-width: 100px;
  max-height: 650px;
  overflow-y: auto;
/*  opacity: 0;  /* */
  position: absolute;
  z-index: 999;
  will-change: width, height;
}

Yeah, can’t do that – it will make the dropdown menu appear already visible. Clicking will change the background-color, etc. to make the dropdown visible.

Kinda figured that, but am wondering what you click to make it visible and why that is not shown in the code?

This is the navbar I am using: http://materializecss.com/navbar.html

It needs Jquery to work. So there is a lot of code there, including materialize.js.

Looks like I was forming the node wrong in the first place. This finally works:

function makeDropdown(){ 
    var node = document.createElement("li"); 
    var link = document.createElement('a');
    link.setAttribute('href', '../index.html');
    node.appendChild(link);
    var textnode = document.createTextNode('Home');
    link.appendChild(textnode);
    var drop = document.getElementById('dropdown1');
    drop.appendChild(node);
}

Do to create all the link, do I simply copy/paste the entire code one copy after another, and just add the links and link names? Is there a short way it is supposed to be done?

I don’t know, StevenHu. I do not normally use JavaScript so I don’t know the best way to create these links. A real JS person would be a better advisor here. :slight_smile:

OK, I will transfer the question to the JS part of the forum.

What I would do instead of hard coding values inside the function is pass values to the function so it can be reused. eg.

function makeDropdown(href_val, link_text){ 

Then you can call the same function many times. eg.

makeDropdown('../index.html', 'Home');
makeDropdown('../tos.html', 'TOS');
makeDropdown('../faq.html', 'FAQ');

So this is shorthand for making an unordered list? I’ll try it out. … Looks like I should put the paired values in an array and cycle through it for each list item.

I tried this, but no response and no errors in console:

function makeDropdown(href_val, link_text) {
var o = new Object();
o.href_val = href_val;
o.link_text = link_text;
o.buildList = function(){
    var node = document.createElement("li"); 
    var link = document.createElement('a');
    link.setAttribute('href', href_val);
    node.appendChild(link);
    var textnode = document.createTextNode(link_text);
    link.appendChild(textnode);
    var drop = document.getElementById('dropdown1');
    drop.appendChild(node);
};
return o;
    var link1 = makeDropdown('../index.html', 'Home');
    var link2 = makeDropdown('search.html', 'Search');
}

(Basic idea from Javascript for Web Developers, p180, The Factory Pattern)

This one doesn’t work either:

function Dropdown(href_val, link_text) {
    this.href_val = href_val;
    this.link_text = link_text;
    this.buildList = function(){
        var node = document.createElement("li"); 
        var link = document.createElement('a');
        link.setAttribute('href', href_val);
        node.appendChild(link);
        var textnode = document.createTextNode(link_text);
        link.appendChild(textnode);
        var drop = document.getElementById('dropdown1');
        drop.appendChild(node);
        console.log(href_val, link_text);
    };
}
var dropdown1 = new Dropdown('../index.html', 'Home');
var dropdown2 = new Dropdown('search.html', 'Search');

You are returning that object, but you’re never actually calling buildlist() on it. Also, the two calls to makeDropdown() should be outside the function itself, as that code following the return is never reached (and would otherwise cause an infinite recursion).

Anyway, using a factory is a nice idea, but I think it makes the affair unnecessarily complicated here. For example, you might just pass a reference to the parent element as well, like

function appendItem(parent, href, text) {
  var item = document.createElement('li');
  var link = document.createElement('a');
  var textNode = document.createTextNode(text);

  link.appendChild(textNode);
  link.setAttribute('href', href);
  item.appendChild(link);
  parent.appendChild(item);
}

var list = document.getElementById('dropdown1');

appendItem(list, '#foo', 'foo');
appendItem(list, '#bar', 'bar');

If you are only going to populate that list once after loading the data (and don’t need to store references to those nodes), you might do it even simpler by concatenating it all to an HTML string, and then inserting it like

list.innerHTML = theString // '<li><a href="#foo">foo</a></li> etc.

Here’s a related thread from a couple of weeks ago.

x-post… I was referring to your previous comment. ^^

What is Parent supposed to resolve to?

TypeError: parent is null
TypeError: parent is undefined

You can pass a reference to any list element where the new list item will get appended, such as #dropdown1 in the above example. Oh and speaking of which, note that this

<ul id="dropdown1" class="dropdown-content"></ul>

is invalid HTML as a list must always contain list items. You could instead use a div here and replace it with the list when it’s fully populated, like

<div id="placeholder"></div>
var placeholder = document.getElementById('placeholder');
var list = document.createElement('ul');

// Populate the list, and then
placeholder.parentNode.replaceChild(list, placeholder);
2 Likes