0

I have a select tag for category and I would like the second select option for subcategories to display after you select the category. also when I try to change the style of an element it keeps returning null I think the JS is being applied before the element loads but I'm not sur how to fix it, any help would be appreciated thank you

<select onchange="displaySubCategory()" onchange="ChangeSubCategory()" id="item-category" required>
          <option class="blank-condition-value" value="">Select</option>
          <option value="Antiques">Antiques</option>
          <option value="Arts & Crafts">Arts & Crafts</option>
          <option value="Baby">Baby & Nursery</option>
          <option value="Books">Books, comics, magazines & Manga</option>
          <option value="Fashion">Fashion</option>
          <option value="Collectables">Collectables</option>
          <option value="Electronics">Electronics</option>
          <option value="Garden & Outdoor">Garden & Outdoor</option>
          <option value="Health & Beauty">Health & Beauty</option>
          <option value="Home & Furniture">Home & Furniture</option>
          <option value="Jewellery">Jewellery & Watches</option>
          <option value="Motor Vehicles">Motor Vehicles</option>
          <option value="Music">Music & Audio</option>
          <option value="Sports & Leisure">Sports, Fitness & Leisure</option>
          <option value="Toys & Games">Toys & Games</option>
          <option value="Tools & DIY">Tools & DIY</option>
        </select>
      </div>
      <div class="subcategory-box"> 
        <label id="subcategory-label" for="subcategories">Item Subcategory:</label>
        <select id="subcategories"></select>

.subcategory-box {
  position: relative;
  display: inline-block;
  height: 60px;
  bottom: 30px;
  left: 35px; 
  display: none;
}
function displaySubCategory() { 
  const category = document.getElementById('item-category');
  const subcat = document.getElementById('subcategory-box');
  window.addEventListener('change', (onDOMContentLoaded) => {
  subcat.value.style.display ='inline-block'; 
  }) ;
};
Demon King
  • 97
  • 6
  • 2
    A quick remark.. Typically, you don't want to set the value of the option to be the same as the label. I'd 're-value' yours to be from 0 to 16. The label is for people, the value is for computers. In my case, I'd make the value its index in some array. Later, you can use the value extracted from the control to index the original array if needed, or some other data. – enhzflep Jan 01 '21 at 20:24
  • 1
    @enhzflep That's not true at all. In fact, if you don't explicitly specify a `value` attribute for an `option`, then that `option`s `value` becomes its text (so the text and the value are the same). This is quite common and there's nothing wrong or out of the ordinary about doing that. Giving an index number as the `value` is not a good idea because 1) Now you've got an ordered set of numbers you need to maintain if options are added or removed and 2) the `option` elements form an array in the DOM, which will automatically have indexes anyway. – Scott Marcus Jan 01 '21 at 20:30
  • @ScottMarcus - Thanks for your thoughts Scott. It's always helpful to hear and consider alternate views. I come at this stuff as an old lover of assembly language. Though the part of me forced to endure DBs also prefers ints to strings. Your comment about added or removed options is worth its weight in Palladium, (many) thanks. – enhzflep Jan 01 '21 at 20:49
  • 1
    @enhzflep Remember though, that even if you did set the values as you suggest, they still wouldn't be ints because there is only one data type in HTML... string and all numbers in JavaScript are floats. You'd have to convert those strings to numbers in JavaScript to work with them as such. Also, the biggest thing to remember about form field values is that they are what gets submitted when a `form` is submitted, so receiving a name/value pair of: `item-category=1` would then require you to keep an indexed list of the actual category names on the server-side (yet more maintenance), when you ... – Scott Marcus Jan 01 '21 at 20:54
  • 1
    @enhzflep ... could instead just pass `item-category:Furniture`, which is much more succinct, and doesn't require nearly as much work to maintain. – Scott Marcus Jan 01 '21 at 20:55
  • Thanks @ScottMarcus for another gem of wisdom. You've provided a couple of really good examples. – enhzflep Jan 02 '21 at 00:34

2 Answers2

1

You had "subcategory-box" as a class on the div element but in js, you were accessing it with document.getElementById. You can use document.getElementsByClassName('.subcategory-box')[0] or document.querySelector('.subcategory-box'').

Another option is to change the class to ID on the DIV element and change the subsequent CSS as well.

Here is the jsfiddle in which I have implemented all the approaches mentioned in the answer.

https://jsfiddle.net/8kagxhtz/

function displaySubCategory() { 
  const category = document.getElementById('item-category');
  
  // Use if you need to check if with id
  //const subcat = document.getElementById('subcategory-box');
  
  // If you need to use class on the element 
  //const subcat = document.getElementsByClassName('subcategory-box')[0];
  
  // Or use this for class
  const subcat = document.querySelector('.subcategory-box');

  subcat.style.display ='inline-block'; 

};
Abbas Afzal
  • 131
  • 2
  • 7
  • *You can use document.getElementsByClassName('.subcategory-box')[0]* **<-- [No, no, no, no, no!](https://stackoverflow.com/questions/54952088/how-to-modify-style-to-html-elements-styled-externally-with-css-using-js/54952474#54952474)** – Scott Marcus Jan 01 '21 at 20:59
  • I have mentioned all possible options for this, preferably document.querySelector('.subcategory-box'). The document.getElementsByClassName('subcategory-box') is an option obviously not a preferred one. – Abbas Afzal Jan 03 '21 at 14:25
0

First, in order to get your code up to modern standards, don't use inline HTML event attributes like onchange. Separate your JavaScript from your HTML.

Now, the error you mention is happening because your code is this:

const subcat = document.getElementById('subcategory-box');

But you don't have any element with that id. Instead, you have this:

<div class="subcategory-box">

But this has a class, not an id. So you need a line like this:

const subcat = document.querySelector('.subcategory-box');    

And, then you'll still have an error because you then do this:

subcat.value.style.display ='inline-block';

you get the error because a div doesn't have a value property and even if it did, the style is a property of the element, not its value.

Next, this:

window.addEventListener('change', (onDOMContentLoaded) => {
  subcat.value.style.display ='inline-block'; 
}) ;

Doesn't really make sense for you to be passing an onDOMContentLoaded argument to a change event handler.

So here's what I think you are looking for:

// Get references to the DOM elements you'll work with just once
const category = document.getElementById('item-category');
const subcat = document.querySelector('.subcategory-box');

// Set up your events in JavaScript, not in HTML
category.addEventListener("change", displaySubCategory);
category.addEventListener("change", changeSubCategory);

function displaySubCategory() { 
  subcat.classList.remove("hidden"); // Remove the CSS that is hiding the element
}

function changeSubCategory() { 

}
/* Separate this into its own class
   so you can add/remove it separately
   from other styling. */
.hidden {
   display: none;
}
<div>
  <select id="item-category" required>
     <!-- If you don't supply a value, then the value becomes the option's text -->
     <option class="blank-condition-value" value="">Select</option>
     <option>Antiques</option>
     <option>Arts & Crafts</option>
     <option value="Baby">Baby & Nursery</option>
     <option value="Books">Books, comics, magazines & Manga</option>
     <option>Fashion</option>
     <option>Collectables</option>
     <option>Electronics</option>
     <option>Garden & Outdoor</option>
     <option>Health & Beauty</option>
     <optio>Home & Furniture</option>
     <option value="Jewellery">Jewellery & Watches</option>
     <option>Motor Vehicles</option>
     <option value="Music">Music & Audio</option>
     <option value="Sports & Leisure">Sports, Fitness & Leisure</option>
     <option>Toys & Games</option>
     <optio>Tools & DIY</option>
  </select>
</div>
<div class="subcategory-box hidden"> 
   <label id="subcategory-label" for="subcategories">Item Subcategory:</label>
   <select id="subcategories"></select>
</div>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71