0

Let me set up the situation:

I have a form. This form has several fields which are dependent upon a single drop-down, lets call it Art Kind. Based on the value of this Art Kind, several other fields are to be hidden/shown. Multiple values within this Art Kind dropdown can trigger the same set of fields being shown. For example.

User selects "Awesome", or "Cool", from the Art Kind dropdown:
     - URL and Name fields are shown
User selects "Rainbow", or "Double Rainbow", from the Art Kind dropdown:
    - URL and Name fields are hidden
    - Color and Size fields are shown

I think you get the idea.

I'm trying to come up with a better solution than something like this:

if (selected == "Awesome" || selected == "Cool")
{
    url.show();
    name.show();
}

Because there are a ton of fields, and a ton of different options that need be shown/hidden depending on selection. I've dabbled in an array, but I'm lost on how to get what I need accomplished.

I was thinking about storing the options in a multidimensional array, or an object, something like:

var values = [
     ['Awesome', 'Cool'], ['url', 'name']
]

But I'm not sure how to implement such a thing.

Any ideas? I'm open to answers that involve jQuery for simplification, but I'd rather keep other libraries out of it.

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
dmackerman
  • 2,938
  • 5
  • 26
  • 43
  • take a look at knockoutjs. You can create a view model and then have dependent observables take care of all the logic of what to show. It might not get away from some of the pain, but might be what you are looking for – CtrlDot Jun 24 '11 at 19:47
  • Possible Duplicate: http://stackoverflow.com/questions/237104/javascript-array-containsobj – Raul Agrait Jun 24 '11 at 19:50
  • I have played with Knockout before, but I'd rather not introduce another library into this. I'm actually integrating this inside an ExtJS project, so no more additional libraries please! If there's something inside of ExtJS that does this that I'm not aware of, let me know! – dmackerman Jun 24 '11 at 19:51

3 Answers3

4

It'd be better to use an object, with the <select> values being the keys. Now as to the values, it's hard to say without knowing more details.

If there are only a few different overall page setups — that is, combinations of things shown and things hidden — then it might be easiest to encapsulate those in a few functions, and have the map go from selected value to function:

function rainbowMode() {
  // show color and size, hide url and name ...
}

function awesomeMode() {
  // show url and name, hide color and size ...
}

var actionMap = {
  'Rainbow': rainbowMode,
  'DoubleRainbow': rainbowMode,
  'Awesome': awesomeMode,
  // ...
};

An entirely different approach would be to tie the selection to the addition/removal of one or more CSS classes to the form container, coupled with CSS rules to hide/show portions of the form. The only catch with this is that you can't make form fields be disabled via CSS alone, so if you need the form posts to not include inappropriate fields that won't really help.

I've been thinking of writing a jQuery plugin to service this very need :-) If I do I'll update the answer in a couple of months. (I've already got the code; I just need to yank it out of its current context.)

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • Right. But wouldn't that require me to manually hide all of the previously shown elements inside the rainbowMode() and awesomemode() functions? I'm trying to think of it as, if something is selected show those elements, if it's not, don't show them. Or is that not possible? – dmackerman Jun 24 '11 at 19:53
  • 1
    Check my update ... using CSS, you can do it. I'll elaborate if you like. However note the caveat about disabling form fields ... – Pointy Jun 24 '11 at 19:53
  • :) as always humbled by the class of your thoughts. please write the plugin as soon as possible :) had a brief glance at your answer and syntax highlighting betrayed me. it didn't look like json and so i commented :) hope you dont mind – naveen Jun 24 '11 at 20:29
  • No that's fine - the string was in fact not quoted properly. I'm not kidding about the plugin; I just need to get out from under a minor pile of work first ... – Pointy Jun 24 '11 at 20:32
3

A similar idea to @Pointy's, somewhat different execution.

function hideAllFormElements()
{
    // snip...
}

function showElements(ids)
{
    for (var i=0; i<ids.length; i++)
    {
        document.getElementById(ids[i]).style.display = 'none';
    }
}

var mapping = {
    Awesome: ['url', 'name'],
    Cool: ['url', 'name'],
    Rainbow: ['color', 'size'],
    'Double Rainbow': ['color', 'size']
};

// later...
hideAllFormElements();
showElements(mapping[selected]);

Edit

Because jQuery is the bee's knees, instead of separate hide/show functions this will suffice, given just var mapping = {...} as above:

$('#mySelect').change(function ()
{
    var selected = $(this).val();
    
    $('#myForm :input').hide();
    $.each(mapping[selected], function ()
    {
        $('#' + this).show();
    });

    // or if you want to get really fancy,
    $($.map(mapping[selected], function ()
    {
        return '#' + this;
    }).get().join(', ')).show();
});
Community
  • 1
  • 1
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • Any reason the last item in 'mapping' is in quotes and the rest aren't? – dmackerman Jun 24 '11 at 19:59
  • 2
    +1 this is cool. if OP had used jquery, it would have been much shorter with `$.map` @dmackerman: cos it has a space. :) – naveen Jun 24 '11 at 19:59
  • I'm open to answers with jQuery. It's being included on the page for one section, but if the `$.map` function would be useful I'd like to see it in action. @naveen: thanks! – dmackerman Jun 24 '11 at 20:01
  • And this looks interesting, but does it solve the issue where multiple selections can trigger the same fields being shown? How would I implement something like this? I'm going to be attaching this logic to the 'select' event on the dropdown. – dmackerman Jun 24 '11 at 20:05
  • 1
    @dmackerman see my edit for a jQuery solution (sorry, I'm not fluent with ExtJS). – Matt Ball Jun 24 '11 at 20:08
  • @matt: OP wont be right always :) was it not much cleaner to pass jquery selectors into `mapping` – naveen Jun 24 '11 at 20:26
  • 1
    @naveen yeah, I know, but he wanted `$.map` so that's how I'd do it. – Matt Ball Jun 24 '11 at 20:36
2

I've done exactly this by using the 'rel' tag, which is mostly unused in HTML. Note that my example uses jQuery, but can be easily adapted to any other JS framework, or straight Javascript.

<select id="artkind">
    <option value="cool">Cool</option>
    <option value="awesome">Awesome</option>
    <option value="rainbow">Rainbow</option>
    <option value="2xrainbow">Double Rainbow</option>
</select>

<input name="url" rel="cool awesome">
<input name="name" rel="cool awesome">
<input name="color" rel="rainbow 2xrainbow">
<input name="size" rel="rainbow 2xrainbow">

<script>
    $('#artkind').change(function() {
        $('input').hide(); // To hide everything
        $('[rel*="'+this.value+'"]').show(); // To show only desired fields
    })
</script>

Note, I haven't actually tested this exact code, so I can't guarantee it works as-is. But I have used this exact method before for the exact thing you're talking about.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189