Как преодолеть ограничение вложенности HTML-формы?

199

Я знаю, что XHTML не поддерживает вложенные теги форм, и я уже читал другие ответы здесь на Stack Overflow относительно этой темы, но я до сих пор не нашел элегантного решения проблемы.

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

Давайте посмотрим на очень простой пример:

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

<form
 action="/post/dispatch/too_bad_the_action_url_is_in_the_form_tag_even_though_conceptually_every_submit_button_inside_it_may_need_to_post_to_a_diffent_distinct_url"
method="post">

    <input type="text" name="foo" /> <!-- several of those here -->
    <div id="toolbar">
        <input type="submit" name="save" value="Save" />
        <input type="submit" name="delete" value="Delete" />
        <a href="/home/index">Cancel</a>
    </div>
</form>

Наша цель - написать форму так, чтобы она не требовала JavaScript , просто старая HTML-форма и кнопки отправки.

Поскольку URL-адрес действия определен в теге Form, а не в каждой отдельной кнопке отправки, наш единственный вариант - опубликовать общий URL-адрес, а затем запустить «if ... then ... else», чтобы определить имя кнопки, которая был представлен. Не очень элегантно, но наш единственный выбор, так как мы не хотим полагаться на JavaScript.

Единственная проблема заключается в том, что нажатие «Удалить» отправит ВСЕ поля формы на сервер, хотя единственное, что необходимо для этого действия, - это скрытый ввод с post-id. В этом небольшом примере нет ничего сложного, но у меня есть формы с сотнями (так сказать) полей и вкладок в моих LOB- приложениях, которые (из-за требований) должны отправлять все за один раз, и в любом случае это кажется очень неэффективным и трата. Если бы поддержка вложенных форм была поддержана, я бы по крайней мере смог бы обернуть кнопку «Удалить» внутри своей формы только с полем post-id.

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

Итак, мой вопрос (особенно к тем, кто говорит, что им не нужна вложенность форм): что ВЫ делаете? Есть ли какое-то элегантное решение, которое я пропускаю, или нижняя строка действительно "Требуется JavaScript или отправьте все"?

gCoder
источник
17
Забавно, но XHTML допускает непрямое вложение форм согласно интересной заметке anderwald.info/internet/nesting-form-tags-in-xhtml - (X) HTML запрещает вложение форм, таких как «form> form», но разрешает «form> fieldset> form ", валидатор W3 говорит, что он действителен, но в таких браузерах есть ошибки.
Ниши
linkrot исправление @ Ниша комментарий ссылка: web.archive.org/web/20170420110433/http://anderwald.info/...
Альбертом

Ответы:

53

Я бы реализовал это точно так, как вы описали: отправьте все на сервер и сделайте простой if / else, чтобы проверить, какая кнопка была нажата.

И затем я реализовал бы вызов Javascript, связывающий событие onsubmit формы, который проверял бы перед отправкой формы, и только отправлял соответствующие данные на сервер (возможно, через вторую форму на странице с идентификатором, необходимым для обработки вещи как скрытый ввод, или обновите местоположение страницы с данными, которые вам нужно передать как запрос GET, или отправьте сообщение Ajax на сервер, или ...).

Таким образом, люди без Javascript могут нормально использовать форму, но нагрузка на сервер компенсируется, потому что люди, у которых есть Javascript, представляют только один необходимый фрагмент данных. Сосредоточение внимания только на поддержке одного или другого действительно ограничивает ваши возможности без необходимости.

В качестве альтернативы, если вы работаете за корпоративным брандмауэром или чем-то другим, и у всех отключен Javascript, вы можете захотеть сделать две формы и поработать с CSS-магией, чтобы создать вид, что кнопка удаления является частью первой формы.

Один карандаш
источник
15
оригинал ОП гласил: нет JavaScript
Джейсон
26
Проблема этого метода в том, что он не является RESTful. Сохранение и удаление соответствуют различным действиям над ресурсом: «Сохранить» -> POST / my_resource (создание нового ресурса), «Сохранить» -> PUT / my_resource (изменение существующего ресурса), «Удалить» -> DELETE / my_resource (уничтожить, уничтожить). Ресурс) Скорее всего, проблема в том, что POST должен создать новый ресурс. Конечно, он никогда не должен удалять существующий ресурс.
user132447
178

Я знаю, что это старый вопрос, но HTML5 предлагает несколько новых опций.

Первый - это отделить форму от панели инструментов в разметке, добавить другую форму для действия удаления и связать кнопки на панели инструментов с соответствующими формами, используя formатрибут.

<form id="saveForm" action="/post/dispatch/save" method="post">
    <input type="text" name="foo" /> <!-- several of those here -->  
</form>
<form id="deleteForm" action="/post/dispatch/delete" method="post">
    <input type="hidden" value="some_id" />
</form>
<div id="toolbar">
    <input type="submit" name="save" value="Save" form="saveForm" />
    <input type="submit" name="delete" value="Delete" form="deleteForm" />
    <a href="/home/index">Cancel</a>
</div>

Эта опция довольно гибкая, но в оригинальном сообщении также упоминалось, что может потребоваться выполнить различные действия с одной формой. HTML5 снова приходит на помощь. Вы можете использовать formactionатрибут на кнопках отправки, чтобы разные кнопки в одной форме могли отправлять разные URL-адреса. В этом примере просто добавляется метод клонирования на панель инструментов вне формы, но он будет работать так же, как вложенный в форму.

<div id="toolbar">
    <input type="submit" name="clone" value="Clone" form="saveForm"
           formaction="/post/dispatch/clone" />
</div>

http://www.whatwg.org/specs/web-apps/current-work/#attributes-for-form-submission

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

shovavnik
источник
4
+1 - но было бы замечательно узнать, для чего form=нужна поддержка браузера - CanIUse на самом деле не говорит. caniuse.com/#feat=forms
AKX
1
Да, это отлично, но на сегодняшний день функция, не поддерживаемая IE, по-прежнему дисквалифицирует ее для «производственного» использования
Jako
3
Это плохая практика <input>для объявления кнопок. <button>элемент был введен 15 лет назад для этой цели. На самом деле люди.
Николас Шенкс
27
Ваша точка зрения излишне спорна и не имеет отношения к вопросу ОП. ОП спросил об ограничении вложенности форм без использования JavaScript, и ответ предлагает решение. Используете ли вы элементы <input>или <button>элементы, не имеет значения, хотя кнопки, как вы говорите, обычно больше подходят для панелей инструментов. Кстати, элементы кнопок не поддерживаются всеми браузерами уже 15 лет.
Шовавник
6
@AKX Хотя это старое утверждение, вы сможете просмотреть поддержку этой функции здесь: html5test.com/compare/feature/form-association-form.html . IE списывает, большинство других настольных браузеров кажутся терпимыми.
жировая
16

Вы можете иметь формы рядом на странице, но не вложенные. затем использовать CSS, чтобы все кнопки выстроились в ряд?

<form method="post" action="delete_processing_page">
   <input type="hidden" name="id" value="foo" />
   <input type="submit" value="delete" class="css_makes_me_pretty" />
</form>

<form method="post" action="add_edit_processing_page">
   <input type="text" name="foo1"  />
   <input type="text" name="foo2"  />
   <input type="text" name="foo3"  />
   ...
   <input type="submit" value="post/edit" class="css_makes_me_pretty" />
</form>
Джейсон
источник
5
Да, и это кажется слишком хакерским. Кроме того, на очень большой странице (представьте вкладки и вложенные вкладки и вложенные кнопки отправки на нескольких уровнях области действия) практически невозможно полностью отделить положение экрана элементов от их положения в документе.
gCoder
3
ну, всегда есть баланс между тем, что ты хочешь сделать, и тем, что ты можешь сделать. Похоже, что это варианты - либо одна форма обрабатывается на сервере, либо одна форма, которую сложно поддерживать - выбирайте мудро :)
Jason
3
Да, к сожалению, со всеми этими ограничениями в html, я просто придерживаюсь того, что поддерживается, и отправляю всю форму на сервер, а затем ненавязчиво использую javascript для клиентов, которые поддерживают его для перехвата субмиссии формы и отправки только того, что действительно необходимо. Это лучший компромисс.
gCoder
13

В HTML5 есть идея «владелец формы» - атрибут «форма» для элементов ввода. Это позволяет эмулировать вложенные формы и решит проблему.

Ниши
источник
1
Любая ссылка на эту ссылку стоит?
Дакаб
См. W3.org/TR/html5/forms.html#form-owner "Элемент, связанный с формой, по умолчанию связан с ближайшим элементом формы предка, но, если он может быть повторно связан, может иметь атрибут формы, указанный для переопределения этот."
Ниши
11

Вид старой темы, но эта может быть полезна для кого-то:

Как кто-то упоминал выше - вы можете использовать пустышку. Мне пришлось преодолеть эту проблему некоторое время назад. Сначала я полностью забыл об этом ограничении HTML и просто добавил вложенные формы. Результат был интересным - я потерял свою первую форму из вложенного. Тогда оказалось, что это своего рода «хитрость» - просто добавить фиктивную форму (которая будет удалена из браузера) перед фактическими вложенными формами.

В моем случае это выглядит так:

<form id="Main">
  <form></form> <!--this is the dummy one-->
  <input...><form id="Nested 1> ... </form>
  <input...><form id="Nested 1> ... </form>
  <input...><form id="Nested 1> ... </form>
  <input...><form id="Nested 1> ... </form>
  ......
</form>

Прекрасно работает с Chrome, FireFox и Safari. IE до 9 (не уверен около 10) и Opera не определяет параметры в основной форме. Глобальное $ _REQUEST пусто, независимо от входных данных. Внутренние формы, кажется, работают хорошо везде.

Не проверялось другое предложение, описанное здесь - набор полей вокруг вложенных форм.

РЕДАКТИРОВАТЬ : Frameset не работает! Я просто добавил основную форму после других (не больше вложенных форм) и использовал «клон» jQuery для дублирования входных данных в форме при нажатии кнопки. Добавлен .hide () к каждому из клонированных входов, чтобы сохранить макет без изменений, и теперь он работает как шарм.

BDJoe
источник
Это отлично сработало для меня. Я работал над страницей asp.net, у которой была всеобъемлющая форма. У меня была внутренняя вложенная форма для использования в плагине jQuery Validation ( github.com/jzaefferer/jquery-validation ), и хотя она отлично работала в FireFox и IE, она не удалась в Chrome с 'TypeError: Невозможно вызвать метод' element 'из не определено. Выяснилось, что вызов инициализации validate () не удался, поскольку он не мог найти внутреннюю форму, так как Chrome удалил ее из блока html, добавленного с помощью jQuery.html (). Добавление небольшой фиктивной формы привело к тому, что Chrome покинул настоящую.
Джон - Не число
Кажется, это больше не работает. Firefox удалил второй тег <form> и завершил мою форму первым тегом </ form>. Это оставило недопустимый тег </ form> внизу страницы и форму, которая не будет отправлена ​​правильно. :(
Тарквин
Очень полезно для меня. Я добавляю веб-виджет, который содержит форму на страницу asp.net. Удивленный WebForm, он всегда генерирует форму, заключающую все в тело HTML. Вложенная форма становится головной болью для веб-виджета. Фиктивная форма кажется быстрым и эффективным решением этой ситуации. Он отлично работает в Chrome и Safari, пока не тестировал Firefox. Спасибо, что поделились, сэкономили мне много времени!
JM Ян
4

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

Или, конечно, переделать ваш интерфейс и позволить людям полностью удалить что-то еще, что не требует, чтобы они вообще видели enormo-форму.

AmbroseChapel
источник
2

Один из способов сделать это без javascript - добавить набор переключателей, определяющих действие:

  • Обновить
  • Удалить
  • Без разницы

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

Другим способом было бы разместить две формы на странице, как вы предложили, только не вложенные. Макет может быть трудно контролировать, хотя:

<form name = "editform" action = "the_action_url" method = "post">
   <input type = "hidden" name = "task" value = "update" />
   <input type = "text" name = "foo" />
   <input type = "submit" name = "save" value = "Save" />
</ Форма>

<form name = "delform" action = "the_action_url" method = "post">
   <input type = "hidden" name = "task" value = "delete" />
   <input type = "hidden" name = "post_id" value = "5" />
   <input type = "submit" name = "delete" value = "Удалить" />
</ Форма>

Использование скрытого поля «задача» в скрипте обработки для соответствующего ветвления.

Рон Сэвидж
источник
1

Эта дискуссия по-прежнему интересует меня. За исходным постом стоят «требования», которые, по-видимому, разделяет ФП - то есть форма с обратной совместимостью. Как человек, чья работа на момент написания статьи иногда должна поддерживать IE6 (и на годы вперед), я копаю это.

Без продвижения структуры (все организации захотят убедиться в совместимости / надежности, и я не использую это обсуждение как оправдание для структуры), решения Drupal для этой проблемы интересны. Drupal также имеет прямое отношение, потому что у фреймворка давно существует политика «она должна работать без Javascript (только если вы хотите)», то есть проблема ОП.

Drupal использует довольно обширные функции form.inc, чтобы найти triggering_element (да, это имя в коде). Смотрите в нижней части кода, приведенного на странице API для form_builder (если вы хотите вникнуть в детали, рекомендуется источник - drupal-x.xx / includes / form.inc ). Построитель использует автоматическую генерацию HTML-атрибута и, таким образом, может по возвращении определить, какая кнопка была нажата, и действовать соответствующим образом (их также можно настроить для запуска отдельных процессов).

Помимо конструктора форм, Drupal разделяет действия по удалению данных на отдельные URL / формы, вероятно, по причинам, указанным в исходном посте. Для этого требуется какой-то шаг поиска / листинга ( стонать в другой форме!, Но удобный для пользователя) в качестве предварительного. Но это имеет то преимущество, что устраняет проблему «отправить все». Большая форма с данными используется по назначению, для создания / обновления данных (или даже для действия «слияния»).

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

Роб Кроутер
источник
0

Используйте iframeдля вложенной формы. Если им нужно разделить поля, тогда ... это не совсем вложено.

Дэн Розенстарк
источник
1
Использование iframe - отличная идея, просто обидно, что в большинстве случаев вам потребуется javascript для изменения его размера (поскольку вы не можете знать, каким будет размер текста пользователя, а следовательно, насколько большой будет кнопка).
Николас Шенкс
@ Николас, как ты можешь использовать Javascript, чтобы изменить его размер? HTML-код iframed не может взаимодействовать с iframe и наоборот, если они не находятся в одном домене.
Дэн Розенстарк
@ Николас, спасибо, просто хотел убедиться, что они должны быть в одном домене
Дэн Розенстарк
0

Мое решение состоит в том, чтобы кнопки вызывали функции JS, которые пишут, а затем отправляют формы из основной формы.

<head>
<script>
function removeMe(A, B){
        document.write('<form name="removeForm" method="POST" action="Delete.php">');
        document.write('<input type="hidden" name="customerID" value="' + A + '">');
        document.write('<input type="hidden" name="productID" value="' + B + '">');
        document.write('</form>');
        document.removeForm.submit();
}
function insertMe(A, B){
        document.write('<form name="insertForm" method="POST" action="Insert.php">');
        document.write('<input type="hidden" name="customerID" value="' + A + '">');
        document.write('<input type="hidden" name="productID" value="' + B + '">');
        document.write('</form>');
        document.insertForm.submit();
}
</script>
</head>

<body>
<form method="POST" action="main_form_purpose_page.php">

<input type="button" name="remove" Value="Remove" onclick="removeMe('$customerID','$productID')">
<input type="button" name="insert" Value="Insert" onclick="insertMe('$customerID','$productID')">

<input type="submit" name="submit" value="Submit">
</form>
</body>
scotweb
источник
-1

Если вы действительно не хотите использовать несколько форм (например, Jason sugests), используйте кнопки и обработчики onclick.

<form id='form' name='form' action='path/to/add/edit/blog' method='post'>
    <textarea name='message' id='message'>Blog message here</textarea>
    <input type='submit' id='save' value='Save'>
</form>
<button id='delete'>Delete</button>
<button id='cancel'>Cancel</button>

И затем в javascript (я использую jQuery здесь для простоты) (хотя это довольно излишне для добавления некоторых обработчиков onclick)

$('#delete').click( function() {
   document.location = 'path/to/delete/post/id'; 
});
$('#cancel').click( function() {
   document.location = '/home/index';
});

Также я знаю, что это сделает половину страницы не работающей без JavaScript.

Пим Ягер
источник
1
Если я ничего не пропустил, это не лучше, чем ссылка на действие удаления: конечным результатом будет запрос GET на действие удаления (плохо). На самом деле это немного грубее, поскольку для выполнения чего-то, что мог бы сделать простой старый тег ссылки, требуется JavaScript.
Кайл Фокс
-1

В ответ на вопрос, размещенный Яром в комментарии к его собственному ответу, я представляю некоторый JavaScript, который изменит размер iframe. В случае кнопки формы можно с уверенностью предположить, что iframe будет в том же домене. Это код, который я использую. Вам придется изменить математические / константы для вашего собственного сайта:

function resizeIFrame(frame)
{
    try {
        innerDoc = ('contentDocument' in frame) ? frame.contentDocument : frame.contentWindow.document;
        if('style' in frame) {
            frame.style.width = Math.min(755, Math.ceil(innerDoc.body.scrollWidth)) + 'px';
            frame.style.height = Math.ceil(innerDoc.body.scrollHeight) + 'px';
        } else {
            frame.width = Math.ceil(innerDoc.body.scrollWidth);
            frame.height = Math.ceil(innerDoc.body.scrollHeight);
        }
    } catch(err) {
        window.status = err.message;
    }
}

Тогда назовите это так:

<iframe ... frameborder="0" onload="if(window.parent && window.parent.resizeIFrame){window.parent.resizeIFrame(this);}"></iframe>
Николас Шэнкс
источник
-1

Я только что придумал хороший способ сделать это с помощью jquery.

<form name="mainform">    
    <div id="placeholder">
    <div>
</form>

<form id="nested_form" style="position:absolute">
</form>

<script>
   $(document).ready(function(){
      pos = $('#placeholder').position();
      $('#nested_form')
         .css('left', pos.left.toFixed(0)+'px')
         .css('top', pos.top.toFixed(0)+'px');
   });
</script>
Mordy
источник
-1

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

<form   
 action="/post/dispatch/too_bad_the_action_url_is_in_the_form_tag_even_though_conceptually_every_submit_button_inside_it_may_need_to_post_to_a_diffent_distinct_url"  
method="post">  

    <input type="text" name="foo" /> <!-- several of those here -->  
    <div id="toolbar">
        <input type="submit" name="action:save" value="Save" />
        <input type="submit" name="action:delete" value="Delete" />
        <input type="submit" name="action:cancel" value="Cancel" />
    </div>
</form>

поэтому на стороне сервера вы просто ищете параметр, который запускает ширину строки "action:", а остальная часть говорит вам, какое действие предпринять

поэтому, когда вы нажимаете кнопку Сохранить, браузер отправляет вам что-то вроде foo = asd & action: save = Save

Лаури Люс
источник
-1

Я обошел проблему, включив флажок в зависимости от того, какую форму человек хотел сделать. Затем использовали 1 кнопку, чтобы отправить всю форму.

Gregws
источник
2
Добро пожаловать в переполнение стека . Лучше описать, как это решает проблему, чем просто сказать, что вы сделали. См. Как ответить для советов по созданию отличных ответов о переполнении стека. Спасибо!
Qantas 94 Heavy
-2

В качестве альтернативы вы можете назначить форму actiob на лету ... может быть, это не лучшее решение, но оно действительно облегчает логику на стороне сервера ...

<form name="frm" method="post">  
    <input type="submit" value="One" onclick="javascript:this.form.action='1.htm'" />  
    <input type="submit" value="Two" onclick="javascript:this.form.action='2.htm'" />  
</form>
Qiniso
источник
Зачем? Я серьезно спрашиваю, потому что нет простого способа узнать, какая кнопка была нажата. Вы говорите, что что-то вроде метода click () jQuery должно использоваться для установки обработчика нажатия кнопки? Или проблема в обработке события click до отправки формы?
Зак Моррис