HTML форма только для чтения SELECT tag / input


589

Согласно спецификациям HTML, selectтег в HTML не имеет readonlyатрибута, только disabledатрибут. Поэтому, если вы хотите, чтобы пользователь не мог изменить выпадающий список, вы должны использовать disabled.

Единственная проблема заключается в том, что отключенные вводы HTML-форм не включаются в данные POST / GET.

Каков наилучший способ эмулировать readonlyатрибут для selectтега и при этом получать данные POST?


5
Не полагайтесь на это на стороне сервера. Любой может создать свою собственную HTML-страницу и сделать ее RW.
Брендан Берд

11
Но это не вопрос PHP.
Kaleb Brasee

4
Я бы предложил вообще не использовать элемент select в этом случае. Есть ли причина, по которой вы не можете просто отобразить значение в виде простого текста?
Big McLargeHuge

2
@ppumkin ваш комментарий не имеет смысла. Я не говорю, что никогда не будет хорошего варианта использования для выбора или скрытых полей формы. У ОП были проблемы с отображением текста на странице, и мне просто было интересно, какова цель использования элемента select в этом случае.
Big McLargeHuge

2
Должно быть, я читаю не тот вопрос. Он говорит, что хочет отключить выбор, чтобы пользователь не изменил его. Возможно, ему нужно визуализировать страницу с помощью select и использовать jquery для предотвращения изменений. Но когда он отправляет его обратно, данных для него нет. Я делал то же самое. Мне нужно отобразить выборки, которые фильтруются другими выборками, а последний выпадающий список сохраняется в БД через ajax, поэтому все предыдущие должны быть заблокированы. Когда я рендеринг страницы, да, я мог бы отображать метки вместо выбора. Но это не проблема :)
Петр Кула

Ответы:


461

Вы должны сохранить selectэлемент, disabledно также добавить еще один скрытый inputс тем же именем и значением.

Если вы снова активируете свой SELECT, вы должны скопировать его значение в скрытый ввод в событии onchange и отключить (или удалить) скрытый ввод.

Вот демо:

$('#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>


5
Если вы снова включаете выбор, вы также должны затем отключить или удалить скрытый ввод (после копирования его значения, как описано). В противном случае вы получите удвоенное значение
Адам

1
@max Ах! Хорошо, это тоже работает. Я предполагал, что с тех пор, как вы сказали, что скрытый ввод должен иметь «то же имя», что и имя для выбора.
Адам

2
Что если я использую множественный выбор?
Анюль Ривас

2
Наличие двух элементов с одинаковыми именами будет отправлять только последний включенный / выбранный ввод, а не удваивать. Кроме того, selectedотправляется только значение, а не весь список. Итак, ваш hiddenсидит перед вашим selectи держит выбранное значение. Если выбор отключен для «только для чтения», пост обратно будет содержать только значение скрытого ввода. Если выбор включен, видимый выбранный параметр будет «перезаписывать / заменять» скрытое значение, и это значение будет отправлено обратно.
Петр Кула

30
Хотя это очевидное решение, оно не подходит, поскольку вам нужно добавить еще одно поле ввода.
Донато

190

Мы могли бы также использовать это

Отключить все, кроме выбранной опции:

<select>
    <option disabled>1</option>
    <option selected>2</option>
    <option disabled>3</option>
</select>

Таким образом, раскрывающийся список все еще работает (и отправляет свое значение), но пользователь не может выбрать другое значение.

демонстрация


3
Хороший вариант для динамических опций
Диего Фаверо

11
Идеально подходит для однозначного тега SELECT! Но он не будет работать для <select multip>, который все же позволит пользователям отменить выбор некоторых из выбранных опций, тем самым изменив значение.
Михаил Бункин

2
Поддержка браузера для optionтега disabledатрибута
Джо.

4
Это отличное решение. Я добавлю, что вы можете легко добиться этого с помощью jQuery следующим образом: $("#yourSelectId option:not(:selected)).attr("disabled", "disabled")
Onkel-j

105

Вы можете повторно включить выбранный объект при отправке.

РЕДАКТИРОВАТЬ : т. Е. Обычно отключать тег select (с атрибутом disabled), а затем автоматически повторно включать его непосредственно перед отправкой формы:

Пример с jQuery:

  • Чтобы отключить это:

    $('#yourSelect').prop('disabled', true);
  • Чтобы повторно включить его перед отправкой, чтобы включить данные GET / POST:

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

Кроме того, вы можете повторно включить каждый отключенный вход или выбрать:

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

4
используйте .prop ('disabled', true / false), чтобы установить свойство disabled. Атрибут не изменит фактическое состояние.
Париж Чар

Я думаю, что это решение умнее, чем решение @bezmax . Потому что, если мы добавляем скрытый ввод, мы должны были учитывать, что только один из каждого ввода (с тем же именем) был включен в любое время. В противном случае, если оба входа были включены, на стороне сервера программа примет массив ввода, который может вызвать исключение (например, если мы не обработали эту ситуацию, и мы ожидаем одну строку и используем 'Request.Form [FieldName] 'команда)
MiT

@Kevin, работает> 90% случаев. Кроме того, вы должны иметь jquery в вашем поясе ... Эх.
Ситилдж

Это намного чище принятый ответ. Проще отменить логику, если вы не хотите, чтобы элемент больше отключался и не было лишних тегов ввода.
Хаоммару

51

другой способ сделать readOnlyатрибут кselect элемента с помощьюcss

Вы могли бы сделать как:

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

DEMO


8
Хорошее решение. Но вы все равно можете изменить значение с помощью клавиатуры.
Марио Вернер

1
Да, это довольно круто. Чтобы изменить значение с клавиатуры, вам нужно будет нажать TAB на элемент, и тогда он будет выбран. Если вы сделаете фоновый цвет серым или отключенным, вы также визуально сообщаете пользователю, что он «отключен», когда он фактически не отключен. Никто больше не поддерживает IE, поэтому кого это волнует. Вы также можете поставить нажатие клавиши предотвращение по умолчанию, если вы хотите, чтобы предотвратить.
Петр Кула

Совершенно неортодоксально! Если выбор должен быть заблокирован от рендеринга страницы,... .on('mouseover', function(){ ... });
Fr0zenFyr

pointer-events: noneпредотвращает фокусировку от курсора мыши. Чтобы также предотвратить фокусировку с клавиатуры, вы можете дополнить ее, размывая сразу все события фокусировки:$('select').css('pointer-events', 'none').on('focus', function () {$(this).blur();});
Quinn Comendant

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

Протестировано и работает в IE 6, 7 и 8b2, Firefox 2 и 3, Opera 9.62, Safari 3.2.1 для Windows и Google Chrome.


15
Проблема в том, что раскрывающийся список отображается так, как если бы он был не только для чтения. Пользователь будет думать, что это не работает ...
Лукас Эдер

8
Это сбивает с толку пользователя, поскольку он все еще может выбрать параметр, но когда он выбирает его, список возвращается к ранее выбранному значению. Гораздо более интуитивно понятно отключить список, чтобы пользователь вообще ничего не выбирал.
Дана

11
У меня была похожая проблема, и я решил ее только отображением выбранной опции. Не требуется JS, меньше путаницы для пользователя ... <select id="countries"> <option value="7" selected="selected">Country7</option> </select>
Potherca

1
@ppumkin @dana @LukasEder Нет .. нет, если вы можете исправить этот UX. Например, вы можете сделать что-то вроде onchange = 'this.selectedIndex=this.defaultIndex; alert("You should not change this..");'этого, вместо того, чтобы просто поменять выбранный индекс молча ..
Fr0zenFyr

1
@LukasEder Любой, кто опасается опыта респондентов, может добавить немного CSS, чтобы действительно подчеркнуть, что раскрывающийся список не должен изменяться. Установите курсор на not-allowedи цвет фона на #CCC.
Александр Диксон

36

Простое решение jQuery

Используйте это, если ваш выбор имеет readonlyкласс

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

Или это, если ваш выбор имеет readonly="readonly"атрибут

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

7
Пожалуйста, объясните ваш код немного. Нужно ли добавлять класс «только для чтения» к элементам выбора? Когда мы должны вызывать этот код: только в document.ready или каждый раз, когда выбор включен / отключен? Безопасен ли ваш код в noscript?
Анар Халилов

$(document).ready(function(){$('select option:not(:selected)').attr('disabled',true);});отлично работает на одном выборе. Класс «только для чтения» не требуется. Многократный выбор проблематичен тем, что, если уже выбрано несколько опций, и поэтому они не отключены, а пользователь выбирает одну из не отключенных опций, другие ранее выбранные опции становятся невыбранными.
Гордон

не понимаю ничего из этого.
Петр Кула

Он отключает параметры, отличные от выбранного, для полей выбора с классом «только для чтения». Если у вас под рукой есть элемент select, вы можете написать:$select.find('option').not(':selected').attr('disabled', 'disabled');
Семра

1
Мне нравится это решение. Я бы поменял селектор select[readonly]так, чтобы вы просто добавляли атрибут readonly к элементу - таким образом вам не нужно обрабатывать выборки иначе, чем любой другой тип. Javascript затем постепенно усиливает эффект. Имейте в виду, что это решение (и большинство других) только помогает пользовательскому агенту обеспечить наилучшее взаимодействие с пользователем - на самом деле оно ничего не применяет (это должно быть сделано на стороне сервера, если это необходимо).
JGIVONI

21

Простое решение CSS:

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

select[readonly] option{
    display:none;
}

Это приводит к тому, что Select становится серым с красивым курсором «отключить» при наведении курсора,
а при выборе список параметров «пуст», поэтому вы не можете изменить его значение.


1
Если у вас есть проверка CSRF (как в Symfony и многих других средах), она не будет работать.
TheBiShOp


11

Это лучшее решение, которое я нашел:

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

Приведенный выше код отключает все остальные параметры, не выбранные, оставляя выбранный параметр включенным. При этом выбранная опция превратится в данные обратной передачи.


11

Я знаю, что уже слишком поздно, но это можно сделать простым CSS:

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

Стиль скрывает все параметры и группы, когда выбран readonly состоянии, поэтому пользователь не может изменить свой выбор.

Нет необходимости в взломах JavaScript.


Красиво и просто. Мне это нравится.
Джонатан Родитель Левеск

Это очень хорошее решение проблемы. Спасибо!
OderWat

10

Еще проще: добавьте атрибут стиля в ваш тег выбора :

style="pointer-events: none;"

Это работает для меня, но события указателя сделают поддержку кросс-браузеров?
Абхиджит Джагтап

3
Это не сработает. Пользователь может изменить ввод с помощью клавиатуры.
Сандху

6

Это самое простое и лучшее решение. Вы установите readolny attr на свой выбор или любой другой attr, например data-readonly, и выполните следующее

$("select[readonly]").live("focus mousedown mouseup click",function(e){
    e.preventDefault();
    e.stopPropagation();
});

1
Вы должны добавить keyup ad keydown здесь, так как выпадающий список все еще доступен, «вкладывая» в него и используя клавиши со стрелками, чтобы изменить значение.
apfz

5

Установите флажок «отключить», если вы планируете сделать его доступным только для чтения, а затем удалите атрибут «отключен» непосредственно перед отправкой формы.

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

Это похоже на состояние гонки, ожидающее наступления.
Брендан Берд

5

В дополнение к отключению опций, которые не должны быть выбраны, я хотел, чтобы они фактически исчезли из списка, но все еще были в состоянии включить их, если мне потребуется позже:

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

Он находит все элементы выбора с атрибутом «только для чтения», затем находит все параметры внутри тех элементов выбора, которые не были выбраны, затем скрывает их и отключает их.

Важно разделить запрос jquery на 2 по соображениям производительности, потому что jquery читает их справа налево, код:

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

сначала найдет все невыбранные параметры в документе, а затем отфильтрует те, которые находятся внутри выделенных элементов, с атрибутом только для чтения.


Может быть, .prop("disabled", true)вместо этого
Сэм

4

Один простой подход на стороне сервера - удалить все опции, кроме той, которую вы хотите выбрать. Таким образом, в Zend Framework 1.12, если $ element является Zend_Form_Element_Select:

 $value =  $element->getValue();
 $options = $element->getAttrib('options');
 $sole_option = array($value => $options[$value]);
 $element->setAttrib('options', $sole_option);

4

Если вы отключите поле формы, оно не будет отправлено при отправке формы. Так что если вам нужно, чтобы readonlyэто работало как, disabledно отправка значений сделайте это:

После любого изменения только для чтения свойств элемента.

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

$('select:not([readonly]) option').removeAttr('disabled');

4

Решение с помощью tabindex. Работает с выбранными, но и текстовыми вводами.

Просто используйте класс .disabled.

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

Изменить: С Internet Explorer вам также нужен этот JS:

$(document).on("mousedown", ".disabled", function (e) {
    e.preventDefault();
});

2

Вслед за предложением Гранта Вагнера; Вот фрагмент кода jQuery, который делает это с помощью функций-обработчиков вместо прямых атрибутов onXXX:

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

};

2

Я решил это с помощью jquery:

      $("select.myselect").bind("focus", function(){
        if($(this).hasClass('readonly'))
        {
          $(this).blur();   
          return;
        }
      });

Это сработало очень хорошо, хотя анимация при наведении курсора все еще будет показывать стрелку выпадающего меню, которая выглядит кликабельной
Johncl

Это не работает для меня в Chrome 26: выбор все еще полностью функционален.
Энди

Но он все равно показывает список, когда вы дважды щелкаете по нему в IE. Во всяком случае, чтобы предотвратить это?
user1995781

2

Если вы используете jquery validate, вы можете сделать следующее ниже, я использовал атрибут disabled без проблем:

$(function(){
    $('#myform').validate({
        submitHandler:function(form){
            $('select').removeAttr('disabled');
            form.submit();
        }
    });
});

2

То, что я нашел, работает отлично, с простым javascript (то есть: библиотека JQuery не требуется), это изменить innerHTML <select>тега на желаемое единственное оставшееся значение.

Перед:

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

Образец Javascript:

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

После:

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

Таким образом, визуальный эффект не изменится, и это будет POST / GET в пределах <FORM>.


1

Вместо самого выбора можно отключить все параметры, кроме выбранного в данный момент. Это дает вид рабочего раскрывающегося списка, но только вариант, который вы хотите передать, является допустимым выбором.


3
В теории отличная идея - но НЕТ поддержки отключенных опций в IE до IE8. tinyurl.com/yle4bto
scunliffe

1

HTML-решение:

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

Javascript из них:

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

удалять:

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

редактировать: добавлены методы удаления


@ButtleButkus работают javascript? Я думаю, это может быть проблема, связанная с браузером. Вы пытались добавить tabindex к элементу
Kadmillos


1
<select id="case_reason" name="case_reason" disabled="disabled">

disabled="disabled" ->получит ваше значение из базы данных, покажет его в форме. readonly="readonly" ->Вы можете изменить свое значение в selectbox, но ваше значение не может быть сохранено в вашей базе данных.


неправильно, это сохранено. Похоже, что свойство «только для чтения» не обрабатывается всеми браузерами и поэтому ненадежно.
Ричи

0

Если с момента рождения выпадающий список доступен только для чтения и не требует изменений, возможно, вам следует использовать другой элемент управления? Как простой <div>(плюс скрытое поле формы) или <input type="text">?

Добавлено: если раскрывающийся список не всегда доступен только для чтения, а JavaScript используется для его включения / отключения, то это все же решение - просто измените DOM на лету.


Это не только для чтения с самого начала. Я использую JavaScript для изменения и обновления. Если предыдущий выпадающий список имеет определенное значение, он становится доступным только для чтения.
Jrgns

Тогда, возможно, вы сможете заменить этот выпадающий список текстовым полем на лету?
Vilx-

Да, но всегда присутствующий скрытый ввод, на мой взгляд, более элегантен
Jrgns

0

Ниже работало для меня:

$('select[name=country]').attr("disabled", "disabled"); 

11
К вашему сведению: отключенное поле формы не будет включено в отправку.
mz_01

0

Я справился с этим, скрыв поле выбора и показав spanвместо него только информационное значение. В случае отключения .readonlyкласса нам также нужно удалить .toVanishэлементы и показать .toShowих.

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

0

В IE я смог победить подход onfocus => onblur двойным щелчком мыши. Но запоминание значения и последующее восстановление его в событии onchange, похоже, решают эту проблему.

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

Вы можете сделать подобное без свойств расширения, используя переменную javascript.


0

Вот попытка использовать пользовательскую функцию jQuery для достижения функциональности (как упомянуто здесь):

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

});
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.