748

According to HTML specs, the select tag in HTML doesn't have a readonly attribute, only a disabled attribute. So if you want to keep the user from changing the dropdown, you have to use disabled.

The only problem is that disabled HTML form inputs don't get included in the POST / GET data.

What's the best way to emulate the readonly attribute for a select tag, and still get the POST data?

Adriano
  • 19,463
  • 19
  • 103
  • 140
Jrgns
  • 24,699
  • 18
  • 71
  • 77
  • 5
    Don't rely on that for the server side. Anybody can create their own HTML page and make it RW. – SineSwiper Nov 06 '12 at 19:43
  • 1
    So many solutions, they respond to your question, but none of them is saying that you can do it with a simple [isset](http://php.net/manual/en/function.isset.php) in your php code. Your code will become cluttered if you use workarounds. – machineaddict May 20 '13 at 08:48
  • 11
    But it's not a PHP-specific question. – Kaleb Brasee Oct 03 '13 at 23:03
  • @Jrgns I see you've done your research regarding readonly vs disabled HTML form input but here is useful question for those who haven't http://stackoverflow.com/questions/7730695/whats-the-difference-between-disabled-disabled-and-readonly-readonly-for-ht – Adriano Jan 24 '14 at 14:13
  • 6
    I would suggest not using a select element at all in this case. Is there any reason you can't just display the value as plain text? – Big McLargeHuge Sep 04 '14 at 21:59
  • 2
    @ppumkin your comment makes no sense. I'm not saying there's never a good use case for select or hidden form fields. The OP was having trouble displaying some text on the page, and I was simply wondering what the purpose was of using a select element in this case. – Big McLargeHuge Nov 25 '14 at 18:16
  • 3
    I must be reading the wrong question. He says he wants to disable the select so the user doesn't change it. Maybe he needs to render the page with selects and use jquery to prevent changes. But when he submits it back there is no data for it. I was doing the same. I need to display selects that is filtered by other selects and the last drop down saves to DB via ajax so all the previous must be locked down. When I re render the page, yea, OK- I could display labels instead of selects. But that is not the problem :) – Piotr Kula Nov 25 '14 at 19:04
  • @BrendanByrd For this case you add an antiforgery token to the requested form which you submit then. On serverside the token is evaluated. – Pascal Feb 21 '18 at 19:38
  • 1
    https://stackoverflow.com/a/23428851/2557900 This should be the accepted answer for this question – vimal1083 Dec 05 '18 at 07:36
  • No, it should not be. That's janky AF. – tnk479 Nov 15 '19 at 13:51

47 Answers47

546

You should keep the select element disabled but also add another hidden input with the same name and value.

If you reenable your SELECT, you should copy its value to the hidden input in an onchange event and disable (or remove) the hidden input.

Here is a demo:

$('#mainform').submit(function() {
    $('#formdata_container').show();
    $('#formdata').html($(this).serialize());
    return false;
});

$('#enableselect').click(function() {
    $('#mainform input[name=animal]')
        .attr("disabled", true);
    
    $('#animal-select')
        .attr('disabled', false)
     .attr('name', 'animal');
    
    $('#enableselect').hide();
    return false;
});
#formdata_container {
    padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
    <form id="mainform">
        <select id="animal-select" disabled="true">
            <option value="cat" selected>Cat</option>
            <option value="dog">Dog</option>
            <option value="hamster">Hamster</option>
        </select>
        <input type="hidden" name="animal" value="cat"/>
        <button id="enableselect">Enable</button>
        
        <select name="color">
            <option value="blue" selected>Blue</option>
            <option value="green">Green</option>
            <option value="red">Red</option>
        </select>

        <input type="submit"/>
    </form>
</div>

<div id="formdata_container" style="display:none">
    <div>Submitted data:</div>
    <div id="formdata">
    </div>
</div>
bezmax
  • 25,562
  • 10
  • 53
  • 84
  • Exactly, this is the standard practice as far as I know. – BobbyShaftoe Dec 15 '08 at 16:10
  • Agreed. This method is noscript safe as well. – annakata Dec 15 '08 at 16:12
  • 6
    If you reenable the select you must also then disable or remove the hidden input (after copying it's value as described) of course. Otherwise you'll get the value submitted double – Adam Mar 12 '13 at 10:55
  • @Adam That is another option, yes. I usually use the way I mentioned in my answer (with no particular reason behind my choice): select tag has no 'name' so it is never submitted, however it has an onchange event registered, so if it is reenabled - it's values are transferred to hidden input. – bezmax Mar 12 '13 at 14:16
  • 1
    @max Ah!. Ok, that works too. I had assumed since yo said that the hidden input should have the "same name" that the select had a name. – Adam Mar 12 '13 at 16:19
  • 2
    What if I'm using a multiple select? – Anyul Rivas May 23 '13 at 19:54
  • @anyulled Each select should have a separate hidden element with the value. – bezmax May 29 '13 at 12:51
  • @Max,can you make a demo of this? – bumbumpaw Aug 14 '14 at 06:14
  • Yes, anyulled! I was having trouble w/ select multiple too. I've posted below what I ended up doing. – gordon Sep 09 '14 at 16:51
  • 3
    Having two elements with the same names will only post back the last enabled input/selected, not double. Also, only the `selected` value gets posted back not the entire list. So, your `hidden` sits before your `select` and holds the selected value. If the select gets disabled for "readonly", the post back will only contain the value of the hidden input. If the select is enabled, the visible selected option will "over write/replace" the hidden value, and that is the value that will get posted back. – Piotr Kula Nov 25 '14 at 14:16
  • If you want to do it properly then this is correct! All the rest are work around'z -- if you feeling a bit rebellious but some good ones there.. . – Piotr Kula Nov 25 '14 at 14:55
  • 63
    While this is the obvious solution, it sucks as a solution, since you have to add another input field. – Donato Mar 27 '15 at 22:12
  • So, both the original dropdown and the hidden dropdown have the same name. But how do I copy the value to the hidden field if they have the same name after re-enabling? – NewbieCoder Apr 22 '15 at 02:41
  • 1
    @Yeen select field does not need a name, it should not be submitted. If you need to enable it and send it's value - either add a onChange event onto it to copy value to your hidden field, or move the name from hidden field to select field. – bezmax Apr 22 '15 at 09:27
  • 1
    I don't like how it is assumed that "jquery" is used everywhere. It's a very fat library, I've never used it due to its enormous size and performance implications. – John Oct 11 '16 at 01:44
  • 1
    @John You are correct, nowadays jQuery is not a requirement anymore, the answer however was made in 2008 when jQuery was pretty much a standard. Anyway, the post is not to advertize jQuery but to show the idea behind creating a hidden input to handle what is submitted. You can use the same technique using any other library or even a pure JS code. It's just that with jQuery the example looks the cleanest IMHO. – bezmax Oct 11 '16 at 02:32
  • 1
    dirtiest solution ever :) – TrOnNe Dec 04 '18 at 23:43
  • 1
    There is a better answer here https://stackoverflow.com/a/23428851/2557900 – vimal1083 Dec 05 '18 at 07:38
  • 1
    @Donato Would be far from the first thing sucking on the Web. The entire platform seems to have been put together by a combination of academics on mild coffee withdrawal symptoms and their interns climbing the career ladder best they can. I mean, not having the foresight to understand a simple taxonomy that is "form controls" and make these implement some single common interface that pertains to form controls in general, even when not knowing SIMULA was invented 20 years earlier, to me is a disturbing observation. Given how the Web has ended up eating all the other platforms, more or less. – Armen Michaeli Jan 24 '22 at 14:39
282

We could also disable all except the selected option.

This way the dropdown still works (and submits its value) but the user can not select another value.

Demo

<select>
    <option disabled>1</option>
    <option selected>2</option>
    <option disabled>3</option>
</select>
Gleb Kemarsky
  • 10,160
  • 7
  • 43
  • 68
vimal1083
  • 8,499
  • 6
  • 34
  • 50
130

You can re-enable the select object on submit.

EDIT: i.e., normally disabling the select tag (with the disabled attribute) and then re-enabling it automatically just before submiting the form:

Example with jQuery:

  • To disable it:

    $('#yourSelect').prop('disabled', true);
    
  • To re-enable it before submission so that GET / POST data is included:

    $('#yourForm').on('submit', function() {
        $('#yourSelect').prop('disabled', false);
    });
    

In addition, you could re-enable every disabled input or select:

$('#yourForm').on('submit', function() {
    $('input, select').prop('disabled', false);
});
fishjd
  • 1,617
  • 1
  • 18
  • 31
kemiller2002
  • 113,795
  • 27
  • 197
  • 251
  • 4
    use .prop('disabled',true/false) to set the disabled property. The attribute won't change the actual state. – Paris Char Jan 30 '15 at 02:08
  • 1
    I think this solution is clever than [@bezmax](http://stackoverflow.com/users/43677/bezmax) solution. Because if we adding hidden input, we had to consider that only one of each inputs (same name) was enable in any time. In otherwise, if both inputs was enabled, in server side, program will take array of input that can cause of exception (for example if we didn't handle this situation, and we expect one string and use 'Request.Form[FieldName]' command) – M.i.T Nov 17 '15 at 05:36
  • @Kevin, works > 90% cases. Also, you have to have jquery in your belt... Eh. – sitilge Jan 18 '16 at 08:14
  • 3
    This is much cleaner the accepted answer. Easier to undo the logic if you don't want the element disabled anymore and no extra input tags. – Haohmaru Feb 16 '16 at 13:58
  • 1
    You saved my day! I was looking for a solution similar to this. I always got NullPointerException in my Spring Boot application. Thank you very much! :) – madaimartin Oct 13 '20 at 11:46
  • I think this answer is the best solution ever rather than add new hidden input type. – Yohanim Feb 19 '21 at 08:16
84

another way of doing a readOnly attribute to a select element is by using css

you could do like :

$('#selection').css('pointer-events','none');

DEMO

Yaje
  • 2,753
  • 18
  • 32
  • 14
    Nice solution. But you can still change the value with keyboard. – Mario Werner Oct 09 '14 at 23:38
  • 2
    Yea this is pretty awesome. To change the value with keyboard you would have to TAB to the element and it becomes selected then. If you make the background colour grey or disabled, you are also visually informing the user this is "disabled" when its actually not disabled. Nobody supports IE any more so who cares. You can also put a keydown prevent default if you want to prevent. – Piotr Kula Nov 25 '14 at 14:52
  • Quite unorthodox!! If the select needs to be blocked from page render, `... .on('mouseover', function(){ ... });` – Fr0zenFyr May 14 '15 at 09:49
  • 4
    `pointer-events: none` prevents focus from the mouse cursor. To also prevent focus from the keyboard, you can supplement it by blurring immediately upon all focus events: `$('select').css('pointer-events', 'none').on('focus', function () {$(this).blur();});` – Quinn Comendant Sep 29 '18 at 06:14
  • 3
    The way to prevent an element from being focused through keyboard navigation, is to use the `tabIndex` attribute/property with the value of `-1`. – Armen Michaeli Apr 12 '22 at 17:37
  • For a solution implementing both the _"mouse lock"_ and as well as _"keyboard lock"_, see [this answer](https://stackoverflow.com/a/76200434/11875147). – Jakub Holan May 08 '23 at 12:31
44

Simple jQuery solution

Use this if your selects have the readonly class

jQuery('select.readonly option:not(:selected)').attr('disabled',true);

Or this if your selects have the readonly="readonly" attribute

$('select[readonly="readonly"] option:not(:selected)').attr('disabled',true);
Eduardo Molteni
  • 38,786
  • 23
  • 141
  • 206
  • 7
    Please explain your code a bit. Do we have to add a 'readonly' class to the select elements? When do we have to call this code: only in document.ready or every time a select is enabled/disabled? Is your code noscript safe? – anar khalilov Jan 09 '14 at 09:36
  • `$(document).ready(function(){$('select option:not(:selected)').attr('disabled',true);});` works just fine on single select. No "readonly" class is required. Multiple select is problematic in that if there is more than one option already selected and so not disabled and a user selects one of the not disabled options, the other previously selected options become unselected. – gordon Sep 09 '14 at 14:51
  • don't understand any of this. – Piotr Kula Nov 25 '14 at 14:43
  • It disables the options other than the selected one for select boxes with "readonly" class. If you have the select element in hand you can write: `$select.find('option').not(':selected').attr('disabled', 'disabled');` – Semra Jan 21 '15 at 15:13
  • Black Brick Software - you sir need an upvote, this is a quick / clean way of faking a readonly state on a select list. – monkeylumps May 15 '15 at 08:14
  • 2
    I like this solution. I would change the selector to `select[readonly]` so that you just add the readonly attribute to the element - this way you don't have to treat selects differently than any other type. The javascript then progressively enhances the effect. Keep in mind that this solution (and most of the others) only helps the user agent provide the best user experience - it does not actually enforce anything (that must be done server-side if needed). – jgivoni Jul 08 '15 at 10:37
  • This is not working for me. The elements are not modified. – toddmo Apr 21 '17 at 15:15
  • This prevents the select's value from being submitted - which does not replicate the functionality of `readonly`. – Jack Wilsdon Aug 20 '19 at 10:33
  • Working Fine Super :) – Thulasiram Apr 17 '21 at 11:06
  • @JackWilsdon Surely not? I haven't tried it yet, but it's disabling all the `option` elements that are not selected. It should still be submitted with the selected option, if there is one. – Auspex Nov 02 '22 at 12:02
43

I know that it is far too late, but it can be done with simple CSS:

select[readonly] option, select[readonly] optgroup {
    display: none;
}

The style hides all the options and the groups when the select is in readonly state, so the user can not change his selection.

No JavaScript hacks are needed.

nrofis
  • 8,975
  • 14
  • 58
  • 113
41
<select id="countries" onfocus="this.defaultIndex=this.selectedIndex;" onchange="this.selectedIndex=this.defaultIndex;">
<option value="1">Country1</option>
<option value="2">Country2</option>
<option value="3">Country3</option>
<option value="4">Country4</option>
<option value="5">Country5</option>
<option value="6">Country6</option>
<option value="7" selected="selected">Country7</option>
<option value="8">Country8</option>
<option value="9">Country9</option>
</select>

Tested and working in IE 6, 7 & 8b2, Firefox 2 & 3, Opera 9.62, Safari 3.2.1 for Windows and Google Chrome.

Grant Wagner
  • 25,263
  • 7
  • 54
  • 64
  • 17
    The problem with this is that the dropdown is rendered as if it weren't readonly. The user will think the thing doesn't work... – Lukas Eder May 24 '11 at 07:45
  • Dojo solution: var connect = dojo.connect(dojo.byId('my_select_id'), 'onfocus', function(){this.blur();return;}); – Mahomedalid Sep 08 '11 at 21:19
  • 9
    This is confusing for the user because they can still select an option, but when they select it, the list changes back to the previously selected value. It is much more intuitive to disable the list to prevent the user from selecting anything at all. – dana Jan 25 '12 at 23:20
  • 11
    I had a similar problem and solved it only displaying the selected option. No JS required, less confusion for the user... `` – Potherca Jul 05 '12 at 14:07
  • Very bad User Experience. People will say the site is broken. – Piotr Kula Nov 25 '14 at 14:37
  • 1
    @ppumkin @dana @LukasEder No.. not if you can fix this UX. For example, you could do something like `onchange = 'this.selectedIndex=this.defaultIndex; alert("You should not change this..");'` instead of just changing the selected index silently.. – Fr0zenFyr May 14 '15 at 09:32
  • Let the user select and option, and then tell them they cant select the option. Brilliant. I would close that site straight away. – Piotr Kula May 14 '15 at 12:13
  • this lets me change it one time (to something wrong) then it's locked – toddmo Apr 21 '17 at 14:57
  • 1
    @LukasEder For anyone leery about the respondent experience, they can add some CSS to really emphasize that the drop down should not be changed. Set the cursor to `not-allowed` and the background-color to `#CCC`. – Alexander Dixon May 03 '17 at 13:23
  • this is what i was looking for and also comment by @ Fr0zenFy – ManojKiran A Jun 12 '19 at 06:38
26

Simple CSS solution:

select[readonly]{
    background: #eee;
    cursor:no-drop;
}

select[readonly] option{
    display:none;
}

This results in Select to be gray with nice "disable" cursor on hover
and on select the list of options is "empty" so you can not change its value.

d.raev
  • 9,216
  • 8
  • 58
  • 79
  • 1
    If you have a CSRF validation (like in symfony and many other frameworks), it won't work. – ThEBiShOp Feb 01 '17 at 17:24
  • 3
    a small improvement: `select[readonly] option:not([selected]) {display:none;}` That way it will only disable the unselected options, and still submit the selected value in the form. But it's still an awesome answer! – Auspex Nov 02 '22 at 14:35
15

Easier still: add the style attribute to your select tag:

style="pointer-events: none;"
15

[SIMPLEST SOLUTION]

Since the OP specifically asked that he does not want to disable the select element, here is what i use to make a select readonly

In html

<select style="pointer-events: none;" onclick="return false;" onkeydown="return false;" ></select>

THAT's IT

Explanation

  • setting pointer-events to none disables the editing of the "select-element" with mouse/cursor events
  • setting the onclick & onkeydown functions to return false disables the editing of the "select-element" with keyboard

This way you don't have to create any extra element, or disable/re-enable the element with javascript or messing with form-submission logic, or use any third party library.

Plus you can easily add css-styling like setting backgrouns-color to grey or text color to grey to imply that element is readonly. I haven't added that to code, since that is pretty specific to your site-theme

Or if you want to do it via javascript

let isReadOnly = true ;

selectElement.onclick = function () {
    return !isReadOnly ;
};
selectElement.onkeydown =function(){
    return !isReadOnly ;
} ;
selectElement.style.pointerEvents = isReadOnly ? "none" : "all" ;
Nick Asher
  • 726
  • 5
  • 19
13

Yet another more contemporary option (no pun intended) is to disable all the options of the select element other then the selected one.

note however that this is an HTML 4.0 feature and ie 6,7,8 beta 1 seem to not respect this.

http://www.gtalbot.org/BrowserBugsSection/MSIE7Bugs/OptionDisabledSupport.html

epeleg
  • 10,347
  • 17
  • 101
  • 151
11

This is the best solution I have found:

$("#YourSELECTIdHere option:not(:selected)").prop("disabled", true);

The code above disables all other options not selected while keeping the selected option enabled. Doing so the selected option will make it into the post-back data.

Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480
11

A bit late to the party. But this seems to work flawlessly for me

select[readonly] {
    pointer-events:none;
}
Joost00719
  • 716
  • 1
  • 5
  • 20
5

In addition to disabling the options that should not be selectable i wanted to actually make them dissapear from the list, but still be able to enable them should i need to later:

$("select[readonly]").find("option:not(:selected)").hide().attr("disabled",true);

This finds all select elements with a readonly attribute, then finds all options inside those selects that are not selected, then it hides them and disables them.

It is important to separate the jquery query in 2 for performance reasons, because jquery reads them from right to left, the code:

$("select[readonly] option:not(:selected)")

will first find all unselected options in the document and then filter those that are inside selects with a readonly attribute.

cernunnos
  • 2,766
  • 1
  • 18
  • 18
5

If you disable a form field, this won't be sent when form is submitted. So if you need a readonly that works like disabled but sending values do this :

After any change in readonly properties of an element.

$('select.readonly option:not(:selected)').attr('disabled',true);

$('select:not([readonly]) option').removeAttr('disabled');
Joaquim Perez
  • 97
  • 1
  • 3
5

This is the simplest and best solution. You will set a readolny attr on your select, or anyother attr like data-readonly, and do the following

$("select[readonly]").live("focus mousedown mouseup click",function(e){
    e.preventDefault();
    e.stopPropagation();
});
Guilherme Ferreira
  • 2,209
  • 21
  • 23
  • 1
    you should add keyup ad keydown here as well as the dropdown is still available by "tabbing" into it and use the arrow keys to change the value. – xfscrypt Feb 18 '17 at 05:51
5

Solution with tabindex. Works with select but also text inputs.

Simply use a .disabled class.

CSS:

.disabled {
    pointer-events:none; /* No cursor */
    background-color: #eee; /* Gray background */
}

JS:

$(".disabled").attr("tabindex", "-1");

HTML:

<select class="disabled">
    <option value="0">0</option>
</select>

<input type="text" class="disabled" />

Edit: With Internet Explorer, you also need this JS:

$(document).on("mousedown", ".disabled", function (e) {
    e.preventDefault();
});
jBelanger
  • 1,526
  • 18
  • 11
5

Set the select disabled when you plan for it to be read-only and then remove the disabled attribute just before submitting the form.

// global variable to store original event/handler for save button
var form_save_button_func = null;

// function to get jQuery object for save button
function get_form_button_by_id(button_id) {
    return jQuery("input[type=button]#"+button_id);
}

// alter value of disabled element
function set_disabled_elem_value(elem_id, value)  {
    jQuery("#"+elem_id).removeAttr("disabled");
    jQuery("#"+elem_id).val(value);
    jQuery("#"+elem_id).attr('disabled','disabled');
}

function set_form_bottom_button_save_custom_code_generic(msg) {
    // save original event/handler that was either declared
    // through javascript or html onclick attribute
    // in a global variable
    form_save_button_func = get_form_button_by_id('BtnSave').prop('onclick'); // jQuery 1.6
    //form_save_button_func = get_form_button_by_id('BtnSave').prop('onclick'); // jQuery 1.7

    // unbind original event/handler (can use any of following statements below)
    get_form_button_by_value('BtnSave').unbind('click');
    get_form_button_by_value('BtnSave').removeAttr('onclick');

    // alternate save code which also calls original event/handler stored in global variable
    get_form_button_by_value('BtnSave').click(function(event){
        event.preventDefault();
        var confirm_result = confirm(msg);
        if (confirm_result) {
            if (jQuery("form.anyForm").find('input[type=text], textarea, select').filter(".disabled-form-elem").length > 0) {
                jQuery("form.anyForm").find('input[type=text], textarea, select').filter(".disabled-form-elem").removeAttr("disabled");
            }

            // disallow further editing of fields once save operation is underway
            // by making them readonly
            // you can also disallow form editing by showing a large transparent
            // div over form such as loading animation with "Saving" message text
            jQuery("form.anyForm").find('input[type=text], textarea, select').attr('ReadOnly','True');

            // now execute original event/handler
            form_save_button_func();
        }
    });
}

$(document).ready(function() {
    // if you want to define save button code in javascript then define it now

    // code below for record update
    set_form_bottom_button_save_custom_code_generic("Do you really want to update this record?");
    // code below for new record
    //set_form_bottom_button_save_custom_code_generic("Do you really want to create this new record?");

    // start disabling elements on form load by also adding a class to identify disabled elements
    jQuery("input[type=text]#phone").addClass('disabled-form-elem').attr('disabled','disabled');
    jQuery("input[type=text]#fax").addClass('disabled-form-elem').attr('disabled','disabled');
    jQuery("select#country").addClass('disabled-form-elem').attr('disabled','disabled');
    jQuery("textarea#address").addClass('disabled-form-elem').attr('disabled','disabled');

    set_disabled_elem_value('phone', '123121231');
    set_disabled_elem_value('fax', '123123123');
    set_disabled_elem_value('country', 'Pakistan');
    set_disabled_elem_value('address', 'address');

}); // end of $(document).ready function
Alessandro Vendruscolo
  • 14,493
  • 4
  • 32
  • 41
Craig Ambrose
  • 51
  • 1
  • 1
4

One simple server-side approach is to remove all the options except the one that you want to be selected. Thus, in Zend Framework 1.12, if $element is a Zend_Form_Element_Select:

 $value =  $element->getValue();
 $options = $element->getAttrib('options');
 $sole_option = array($value => $options[$value]);
 $element->setAttrib('options', $sole_option);
David
  • 815
  • 8
  • 18
3

input being your <select> element:

input.querySelectorAll(':not([selected])').forEach(option => {
  option.disabled = true
})

This will keep the select in the data (as it's not disabled) and only the option that are not selected are disabled, therefore not selectable. The result is a readable select that cannot be changed (=> read only).

vinyll
  • 11,017
  • 2
  • 48
  • 37
2

Following on from Grant Wagners suggestion; here is a jQuery snippet that does it with handler functions instead of direct onXXX attributes:

var readonlySelect = function(selector, makeReadonly) {

    $(selector).filter("select").each(function(i){
        var select = $(this);

        //remove any existing readonly handler
        if(this.readonlyFn) select.unbind("change", this.readonlyFn);
        if(this.readonlyIndex) this.readonlyIndex = null;

        if(makeReadonly) {
            this.readonlyIndex = this.selectedIndex;
            this.readonlyFn = function(){
                this.selectedIndex = this.readonlyIndex;
            };
            select.bind("change", this.readonlyFn);
        }
    });

};
thetoolman
  • 2,144
  • 19
  • 11
2

Rather than the select itself, you could disable all of the options except for the currently selected option. This gives the appearance of a working drop-down, but only the option you want passed in is a valid selection.

Adam Bellaire
  • 108,003
  • 19
  • 148
  • 163
  • 3
    In theory a great idea - but there is NO support for disabled options in IE before IE8. http://tinyurl.com/yle4bto – scunliffe Oct 14 '09 at 19:35
2

What I found works great, with plain javascript (ie: no JQuery library required), is to change the innerHTML of the <select> tag to the desired single remaining value.

Before:

<select name='day' id='day'>
  <option>SUN</option>
  <option>MON</option>
  <option>TUE</option>
  <option>WED</option>
  <option>THU</option>
  <option>FRI</option>
  <option>SAT</option>
</select>

Sample Javascript:

document.getElementById('day').innerHTML = '<option>FRI</option>';

After:

<select name='day' id='day'>
  <option>FRI</option>
</select>

This way, no visiual effect change, and this will POST/GET within the <FORM>.

Syd
  • 23
  • 6
1

If you are using jquery validate, you can do the following below, I used the disabled attribute without a problem:

$(function(){
    $('#myform').validate({
        submitHandler:function(form){
            $('select').removeAttr('disabled');
            form.submit();
        }
    });
});
Rafael Moni
  • 656
  • 1
  • 10
  • 22
1
<select id="case_reason" name="case_reason" disabled="disabled">

disabled="disabled" ->will get your value from database dan show it in the form. readonly="readonly" ->you can change your value in selectbox, but your value couldn't save in your database.

Ataur Rahman Munna
  • 3,887
  • 1
  • 23
  • 34
  • wrong, it's saved. Looks like the 'readonly' property isn't processed by all browsers and thus unreliable. – richey Mar 27 '16 at 22:58
1

If the select dropdown is read-only since birth and does not need to change at all, perhaps you should use another control instead? Like a simple <div> (plus hidden form field) or an <input type="text">?

Added: If the dropdown is not read-only all the time and JavaScript is used to enable/disable it, then this is still a solution - just modify the DOM on-the-fly.

Vilx-
  • 104,512
  • 87
  • 279
  • 422
  • It's not readonly from the beginning. I use JavaScript to change and update. If a previous dropdown has a certain value, this one becomes readonly. – Jrgns Dec 15 '08 at 16:07
  • Then perhaps you can replace this dropdown with a textbox on-the-fly? – Vilx- Dec 15 '08 at 16:09
  • Yup, but the always there hidden input is more elegant in my opinion – Jrgns Dec 15 '08 at 16:23
1

What's the best way to emulate the readonly attribute for a select tag, and still get the POST data?

Just make it an input/text field and add the 'readonly' attribute to it. If the select is effectively 'disabled', then you can't change the value anyway, so you don't need the select tag, and you can simply display the "selected" value as a readonly text input. For most UI purposes I think this should suffice.

PeterG
  • 772
  • 2
  • 11
  • 28
1

I resolved it with jquery:

      $("select.myselect").bind("focus", function(){
        if($(this).hasClass('readonly'))
        {
          $(this).blur();   
          return;
        }
      });
ownking
  • 1,956
  • 1
  • 24
  • 34
1

html solution:

<select onfocus="this.blur();">

javascript ones:

selectElement.addEventListener("focus", selectElement.blur, true); selectElement.attachEvent("focus", selectElement.blur); //thanks, IE

to remove:

selectElement.removeEventListener("focus", selectElement.blur, true); selectElement.detachEvent("focus", selectElement.blur); //thanks, IE

edit: added remove methods

Kadmillos
  • 74
  • 5
1

In IE I was able to defeat the onfocus=>onblur approach by double-clicking. But remembering the value and then restoring it in the onchange event seems to handle that issue.

<select onfocus="this.oldvalue=this.value;this.blur();" onchange="this.value=this.oldvalue;">
....
</select>

You can do similar without expando properties by using a javascript variable.

Mario S
  • 11,715
  • 24
  • 39
  • 47
Sojourneer
  • 11
  • 1
0

Here's an attempt to use a custom jQuery function to achieve the functionality (as mentioned here):

$(function(){

 $.prototype.toggleDisable = function(flag) {
    // prepare some values
    var selectId = $(this).attr('id');
    var hiddenId = selectId + 'hidden';
    if (flag) {
      // disable the select - however this will not submit the value of the select
      // a new hidden form element will be created below to compensate for the 
      // non-submitted select value 
      $(this).attr('disabled', true);

      // gather attributes
      var selectVal = $(this).val();
      var selectName = $(this).attr('name');

      // creates a hidden form element to submit the value of the disabled select
      $(this).parents('form').append($('<input></input>').
        attr('type', 'hidden').
        attr('id', hiddenId).
        attr('name', selectName).
        val(selectVal) );
    } else {
      // remove the newly-created hidden form element
      $(this).parents('form').remove(hiddenId);
      // enable back the element
      $(this).removeAttr('disabled');
    }
  }

  // Usage
  // $('#some_select_element').toggleDisable(true);
  // $('#some_select_element').toggleDisable(false);

});
tamersalama
  • 4,093
  • 1
  • 32
  • 35
0

Simply, remove the disabled attribute before submit the form.

    $('form').submit(function () {
        $("#Id_Unidade").attr("disabled", false);
    });
0
    var selectedOpt;//initialize var
    var newIdForHidden;//initialize var
    $('.disabledOnEdit').focusin(function(){
        selectedOpt = $(this).find(":selected").val();
        newIdForHidden = $(this).attr('id')+'Hidden';
        //alert(selectedOpt+','+newIdForHidden);
        $(this).append('');
        $(this).find('input.hiddenSelectedOpt').attr('id',newIdForHidden).val(selectedOpt);
    });
    $('.disabledOnEdit').focusout(function(){
        var oldSelectedValue=$(this).find('input.hiddenSelectedOpt').val();
        $(this).val(oldSelectedValue);
    });
Robin
  • 41
  • 6
0

select multiple does not respond nearly as well to the above code suggestions. With MUCH sledgehammering and kludging, I ended up with this:

var thisId="";
var thisVal="";
function selectAll(){
    $("#"+thisId+" option").each(function(){
        if(!$(this).prop("disabled"))$(this).prop("selected",true);
    });
    $("#"+thisId).prop("disabled",false);
}
$(document).ready(function(){
    $("select option:not(:selected)").attr('disabled',true);
    $("select[multiple]").focus(function(){
        thisId=$(this).prop("id");
        thisVal=$(this).val();
        $(this).prop("disabled",true).blur();
        setTimeout("selectAll();",200);
    });
});
gordon
  • 1,152
  • 1
  • 12
  • 18
0

very simple. First store value in variable. Then on change event set value to stored variable that holds initial value of

I have a whose name is mapping. Then my code will be as follows;

$("document").ready(function(){ 
    var mapping=$("select[name=mapping]").val();
    $("select[name=mapping]").change(function(){
        $("select[name=mapping]").val(mapping);
    });
});
Kampai
  • 22,848
  • 21
  • 95
  • 95
0

If you have a select tag that should be readonly you have to, logically, transform the select box in a single "text" field.

I say logically because it's like: "I have to display a value to user"

No matter if the value comes from a select tag, is still a single value and cannot be changed (readonly).

So, logically, you use a select tag only when you first insert the value.

Then, when you need to display this value, you have to put it on a "text field-readonly".

Same for a multiple-select that becomes a list of values (the selected value) if readonly

I use "text" because a readonly-tag doesn't need a "type" attribute. Cheers

Mindexperiment
  • 145
  • 1
  • 10
0

I know this won't help everyone (if you are client side only) but will help some who are full stack and have control of backend as well as front.

If a user does not have priviledge to edit a field, I only return the current selection for the drop down.

Here is some of my backend controller:

        #region Prepare Action Priviledges
        editAuditVM.ExtAuditEditRoleMatrixVM = new ExtAuditEditRoleMatrixVM
        {
            CanEditAcn = _extAuditEditRoleMatrixHelper.CanEditAcn(user, audit),
            CanEditSensitiveDesignation = _extAuditEditRoleMatrixHelper.CanEditSensitiveDesignation(user, audit),
            CanEditTitle = _extAuditEditRoleMatrixHelper.CanEditTitle(),
            CanEditAuditScope = _extAuditEditRoleMatrixHelper.CanEditAuditScope(user, audit)
        };
        #endregion


        #region Prepare SelectLists for Drop Downs
        #region AuditScope List
        IQueryable<SelectListItem> auditScopes = _auditTypesRepo.AuditTypes
            .Where(at => at.AuditTypeClassCode.ToLower() == "e")
            .Select(at => new SelectListItem
            { Text = at.AuditTypeText, Value = at.AuditTypeID.ToString() });
        // Cannot make a select readonly on client side.
        //  So only return currently selected option.
        if (!editAuditVM.ExtAuditEditRoleMatrixVM.CanEditAuditScope)
        {
            auditScopes = auditScopes
                .Where(ascopeId => ascopeId.Value == editAuditVM.ExternalAudit.AuditTypeID.ToString());
        }
        #endregion
        #endregion
Sam
  • 4,766
  • 11
  • 50
  • 76
0

So for whatever reason all jquery based solutions mentioned here did not work for me. So here is a pure javascript solution which should also preserve the selected value when doing a POST.

setDropdownReadOnly('yourIdGoesHere',true/false)
    function setDropdownReadOnly(controlName, state) {
        var ddl = document.getElementById(controlName);

        for (i = 0; i < ddl.length; i++) {
            if (i == ddl.selectedIndex)
                ddl[i].disabled = false;
            else
                ddl[i].disabled = state;
        }
    }
Alex Begun
  • 451
  • 3
  • 7
0

My solution is to add select[readonly] { pointer-events: none; } style as many people here suggested, and then add this JS to handle keyboard events:

$(document).on('keydown', 'select[readonly]', function(e) {
    if (e.keyCode != 9) {
        if (e.preventDefault) {
            e.preventDefault();
        }

        e.returnValue = false;
        e.cancel = true;
    }
});

This still allows traversing through the element with tab.

R.P. Pedraza
  • 173
  • 1
  • 6
0

Extract from https://stackoverflow.com/a/71086058/18183749

If you can't use the 'disabled' attribut (as it erases the value's input at POST), and noticed that html attribut 'readonly' works only on textarea and some input(text, password, search, as far I've seen), and finally, if you don't want to bother with duplicating all your select, checkbox and radio with hidden input logics, you might find the following function or any of his inner logics to your liking :

addReadOnlyToFormElements = function (idElement) {
    
        // html readonly don't work on input of type checkbox and radio, neither on select. So, a safe trick is to disable the non-selected items
        $('#' + idElement + ' select>option:not([selected])').prop('disabled',true);
    
        // and, on the selected ones, to mimic readOnly appearance
        $('#' + idElement + ' select').css('background-color','#eee');
    }

And there's nothing easier than to remove these readonly

removeReadOnlyFromFormElements = function (idElement) {

    // Remove the disabled attribut on non-selected 
    $('#' + idElement + ' select>option:not([selected])').prop('disabled',false);

    // Remove readOnly appearance on selected ones
    $('#' + idElement + ' select').css('background-color','');
}
0

This might be similar in a way with other solutions but is simplified to fewer lines.

Assuming there would be a jquery function that disables the target option...

$("select[id='country']").val('PH').attr("disabled", true);
$("select[id='country']").parent().append("<input type='hidden' id='country' value='PH'>");

And incase you want to re-enable the option...

$("select[id='country']").attr("disabled", false);
$("input[id='country']").remove();
Jacobski
  • 741
  • 6
  • 10
0

This javascript find all 'select' with 'readonly' atribute, and then put disabled in the 'options' except the selected (for postback)

document.querySelectorAll("select[readonly] > option:not([selected])").forEach( (el) => el.setAttribute("disabled", "disabled") );
without readonly: <select>
  <option>1</option>
  <option selected>2</option>
  <option>3</option>
</select>

<br>
with readonly: <select readonly="readonly">
  <option>1</option>
  <option selected>2</option>
  <option>3</option>
</select>
Wagner Pereira
  • 1,050
  • 11
  • 11
0

Based on Yaje's answer, Armen's comment, and my own testing, I have found this workaround to create a readonly select which can be easily reverted back to non-readonly.

Code

function state_change(){
  var readonly_state = $('#your_decider_id').val()
  if (readonly_state == "locked"){
    $('#your_select_id').css('background-color', '#E9ECEF');
    $('#your_select_id').css('pointer-events','none');
    $('#your_select_id').attr('tabIndex',-1);
  } else {
    $('#your_select_id').css('background-color', '');
    $('#your_select_id').css('pointer-events','auto');
    $('#your_select_id').attr('tabIndex',0);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<select id="your_decider_id" onchange="state_change()">
  <option value="locked">Locked</option>
  <option value="unlocked">Unlocked</option>
</select>

<select id="your_select_id">
  <option value="option1">Option 1</option>
  <option value="option2">Option 2</option>
  <option value="option3">Option 3</option>
</select>

Explanation

The css('background-color', '#E9ECEF') sets the background color to grey so the select looks like it is in readonly state. However, you can still change the values.

The css('pointer-events','none') is then used to add the pointer-events: none; style to the <select> element. This means that the element cannot be targeted by mouse pointer, and thus, you cannot change the value using the mouse.

The attr('tabIndex',0) is then used to add the tabindex="-1" attribute to the <select> element. This means that the element is not reachable via sequential keyboard navigation, and thus, you cannot change the value using the keyboard.

Jakub Holan
  • 303
  • 1
  • 8
0

Below worked for me :

$('select[name=country]').attr("disabled", "disabled"); 
HoLyVieR
  • 10,985
  • 5
  • 42
  • 67
john
  • 17
  • 1
0

I managed it by hiding the select box and showing a span in its place with only informational value. On the event of disabling the .readonly class, we need also to remove the .toVanish elements and show the .toShow ones.

 $( '.readonly' ).live( 'focus', function(e) {
                $( this ).attr( 'readonly', 'readonly' )
                if( $( this ).get(0).tagName == 'SELECT' ) {
                    $( this ).before( '<span class="toVanish readonly" style="border:1px solid; padding:5px">' 
                            + $( this ).find( 'option:selected' ).html() + '</span>' )
                    $( this ).addClass( 'toShow' )
                    $( this ).hide()
            }
    });
fiatjaf
  • 11,479
  • 5
  • 56
  • 72
-2

In an option you can use disabled="disabled", instead of on the select itself

Pankrates
  • 3,074
  • 1
  • 22
  • 28
unplugandplay
  • 618
  • 5
  • 8
  • 1
    Why was this voted down? It's a perfectly reasonable answer. If you disable all select box options, the select box is effectively read-only. – KyleFarris Nov 21 '13 at 19:56
  • 2
    Read the question first... There is "The only problem is that disabled HTML form inputs don't get included in the POST / GET data." – czjvic Feb 13 '14 at 09:33
  • In the answer suggested here, `option` is disabled, not select; therefore the data is still sent, therefore it's valid… – vinyll Dec 11 '20 at 16:45
-4

Had same problem and found very useful property of form - submitdisabledcontrols.

Set that to True and disabled inputs are now POST.

MKB
  • 7,587
  • 9
  • 45
  • 71
gbear
  • 1