Як ви подолаєте обмеження введення HTML-форми?


199

Я знаю, що XHTML не підтримує вкладені теги форм, і я вже читав тут інші відповіді на "Переповнення стека" щодо цієї теми, але я все ще не знайшов елегантного рішення проблеми.

Деякі кажуть, що вам це не потрібно, і що вони не можуть придумати сценарій, якщо це було б потрібно. Ну, особисто я не можу придумати сценарій, якого я не мав потрібен.

Давайте подивимось дуже простий приклад:

Ви створюєте додаток для блогу, і у вас є форма з деякими полями для створення нової публікації та панелі інструментів з "діями", такими як "Зберегти", "Видалити", "Скасувати".

<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-адреса дії визначена в тезі форми, а не в кожній окремій кнопці подання, наш єдиний варіант - опублікувати загальну URL-адресу, а потім запустити "якщо ... то ... інше", щоб визначити назву кнопки, яка було подано. Не дуже елегантний, але єдиний наш вибір, оскільки ми не хочемо покладатися на JavaScript.

Єдина проблема полягає в тому, що натискаючи кнопку "Видалити", на сервер будуть подані ВСІ поля форми, хоча єдине, що потрібно для цієї дії, - це прихований вхід з повідомленням post-id. У цьому маленькому прикладі не дуже велика справа, але в моїх додатках LOB є форми із сотнями (так би мовити) полів і вкладок, які (через вимоги) повинні подавати все за один раз, і в будь-якому випадку це здається дуже неефективним і відходи. Якщо підтримка вклади форми підтримується, я, принаймні, зможу загорнути кнопку "Видалити" для надсилання всередину власної форми лише з полем "ідентифікатор".

Ви можете сказати "Просто реалізуйте" Видалити "як посилання, а не надсилати". Це було б помилково на багатьох рівнях, але найголовніше, оскільки дії побічних ефектів, такі як "Видалити" тут, ніколи не повинні бути GET-запитом.

Тож моє запитання (особливо до тих, хто каже, що їм не потрібна форма гніздування): «Що ви робите? Чи є якесь елегантне рішення, яке мені не вистачає, або підсумок справді "Або вимагати JavaScript, або надіслати все"?


17
Це смішно, але XHTML дає можливість непрямої форми гніздування відповідно до цікавої примітки anderwald.info/internet/nesting-form-tags-in-xhtml - (X) HTML забороняє вкладати такі форми, як "форма> форма", але дозволяє "форма> набір полів> форма ", W3 валідатор каже, що він дійсний, але у браузерах є помилки з таким вкладом.
Ніші


виправити посилання на коментар @ Ніші: посилання: web.archive.org/web/20170420110433/http://anderwald.info/…
Альберт

Відповіді:


53

Я реалізував би це саме так, як ви описали: подайте все на сервер і зробіть просту функцію if / else, щоб перевірити, яку кнопку натиснули.

І тоді я би застосував дзвінок Javascript, пов'язуючи з подією на підписку форми, яка перевіряла б ще до подання форми, і лише надсилала відповідні дані на сервер (можливо, через другу форму на сторінці з ідентифікатором, необхідним для обробки речі як прихований ввід, або оновіть розташування сторінки за допомогою даних, які вам потрібно передавати як GET-запит, або виконайте публікацію в Ajax на сервері або ...).

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

Крім того, якщо ви працюєте за корпоративним брандмауером чи іншим, і у всіх є відключений Javascript, ви можете зробити дві форми і виконати деякі магії CSS, щоб виглядати, як кнопка видалення є частиною першої форми.


15
Оригінал ОП заявив, що немає JavaScript
Джейсон

26
Проблема цього методу полягає в тому, що він є НЕ ВІДПОВІДНИМ. Збереження та видалення відповідають різним діям на ресурсі: "Зберегти" -> POST / my_resource (створити новий ресурс) "Зберегти" -> PUT / my_resource (змінити існуючий ресурс) "Видалити" -> DELETE / my_resource (знищити ресурс) На жаль, проблема полягає в тому, що POST повинен створити новий ресурс. Звичайно, він ніколи не повинен видаляти наявний ресурс.
користувач132447

177

Я знаю, що це старе питання, але 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. Недоліком є ​​те, що вони не підтримуються у старих веб-переглядачах, тому вам доведеться виконати поліфайл для старих браузерів.


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
ну, це завжди баланс між тим, що ти хочеш зробити, і тим, що ти можеш зробити. схоже, що це варіанти - або одна форма, яка обробляється на сервері, або одна чимала форма, яку важко підтримувати - вибирайте з розумом :)
Джейсон,

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 порожній, незалежно від введених даних. Внутрішні форми, здається, працюють добре скрізь.

Ще не перевірена інша описана пропозиція - набір полів навколо вкладених форм.

EDIT : Frameset не працював! Я просто додав основну форму після інших (більше немає вкладених форм) і використав "клон" jQuery для дублювання входів у формі при натисканні кнопки. Додано .hide () до кожного з клонованих входів, щоб зберегти макет незмінним, і тепер він працює як шарм.


Це прекрасно працювало для мене. Я працював на сторінці asp.net, яка мала всеохоплюючу форму. У мене була внутрішня вкладена форма, яку слід використовувати для плагіна jQuery Validation ( github.com/jzaefferer/jquery-validation ), і хоча вона працювала ідеально у FireFox та IE, вона не вдалася в Chrome із "TypeError: Cannot call method 'element" з невизначений '. Виявилося, що виклик ініціалізації validate () виявився невдалим, оскільки він не міг знайти внутрішню форму, оскільки Chrome видалив його з блоку доданого HTML за допомогою jQuery.html (). Додавання невеликої манекенової форми спочатку змусило Chrome залишити справжню.
Джон - Не число

Схоже, це більше не працює. Firefox зняв другий тег <form>, а потім припинив мою форму з першим тегом </form>. Це залишило недійсний тег </form> внизу сторінки та форму, яка не надсилатиметься належним чином. :(
Таркін

Дуже корисно для мене. Я додаю веб-віджет, який містить форму, на сторінку asp.net. Здивує WebForm, що він завжди генерує форму, що охоплює все в тілі HTML. Форма, що додається, стає головним болем для веб-віджета. Форма пустушки здається швидким та ефективним рішенням цієї ситуації. Він ідеально працює в Chrome і Safari досі, ще не тестував Firefox. Дякую за те, що поділилися, врятували мені багато часу!
Дж. М. Ян

4

Я думаю, що Джейсон має рацію. Якщо ваша дія "Видалити" є мінімальною, переконайтеся, що вона є самою формою, і сумістіть її з іншими кнопками, щоб інтерфейс виглядав як одна уніфікована форма, навіть якщо її немає.

Або, звичайно, перепроектуйте свій інтерфейс, і дозвольте людям видалити десь ще цілком, що не вимагає, щоб вони взагалі бачили цю форму.


2

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

  • Оновлення
  • Видалити
  • Що б там не було

Тоді сценарій дій вживав би різні дії залежно від значення встановленого перемикача.

Іншим способом було б розмістити дві форми на сторінці, як ви запропонували, просто не вклавшись. Макет може бути важко контролювати, хоча:

<form name = "editform" action = "washing_url" метод = "повідомлення">
   <input type = "hidden" name = "task" value = "update" />
   <type type = "text" name = "foo" />
   <input type = "submit" name = "save" value = "Зберегти" />
</form>

<form name = "delform" action = "washing_url" метод = "повідомлення">
   <input type = "hidden" name = "task" value = "delete" />
   <input type = "hidden" name = "post_id" value = "5" />
   <type type = "submit" name = "delete" value = "Delete" />
</form>

Використовуючи приховане поле "завдання" в сценарії обробки, щоб відповідне розгалуження.


1

Ця дискусія все ще мене цікавить. За початковим повідомленням стоять "вимоги", які, схоже, поділяють ОП, тобто форма із зворотною сумісністю. Як хтось, чиї роботи під час написання письмово повинні інколи підтримувати IE6 (і на наступні роки), я це викопую.

Не висуваючи рамки (всі організації хочуть запевнити себе у сумісності / надійності, і я не використовую це обговорення як обґрунтування рамки), рішення Drupal для цього питання цікаві. Drupal також має безпосереднє значення, тому що в рамках давно проводилася політика "вона повинна працювати без Javascript (тільки якщо ви хочете)", тобто питання ОП.

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

Крім конструктора форм, Drupal розбиває дії "видалення" на окремі URL-адреси / форми, ймовірно, з причин, зазначених у вихідному дописі. Для цього потрібен певний крок пошуку / списку ( стогне інша форма!, Але це зручно для користувачів) як попередній. Але це має перевагу усунення проблеми "подати все". Велика форма з даними використовується за цільовим призначенням, створенням / оновленням даних (або навіть "об'єднанням").

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


0

Використовуйте iframeдля вкладеної форми. Якщо їм потрібно поділитися полями, то ... це насправді не вкладено.


1
Використання iframe - чудова ідея, шкода, що в більшості випадків вам знадобиться javascript, щоб змінити його розмір (оскільки ви не можете знати, яким буде розмір тексту користувача, отже, наскільки великою буде кнопка).
Ніколас Шенкс

@Nicholas, як ви можете використовувати Javascript, щоб змінити його розмір? Iframed HTML не може спілкуватися з iframe, і навпаки, якщо вони не на одному домені.
Дан Розенстарк

@Nicholas, дякую, просто хотів переконатися, що вони повинні бути в одному домені
Dan Rosenstark

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>

-1

Якщо ви дійсно не хочете використовувати декілька форм (як Jason Джейсон), тоді використовуйте кнопки та 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>

-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 = Зберегти


-1

Я вирішив проблему, включивши прапорець залежно від того, яку форму людина хотіла зробити. Тоді використовується 1 кнопка для подання всієї форми.


2
Ласкаво просимо до переповнення стека . Краще описати, як це вирішує проблему, а не просто говорити, що ти зробив. Див. Як відповісти, щоб отримати поради щодо створення чудових відповідей на переповнення стека. Дякую!
Qantas 94 Важкий

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

Чому? Я питаю серйозно, тому що немає простого способу отримати, яку кнопку було натиснуто. Ви говорите, що для встановлення обробника onclick кнопки слід використовувати щось на зразок методу jQuery click ()? Або проблема з обробкою події клацання перед надсиланням форми?
Зак Морріс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.