HTML form readonly SELECT tag/input

Viewed 905349

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?

46 Answers

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>

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

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

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

[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 class="form-control-sm"  style="pointer-events: none;" onclick="return false;" onkeydown="return false;" ></select>

THAT's IT

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

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

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

};

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

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.

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.

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.

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

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

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.

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

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

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>

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

Set that to True and disabled inputs are now POST.

Related