3

I am programmer who learning jQuery javascript but never really grasped vanilla javascript (i know I am a naughty programmer). My question is how would I go about replicating this functionality in vanilla JS?

$('select').change(function() {
    if($(this).val() == "Other (please specify)") {
        $(this).parent().parent().find("input.hidden").show();
    }
});
j08691
  • 204,283
  • 31
  • 260
  • 272
Udders
  • 6,914
  • 24
  • 102
  • 194
  • 5
    One thing you should do is look at the jQuery source code to see exactly what the methods do (`$`, `change`, `val`, `parent`, `find` and `show` here). – rgthree Oct 02 '12 at 14:42
  • Since this does pretty sophisticated querying and manipulation of the DOM, it's a great use case for jQuery. As a general comment, the way to solve this sort of problem is 1) figure out what your code is supposed to do, and 2) express that in javascript. – Tamzin Blake Oct 02 '12 at 14:44
  • 2
    Wow scary to think people go straight into jQuery without doing the basics :-o more work for the people that will know how to fix things ;-) – Alex Gill Oct 02 '12 at 14:47
  • 1
    @ThomBlake: Sophisticated querying? It's selecting by tag and class. A person should need more than simple DOM selection to motivate them to use a large library like jQuery. – I Hate Lazy Oct 02 '12 at 14:53
  • @Alex Have you ever programmed in assembly? Do you expect everyone to? – Ian Oct 02 '12 at 14:54
  • 2
    @ianpgall: That's a silly comparison. The native DOM API is very high-level and well within the capability of most people. Sadly, that's a typical jQuery mentality. – I Hate Lazy Oct 02 '12 at 14:56
  • @user1689607: I disagree. That train of thought really only matters to programmers, and the truth is that most people are really only trying to get from point A to B in the quickest way possible. You don't start driving a car by learning to build one first. You take it for a spin and then think "Hey, I'd like to know more about this". – jwatts1980 Oct 02 '12 at 15:01
  • @user1689607 Not silly at all. People could learn assembly and understand the low level of the computer. Sure native Javascript is very capable of most people, but why learn the full DOM API when you can just understand the theory, and use a library that makes cross-browser compatibility. I was against jQuery years ago, and I never fully learned the DOM API...just enough to do what I want, and then found how nice jQuery was. I know some people rely on it way too much, but it's not a bad idea to use it for normal functionality. – Ian Oct 02 '12 at 15:02
  • 2
    @jwatts1980: Driving a car compared to building a car is hardly analogous. It's more like driving a car compared to driving a car that may be a little less comfortable at first. – I Hate Lazy Oct 02 '12 at 15:06
  • @user1689607 The point is that jQuery is built from Javascript, as a car is built from its parts. You don't HAVE to understand the Javascript language to understand jQuery, as you don't HAVE to understand the parts of a car to know how to drive one. In both cases, you have to understand the theory of how Javascript and parts of a car work, in order to use their superior. – Ian Oct 02 '12 at 15:25
  • 1
    @ianpgall: Yeah, my point is that these analogies about building cars and such suggest that the native API is far out of reach, and makes it seem as though there's a much greater gap between it and jQuery than there really is. Not sure if you've noticed, but people who write jQuery without understanding the language and the DOM often end up writing some pretty bad code. Have you ever seen this? `$(this).attr("id")`? Or this? `$(this)[0].outerHTML`. It's like jQuery is some magic gateway through which all DOM elements must pass. – I Hate Lazy Oct 02 '12 at 15:59
  • ...then there's this gem: `for(var i = 0; i < $('.cls').length; i++) { if ($('.cls').eq(i).addr("id") == "foo") { $('.cls').eq(i).doSomething(); }` – I Hate Lazy Oct 02 '12 at 16:02
  • jQuery is vanilla javascript , you are mixing javascript and the DOM,which proves that most people dont understand what javascript is,even those who claim writing "vanilla" javascript.There is no such thing as "vanilla" javascript. – mpm Apr 03 '14 at 15:28

2 Answers2

6

This site might help !

But here's a step by step conversion:

var selects = document.getElementsByTagName('select');
for (var i=0; i<selects.length; i++) {
   selects[i].onchange = function() {
    if( this.value == "Other (please specify)") {
        var elements = this.parentNode.parentNode.getElementsByTagName("input");
        for (var j=0; j<elements.length; j++) {
           if( !elements[j].className.match(/\bhidden\b/)) continue;
           elements[j].style.display = ''; // the exact thing to do here would depend on your previous actions 
        }
    }
   }
}
Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • Technically the onchange is an [addEventListener](https://developer.mozilla.org/en-US/docs/DOM/element.addEventListener) – epascarello Oct 02 '12 at 14:46
  • @epascarello right. To others : the difference is that `onchange` erases precedently defined callback. But it's generally the goal. – Denys Séguret Oct 02 '12 at 14:48
  • @epascarello: addEventListener just lets you add multiple events, plus its not cross browser – jwatts1980 Oct 02 '12 at 14:48
  • That doesn't matter, you shouldn't be using `onchange` because when you use `$("something").change()` it lets you add a new event, not overwrite any old ones. `addEventListener` is one of the options, but you should use something like: http://ejohn.org/projects/flexible-javascript-events/ – Ian Oct 02 '12 at 14:51
  • @dystroy: I don't think `show()` changes the `visibility`. It sets `display: none;` I'm pretty sure. – jwatts1980 Oct 02 '12 at 14:51
  • @Kolink thanks for the change that removed the need for querySelectorsAll – Denys Séguret Oct 02 '12 at 14:51
  • @jwatts1980 I think you mean `display: block;` or `display: inline;` depending on the element. – Ian Oct 02 '12 at 14:51
  • @jwatts1980 I originally used display='block', but the problem is that you usually use a specific code (depending on your dom) while jquery has a generic (and smart) function. – Denys Séguret Oct 02 '12 at 14:53
  • @ianpgall: Right! `hide()` sets `display:none;` – jwatts1980 Oct 02 '12 at 14:53
  • @dystroy: Understood. I've never peeked at the jQuery code for `show()`, but I've always wondered how it knows which `display` type to set.. so yeah, `visibility` was probably the better option for this code. – jwatts1980 Oct 02 '12 at 14:55
  • I went into [the source code](http://code.jquery.com/jquery-latest.js) to see that jquery simply does 'display=""'. – Denys Séguret Oct 02 '12 at 14:55
  • @jwatts1980 I wonder if it just checks the tag name against a list of known block/inline elements. But I'm guessing when you call `.show` or `.hide`, it stores the past state in `$.cache`, which is basically `$.data`...and of course checks that first to see if a previous known state was "saved". – Ian Oct 02 '12 at 14:56
  • @dystroy I highly doubt that. You can set `display: inline` for a div, and using `.hide` and `.show`, I'm sure it keeps its original "inline" style. That would be a bad bug if it didn't. – Ian Oct 02 '12 at 14:57
  • @dystroy I actually did look in the source, and it has this: `jQuery._data( elem, "olddisplay" )` which may or may not refer to what we're talking about...but like I said, it looks at jQuery's stored state of it...not just setting `display=''`. Unless you can actually find where it does that. – Ian Oct 02 '12 at 15:09
  • @ianpgall yes, you're right. Hence the edit in the answer (I hadn't had the time to check all comments). – Denys Séguret Oct 02 '12 at 15:14
6

$('select') - use document.getElementsByTagName, then loop over the returned list

.change(function() {…} - check out advanced event registration model for browser differences

$(this).val() - simply this.value; you should use this even in jQuery

$(this).parent().parent() - get the parentNode of the element (two times)

.find("input.hidden") - this is a bit harder. You could use .querySelector[All], but that does not work in legacy browsers. jQuery adds lots of sugar with its cross-browser selector engine. You might use another way to get the input element(s) that works cross-browser; you might try something along javascript document.getElementsByClassName compatibility with IE.

.show() - just remove the display:none; via el.style.display = "";. Btw, you might just want to remove the hidden class instead of overwriting it with an inline style :-)

Real vanilla for W3-compliant browsers:

[].each.call(document.getElementsByTagName('select'), function(select) {
    select.addEventListener("change", function(e) {
        if (this.value == "Other (please specify)") {
            var inputs = this.parentNode.parentNode.querySelectorAll("input.hidden");
            for (var i=0; i<inputs.length; i++)
                inputs[i].classList.remove("hidden");
        }
    }, false);
});

This should work in older browsers, too:

(function(selects, handler) {
    if (document.addEventListener)
        for (var i=0; i<selects.length; i++)
            selects[i].addEventListener("change", handler, false);
    else
        for (var i=0; i<selects.length; i++)
            selects[i].attachEvent("onchange", handler);
})(document.getElementsByTagName('select'), function() {
    if (this.value == "Other (please specify)") {
        var inputs = this.parentNode.parentNode.getElementsByTagName("input");
        for (var i=0; i<inputs.length; i++)
            if (/\bhidden\b/.test(inputs[i].className))
                inputs[i].style.display = "";
    }
});
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Why only target W3-compliant browsers? – Ian Oct 02 '12 at 14:54
  • I don't want to *target* any browsers, that's the OPs job :-) My aim was to show standards-compliant code, in the text I've mentioned what to change for older/MS browsers. Also, I didn't want to repeat Dystroy (the only improvement would have been not using `onchange` property). – Bergi Oct 02 '12 at 15:02
  • True you would've "repeated" the code, but you would've made it correct. A cross-browser way of `.querySelectorAll` (in this scenario) would be `.getElementsByTagName("input")` and then matching the class...which is what Dystroy did do. But it's worth mentioning all options in order to cover all scenarios. – Ian Oct 02 '12 at 15:06
  • 1
    And I also don't think jQuery just sets `.style.display = ""`...it saves the previous state. Which I understand isn't as serious or necessary in this case, but you could set the `display: inline` for a div in the CSS, and expect that to come back, but using `display = ""` would turn it back into `block`. – Ian Oct 02 '12 at 15:11
  • How jQuery implements show/hide is completely irrelevant. What matters is creating code that will properly set the display of the element. Hard to say what the best approach would be without more information. – I Hate Lazy Oct 02 '12 at 15:17
  • @user1689607 It's perfectly relevant, because this doesn't properly set the display of the element. Like I said before, if for some reason, someone wants to override default `display` style, and even use something like `inline-block` or non-default display, then simply setting `display = ''` will NOT keep that original setting - it will use the default of the element (`block` for div, `inline` for span, etc.) – Ian Oct 02 '12 at 15:21
  • 1
    How jQuery implements it is completely irrelevant. You can have a perfectly working implementation without emulating jQuery. I didn't say that doing `display = ""` is sufficient. I *did* say that it's hard to know the best approach without more information. – I Hate Lazy Oct 02 '12 at 15:25
  • @ianpgall: OK, I've added a cross-browser implementation, you might remove your downvote :-) Also, I don't think anyone should give an inline-style `display:weird;` to a div, instead use the right tag (like span) - it would be OK to style them from a stylesheet. If we wanted to save a previous state, I'd need to formulate a vanilla-`hide()` first… – Bergi Oct 02 '12 at 15:26
  • @user1689607 It's still not irrelevant. The fact that they do it right (in whatever way) is relevant, and that adopting something similar for the answer to the OP's question is necessary, yet `display = ''` is sufficient for now. – Ian Oct 02 '12 at 15:27
  • @Bergi: Realistically, you would probably abstract parts of your code away in to reusable functions, right? I mean I'm sure it works *(aside from the missing `}`)*, but that's not how you'd actually write it, is it? – I Hate Lazy Oct 02 '12 at 15:28
  • @Bergi I didn't mean something like `display: weird;`, I meant a valid value for the `display` style...such as anything from https://developer.mozilla.org/en-US/docs/CSS/display . Which at any point, could be applied to any element (it's ultimately up to the developer), and shouldn't be forgotten. I know the answers to this question are just an example and I'm overkilling it, but that's why these are comments, just pointing it out :) – Ian Oct 02 '12 at 15:33
  • @user1689607: Surely you would abstract the functionality. Then add some gimmicks and sugar, and out comes your own framework :-) I guess, realistically you just *would* use jQuery – Bergi Oct 02 '12 at 15:34
  • @ianpgall: Yeah, I actually think all inline styles are bad practise (some like `
    ` even "weird"), but one should really use stylesheets and classes for such things.
    – Bergi Oct 02 '12 at 15:40
  • @Bergi: It would take a lot of abstractions to equal the size of jQuery. Very often it isn't necessary. A reusable function here and there is just part of writing clean code. – I Hate Lazy Oct 02 '12 at 15:41