-1

I have got several select tags and divs in my html:

<div>
    <select name="one" id="oneId" class="selectOne" onchange="myFunction()">
        <option value="" disabled selected>choose*</option>
        <option value="1">Yes</option>
        <option value="2">No</option>
    </select>
    <div class="show" style="display:none;">
        <input type="number" value="" id="other"></input>
    </div>
</div>

Now I would like to do semething like this:

function myFunction() {
    var a = this.value; // to select this "select tag" of this function
    var b = this.closest("show"); // to select closest "show div" to select tag
    var c = b.children; //input // to select input in "show div"

    if (a == 1) {
        b.style.display = "block";
        c.removeAttribute("disabled");
    } else {
        b.style.display = "none";
        c.setAttribute('disabled','disabled');
    }
}

function myFunction() {
  var a = this.value; // to select this "select tag" of this function
  var b = this.closest("show"); // to select closest "show div" to select tag
  var c = b.children; //input // to select input in "show div"

  if (a == 1) {
    b.style.display = "block";
    c.removeAttribute("disabled");
  } else {
    b.style.display = "none";
    c.setAttribute('disabled', 'disabled');
  }
}
<div>
  <select name="one" id="oneId" class="selectOne" onchange="myFunction()">
    <option value="" disabled selected>choose*</option>
    <option value="1">Yes</option>
    <option value="2">No</option>
  </select>
  <div class="show" style="display:none;">
    <input type="number" value="" id="other"></input>
  </div>
</div>

The main problem is with selecting proper elements (read the comment tags)

terrymorse
  • 6,771
  • 1
  • 21
  • 27
Matredok
  • 90
  • 1
  • 13
  • Maybe you should forget the inline listeners, and take a look at [addEventListener](https://developer.mozilla.org/en-US/search?q=addEventListener). – Teemu Dec 29 '20 at 19:55
  • Change `this.closest("show");` to `this.closest(".show");` as `.closest()` takes a CSS selector and `show` is a class, not an element. – Scott Marcus Dec 29 '20 at 19:59
  • 1
    [`closest`](https://developer.mozilla.org/en-US/docs/Web/API/Element/closest) looks for ancestors, but that `div.show` is a *sibling* of your `select` element. Just use `this.nextElementSibling`. – Bergi Dec 29 '20 at 20:09
  • You should take a look at [`EventListener`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#The_event_listener_callback) and how to use the [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) object that is passed. That solves your problem of your `this` inside your listener-function. – Oskar Grosser Dec 29 '20 at 22:49

2 Answers2

2

Referencing the event target

this will differ depending on how the listener-function is added to an element.

You can add a listener using …

  1. EventTarget.addEventListener(). this will be the element it is added to.
  2. HTMLElement.onevent – where event of .onevent is an actual event name. this will be the element it is added to.
  3. … inline onevent listeners (e.g. using the HTML-attribute onchange="myFunction()"). this will be the global object, see globalThis.

It is generally recommended to use EventTarget.addEventListener() for multiple reasons. Here are two of many reasons why you should use it:

  1. It allows for multiple listeners to be added instead of just one.
  2. Using it will keep your HTML clear of JavaScript, separating functionality from structure. Use JavaScript to add functionality, not HTML.

Here is an example demonstrating the ways of adding listeners:

document.querySelector('button').addEventListener('click', log); // Case 1
document.querySelector('button:nth-child(2)').onclick = log; // Case 2

function log() {
  console.log((this === globalThis ? "'this' is 'globalThis'" : this));
}
<button>Using addEventListener()</button>
<button>Using Element.onclick</button>
<button onclick="log()">Using onevent-listener</button> <!-- Case 3 -->

To make it more consistent, one can use Event.target. The event-object is usually passed as the first argument. Using inline onevent listener would require you to pass the event-object event yourself.

Using Event.target will always return the event-target, regardless of what way you choose to add the listener.

The modified code would look like this:

document.querySelector('button').addEventListener('click', log); // Case 1
document.querySelector('button:nth-child(2)').onclick = log; // Case 2

function log(evt) {
  console.log((evt.target === globalThis ? 'test' : evt.target));
}
<button>Using addEventListener()</button>
<button>Using Element.onclick</button>
<button onclick="log(event)">Using onevent-listener</button> <!-- Case 3 -->

Finding other elements relative to an element

This is accomplished by using the .querySelector() function of Element or Document to query for children, and the Element.closest() function to query for ancestors. Both functions require a String containing CSS selectors to be passed.

In case you need to find a neighboring sibling, you can use Node.nextSibling or Node.previousSibling to find neighboring Node siblings. This also includes TextNodes and similar non-element Nodes.
To only find neighboring element siblings, use Element.nextElementSibling or Element.previousElementSibling.

If your <div class="show"> will always be the following element after your <select>, you can simply use .nextElementSibling like this:

document.querySelector('select').addEventListener('change', selectChanged);

function selectChanged(evt) {
  var divShow = evt.target.nextElementSibling;
  
  divShow.style.display = evt.target.value;
}
<select>
  <option disabled selected>Choose</option>
  <option value="block">Show &lt;div&gt;</option>
  <option value="none">Hide &lt;div&gt;</option>
</select>
<div class="show" style="display:none">
  This &lt;div&gt; is shown.
</div>
Oskar Grosser
  • 2,804
  • 1
  • 7
  • 18
0

this refers to the select element inside the onchange function.

You are calling myFunction() from that function so, as per the standard rules, this is going to be window inside myFunction.

If you want to pass the element along then you'll need to do so explicitly (either as an argument or with apply or call).


Better yet, don't use intrinsic event attributes (which have all sorts of issues), and use addEventListener instead.


Once you've sorted that problem, you'll find that you are using closest completely wrong.

It searches ancestors not siblings (so possibly you'd want this.parentElement.querySelector instead) and, like querySelector expects a selector and not a plain class name.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335