3

I have a few blocks that I want to be visible based on selected option.

div {
  display: none;
}
select[value="test1"] ~ #targ1 {
  display: block;
}
select[value="test2"] ~ #targ2 {
  display: block;
}
select[value="test3"] ~ #targ3 {
  display: block;
}
Dropdown list:
<select>
  <option value="test1">Test a</option>
  <option value="test2">Test b</option>
  <option value="test3">Test c</option>
</select>

<div id="targ1">Test 1</div>
<div id="targ2">Test 2</div>
<div id="targ3">Test 3</div>

I have also tried other selectors, such as select[selected] ~ div or option:focus #targ1.

I even tried to add an ::after pseudo element on the select element, but divs inside the option tag are ignored or act weird. It seems like select and option tags have kind of their own rules which I didn't find on w3schools.

How can I show and hide the div elements based on the currently selected option using only HTML and CSS?

Edit: Simpliest way to solve this is inline-js, but I'm looking for a no js solution.

[data-value=test1] ~ #test1 {
    color: red;
}
[data-value=test2] ~ #test2 {
    color: green;
}
[data-value=test3] ~ #test3 {
    color: blue;
}
<select onchange="this.dataset.value = this.value" data-value="test1">
  <option value="test1" checked>Test a</option>
  <option value="test2">Test b</option>
  <option value="test3">Test c</option>
</select>

<div id="test1">Target 1</div>
<div id="test2">Target 2</div>
<div id="test3">Target 3</div>
Eugene
  • 606
  • 7
  • 21

2 Answers2

3

The bad news

In order to detect which option is currently selected with CSS, you would need to apply the :checked pseudo-class to the option element, not to the select element. See the question "CSS :selected pseudo class similar to :checked, but for elements" for more info.

option:checked {
  font-weight: bold;
}
<select>
  <option>Choice 1</option>
  <option>Choice 2</option>
</select>

Combining that with CSS combinators (the adjacent sibling selector +, the general sibling selector ~, the child selector >, and the descendent selector) is frustratingly ineffectual. The combinators can be combined to select any sibling or descendant, but can never select a parent or cousin... and since the HTML spec for the select element says that its allowed content is "zero or more option or optgroup elements", you simply can't affect a div using the value of a select element with CSS alone.

Using a drop-down with JavaScript

The example below monitors the select element for changes. When a change occurs, it grabs the newly-selected option element and reads the targets for that option from a data attribute (this is a nice convenience, because it allows you to specify the filter targets right in the HTML -- thereby keeping your model and control logic separate). Then it goes through all valid targets and either adds or removes the .hidden class based on whether that target is in the list provided by the data attribute.

var filter = document.getElementById('filter');

filter.addEventListener('change', function() {
  var option = this.options[this.selectedIndex];
  var targets = option.dataset.targets.split(/(\s+)/);
  for (var target of document.getElementsByClassName('target')) {
    if (targets.indexOf(target.id) >= 0)
      target.classList.remove('hidden');
    else
      target.classList.add('hidden');
  }
});
.target {
  display: block;
}

.target.hidden {
  display: none;
}
<select id="filter">
  <option data-targets="">Show no targets</option>
  <option data-targets="t1">Show target 1</option>
  <option data-targets="t2">Show target 2</option>
  <option data-targets="t1 t2">Show targets 1 and 2</option>
</select>

<div class="target hidden" id="t1">Thsi is the first target.</div>
<div class="target hidden" id="t2">This is the second target.</div>

Using radio buttons without JavaScript

Unlike drop-down elements, radio buttons don't need to be nested inside a wrapping element. Because of this, you can structure your HTML so that the div elements are general siblings of the radio buttons, and you can use the :checked class to affect their visibility.

.target {
  display: none;
}

#f1:checked ~ #t1 {
  display: block;
}

#f2:checked ~ #t2 {
  display: block;
}

#f3:checked ~ #t1,
#f3:checked ~ #t2 {
  display: block;
}
<input type="radio" name="filter" id="f0" checked><label for="f0">Show no targets</label><br>
<input type="radio" name="filter" id="f1"><label for="f1">Show target 1</label><br>
<input type="radio" name="filter" id="f2"><label for="f2">Show target 2</label><br>
<input type="radio" name="filter" id="f3"><label for="f3">Show targets 1 and 2</label>

<div class="target" id="t1">This is the first target.</div>
<div class="target" id="t2">This is the second target.</div>
Community
  • 1
  • 1
Woodrow Barlow
  • 8,477
  • 3
  • 48
  • 86
2

@Woodrow Barlow actually has a good answer, but question is more related to JS free code

Therefore the following snippet is a good example, I actually used technique into a few pages:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <style>
            span,
            li {
                display: none;
            }

            ul {
                border: 1px solid black;
            }

            ul:focus li {
                display: block;
            }

            #rad1:checked ~ ul > #opt1,
            #rad1:checked ~ #targ1 {
                display: block;
            }

            #rad2:checked ~ ul > #opt2,
            #rad2:checked ~ #targ2 {
                display: block;
            }

            #rad3:checked ~ ul > #opt3,
            #rad3:checked ~ #targ3 {
                display: block;
            }
        </style>
    </head>
    <body>
        <input id="rad1" type="radio" name="targ" value="1" hidden checked />
        <input id="rad2" type="radio" name="targ" value="2" hidden />
        <input id="rad3" type="radio" name="targ" value="3" hidden />

        <h4>Dropdown list:</h4>
        <ul tabindex="-1">
            <li id="opt1"><label for="rad1">Test 1</label></li>
            <li id="opt2"><label for="rad2">Test 2</label></li>
            <li id="opt3"><label for="rad3">Test 3</label></li>
        </ul>

        <span id="targ1">Test 1</span>
        <span id="targ2">Test 2</span>
        <span id="targ3">Test 3</span>
    </body>
</html>

For a regular dropdown which has only 2 states, can be used input type="checkbox"


First answer summary using url hash:

<!DOCTYPE html>
<html>
    <style>
        [id*="item"] {
            display: none;
        }

        [id*="item"]:target {
            display: block;
        }
    </style>
    <body>
        <div>
            <a href="#item1">show item1</a>
            <a href="#item2">show item2</a>
            <a href="#item3">show item3</a>
        </div>

        <div tabindex="0" id="item1">item1</div>
        <div tabindex="0" id="item2">item2</div>
        <div tabindex="0" id="item3">item3</div>
    </body>
</html>
Eugene
  • 606
  • 7
  • 21