Jrgns
Jrgns

Reputation: 25178

HTML form readonly SELECT tag/input

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?

Upvotes: 776

Views: 1075455

Answers (30)

nrofis
nrofis

Reputation: 9756

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.

For extra safty, and prevent key event, conisder adding the disabled attribute to every option and optgroup except for the selected value.

Upvotes: 48

lonix
lonix

Reputation: 20489

Simplest solution:

  • use pointer-events:none to bar mouse/touch
  • use tabindex="-1" to bar keyboard
  • use aria-disabled="true" to bar assistive technology

<select style="pointer-events:none" tabindex="-1" aria-disabled="true">
  <option value=""></option>
  <option value="1" selected>Foo</option>
  <option value="2">Bar</option>
  <option value="3">Baz</option>
</select>

Upvotes: -1

vincent thorpe
vincent thorpe

Reputation: 294

readonly data you want to send is commonly use to avoid to load again the same data from the server. so you can achieve this

<form action="#" method="post">
    <!-- this will see the user; remove name attr to make it not visible by the server -->
    <select required disabled>
        <option value="0">Select vehicle type:</option>
        <option value="1" selected>zero</option>
        <option value="2">matsuda</option>
    </select><br>

    <!-- the server will receive this data: "vehicleType", value: "1" -->
    <select name="vehicleType" required style="display:none">
        <option value="0">Select vehicle type:</option>
        <option value="1" selected>zero</option>
        <option value="2">matsuda</option>
    </select>

    <button type="submit">Go</button>
</form>

Upvotes: -1

Jakub Holan
Jakub Holan

Reputation: 421

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.

Upvotes: 0

Nick Asher
Nick Asher

Reputation: 756

[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" ;

Upvotes: 18

Wagner Pereira
Wagner Pereira

Reputation: 1078

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>

Upvotes: 0

Jacobski
Jacobski

Reputation: 771

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();

Upvotes: 0

vincent salomon
vincent salomon

Reputation: 11

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','');
}

Upvotes: 0

vimal1083
vimal1083

Reputation: 8661

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>

Upvotes: 294

vinyll
vinyll

Reputation: 11399

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).

Upvotes: 3

R.P. Pedraza
R.P. Pedraza

Reputation: 193

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.

Upvotes: 0

Joaquim Perez
Joaquim Perez

Reputation: 95

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');

Upvotes: 5

Joost00719
Joost00719

Reputation: 754

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

select[readonly] {
    pointer-events:none;
}

Upvotes: 11

Alex Begun
Alex Begun

Reputation: 451

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;
        }
    }

Upvotes: 0

PeterG
PeterG

Reputation: 782

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.

Upvotes: 2

Black Brick Software
Black Brick Software

Reputation: 473

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);

Upvotes: 44

Sam
Sam

Reputation: 5627

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

Upvotes: 0

jBelanger
jBelanger

Reputation: 1778

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();
});

Upvotes: 5

mainak chakraborty
mainak chakraborty

Reputation: 664

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

style="pointer-events: none;"

Upvotes: 16

Syd
Syd

Reputation: 23

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>.

Upvotes: 2

Afwan Zikri
Afwan Zikri

Reputation: 21

<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.

Upvotes: 1

kemiller2002
kemiller2002

Reputation: 115420

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);
});

Upvotes: 136

Mindexperiment
Mindexperiment

Reputation: 137

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

Upvotes: 0

bezmax
bezmax

Reputation: 26122

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>

Upvotes: 562

ramesh shinde
ramesh shinde

Reputation: 1

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);
    });
});

Upvotes: 0

Guilherme Oliveira
Guilherme Oliveira

Reputation: 2369

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();
});

Upvotes: 5

gordon
gordon

Reputation: 1182

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);
    });
});

Upvotes: 0

Leniel Maccaferri
Leniel Maccaferri

Reputation: 102368

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.

Upvotes: 11

Yaje
Yaje

Reputation: 2821

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

Upvotes: 88

d.raev
d.raev

Reputation: 9546

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.

Upvotes: 28

Related Questions