Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > JavaScript: Наши скрипты > скрипт для сборки данных с формы


Автор: polosatij 30.4.2007, 15:15
в общем, надоело писать одно и то же.. посему сейчас затратил часик и написал скрипт для сборки данных с формы..
просьба посмотреть, высказать мысли, поправить, если кто-то что-то найдёт или просто предложить более лучший вариант:



Код

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Untitled Document</title>
<script language="javascript" type="text/javascript">
    /**
      * id - form id
      * isSetTrue - tol'ko zapolnennye polja!
      */
    function sammeln(id, isSetOnly) {
        var map = new Object();
        var nodes  = document.getElementById(id).childNodes; 
        for (ii in nodes) {
            if (!nodes[ii].nodeName) { continue; }
            
            var name = nodes[ii].nodeName.toLowerCase();
            if ('input' == name && nodes[ii].value.length) { 
                if ('checkbox' == nodes[ii].type.toLowerCase()) {
                    if (isSetOnly) {
                        if(nodes[ii].checked) {
                            map[nodes[ii].name] = 'true';    
                        }
                    }else {
                        map[nodes[ii].name] = nodes[ii].checked;
                    }
                    continue;                    
                }
                
                map[nodes[ii].name] = nodes[ii].value;
                continue;
            }
            
            if ('textarea' == name && nodes[ii].value.length) {
                map[nodes[ii].name] = nodes[ii].value;
                continue;
            }
            
            if ('select' == name) {
                var select_nodes = nodes[ii].childNodes;
                var coll = new Array();

                for (ss in select_nodes) {
                    if (!select_nodes[ss].nodeName) { continue; }
                    if ('option' == select_nodes[ss].nodeName.toLowerCase() && select_nodes[ss].selected) {
                        coll.push(select_nodes[ss].value);
                    }
                }
                
                if(coll.length) { map[nodes[ii].name] = coll; }
                continue;
            }
        }
        
        for (i1 in map) {
            alert(i1 + "= " + map[i1]);    
        }
    }
</script>
</head>

<body>
    <form id="testform" action="">
        <input name="input_text" type="text" size="40" /><br />
        <input name="input_checkbox" type="checkbox" /><br />
        <textarea name="textarea"></textarea><br />
        <select name="select" multiple="multiple" size="2">
            <option>select_example_1</option>
            <option>select_example_2</option>
        </select><br />
        <input name="input_button" type="button" value="absenden" onclick="javascript:sammeln('testform', true)" />
    </form>
</body>
</html>


Автор: Alex_B 30.4.2007, 15:55
переменная ii объявлена глобальной? тогда нельзя будет применить скрипт если на странице более одной формы

Автор: polosatij 30.4.2007, 21:43
вторая версия продукта  smile 

принимая помарки и считая то, что input может не быть node от form имеем:

Код

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Untitled Document</title>
<script language="javascript" type="text/javascript">
    /**
      * id - form id
      * isSetTrue - tol'ko zapolnennye polja!
      */
    function getMapAusForm(id, isSetOnly) {
        var map = new Object();
        // var nodes  = document.getElementById(id).childNodes; 
        var nodes = document.getElementById(id).elements;
        for (var i1 in nodes) {
            if (!nodes[i1].nodeName) { continue; }
            
            var name = nodes[i1].nodeName.toLowerCase();
            if ('input' == name && nodes[i1].value.length) { 
                if ('checkbox' == nodes[i1].type.toLowerCase()) {
                    if (isSetOnly) {
                        if(nodes[i1].checked) {
                            map[nodes[i1].name] = 'true';    
                        }
                    }else {
                        map[nodes[i1].name] = nodes[i1].checked;
                    }
                    continue;                    
                }
                
                map[nodes[i1].name] = nodes[i1].value;
                continue;
            }
            
            if ('textarea' == name && nodes[i1].value.length) {
                map[nodes[i1].name] = nodes[i1].value;
                continue;
            }
            
            if ('select' == name) {
                var select_nodes = nodes[i1].childNodes;
                var coll = new Array();

                for (var i2 in select_nodes) {
                    if (!select_nodes[i2].nodeName) { continue; }
                    if ('option' == select_nodes[i2].nodeName.toLowerCase() && select_nodes[i2].selected) {
                        coll.push(select_nodes[i2].value);
                    }
                }
                
                if(coll.length) { map[nodes[i1].name] = coll; }
                continue;
            }
        }
        
        return map;
    }
    
    function getUrlFromMap(map) {
        var amp = '&';
        var url = '';
        for (var i1 in map) {
            if (map[i1] instanceof Object) {
                for (var i2 in map[i1]) {
                    url += i1 + '=' + map[i1][i2] + amp;
                }
                continue;
            }
            
            url += i1 + "=" + map[i1] + amp;    
        }
        
        url = url.substring(0, url.length - amp.length);
        return url;
    }
</script>
</head>

<body>
    <form id="testform" action="">
        <input name="input_text" type="text" size="40" /><br />
        <input name="input_checkbox" type="checkbox" /><br />
        <textarea name="textarea"></textarea><br />
        <select name="select" multiple="multiple" size="2">
            <option>select_example_1</option>
            <option>select_example_2</option>
        </select><br />
        <input name="input_button" type="button" value="absenden" onclick="javascript:alert(getUrlFromMap(getMapAusForm('testform', true)))" />
    </form>
</body>
</html>



да, кстати, незабудьте, например, про encodeURIComponent smile 

Автор: AKS 2.5.2007, 09:01
polosatij
Глядя на ту часть кода в функции getMapAusForm, которая касается перебора "детишек" в select'е, я понял, что интересуют только option. Зачем тогда браться за перебор коллекции childNodes? Можно ведь сразу "уцепиться" за options - условие в цикле останется одно и будет оно короче...

Автор: polosatij 2.5.2007, 14:29
AKS

да, ты прав, это излишки прошлой версии  smile 

значит так, переправляя строку 41, имеем:

Код

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Untitled Document</title>
<script language="javascript" type="text/javascript">
    /**
      * id - form id
      * isSetTrue - tol'ko zapolnennye polja!
      */
    function getMapAusForm(id, isSetOnly) {
        var map = new Object();
        var nodes = document.getElementById(id).elements;
        for (var i1 in nodes) {
            if (!nodes[i1].nodeName) { continue; }
            
            var name = nodes[i1].nodeName.toLowerCase();
            if ('input' == name && nodes[i1].value.length) { 
                if ('checkbox' == nodes[i1].type.toLowerCase()) {
                    if (isSetOnly) {
                        if(nodes[i1].checked) {
                            map[nodes[i1].name] = 'true';    
                        }
                    }else {
                        map[nodes[i1].name] = nodes[i1].checked;
                    }
                    continue;                    
                }
                
                map[nodes[i1].name] = nodes[i1].value;
                continue;
            }
            
            if ('textarea' == name && nodes[i1].value.length) {
                map[nodes[i1].name] = nodes[i1].value;
                continue;
            }
            
            if ('select' == name) {
                var select_nodes = nodes[i1].options;
                var coll = new Array();

                for (var i2 in select_nodes) {
                    if (!select_nodes[i2].nodeName) { continue; }
                    if ('option' == select_nodes[i2].nodeName.toLowerCase() && select_nodes[i2].selected) {
                        coll.push(select_nodes[i2].value);
                    }
                }
                
                if(coll.length) { map[nodes[i1].name] = coll; }
                continue;
            }
        }
        
        return map;
    }
    
    function getUrlFromMap(map) {
        var amp = '&amp;';
        var url = '';
        for (var i1 in map) {
            if (map[i1] instanceof Object) {
                for (var i2 in map[i1]) {
                    url += i1 + '=' + map[i1][i2] + amp;
                }
                continue;
            }
            
            url += i1 + "=" + map[i1] + amp;    
        }
        
        url = url.substring(0, url.length - amp.length);
        return url;
    }
</script>
</head>

<body>
    <form id="testform" action="">
        <input name="input_text" type="text" size="40" /><br />
        <input name="input_checkbox" type="checkbox" /><br />
        <textarea name="textarea"></textarea><br />
        <select name="select" multiple="multiple" size="2">
            <option>select_example_1</option>
            <option>select_example_2</option>
        </select><br />
        <input name="input_button" type="button" value="absenden" onclick="javascript:alert(getUrlFromMap(getMapAusForm('testform', true)))" />
    </form>
</body>
</html>




Автор: AKS 2.5.2007, 14:44
polosatij, у меня еще такой вопрос. 
В функции getMapAusForm есть два цикла по коллекциям elements объекта формы и options объекта меню выбора. Могут ли в эти коллекции "попасться" безымянные элементы? Т.е. есть ли смысл в условиях, вроде:
Код

if (!nodes[i1].nodeName) { continue; }

Лично я не могу утвержать, что это возможно, или же наоборот - поэтому и спрашиваю.

Автор: polosatij 2.5.2007, 14:55


ммм.. я это сделал, так как не вижу смысла в элементах, у которых нет имени => ведь данные передающиеся по AJAX должны быть хоть как-то названы.. ведь на сервере, их надо как-то спрашивать  smile 

Автор: AKS 2.5.2007, 15:01
polosatij, так вот я и хочу узнать ваше мнение - могут ли в коллекцию элементов формы попасть элементы без имени. Т.е. мне кажется, что если у элемента нет имени тэга, то как он вообще может оказаться среди элементов формы (или меню выбора)?

Автор: 12345c 2.5.2007, 16:02
AKS: насколько помню, элементы без имени так и передаются: ?=12&=34& ...

Автор: AKS 2.5.2007, 16:03
Т.к. для IE надо было заменить цикл for...in, неподходящий для перебора коллекции элементов формы, стал переделывать. Вот что в результате получилось из функции getMapAusForm:
Код

function getMapAusForm(id, isSetOnly) {
    var map = {},
        nodes = document.getElementById(id).elements,
        i1 = 0,
        i2 = 0,
        currentNode, currentOption, options, optArr;
    while (currentNode = nodes[i1++]) {
        switch (currentNode.nodeName.toLowerCase()) {
          case 'input':
              if ('checkbox' === currentNode.type.toLowerCase()) {
                  if (isSetOnly && !currentNode.checked) {
                      break;
                  }
                  map[currentNode.name] = currentNode.checked;
                  break;
              }
              if (currentNode.value.length) {
                  map[currentNode.name] = currentNode.value;
              }
              break;
          case 'textarea':
              if (currentNode.value.length) {
                  map[currentNode.name] = currentNode.value;
              }
              break;
          case 'select':
              options = currentNode.options;
              optArr = [];
              while (currentOption = options[i2++]) {
                  if (currentOption.selected) {
                      optArr.push(currentOption.value
                                    || currentOption.text);
                  }
              }     
              if (optArr.length) {
                  map[currentNode.name] = optArr;
              }
              break;
        }
    }
    return map;
}


Добавлено @ 16:13
12345c, не то я имел ввиду. Условие:
Код

if (!nodes[i1].nodeName)

Т.е. по-русски спрашиваем: "Элемент, а есть ли у тебя имя тэга?", так ведь? Вот я и хочу узнать (сам на 100% не знаю) - а бывают ли в коллекциях элементы без имени тэга?

Автор: 12345c 2.5.2007, 18:21
AKS, nodeName - это совсем не из "коллекции элементов формы" - form.

при проверке "правильные" браузеры сказали, что нет, а IE - да (при пустом имени тег.

Код

<div id=d1 name=dd1></div>
<div id=d1 name=""></div>
<script>onload=function(){alert(typeof(document.getElementsByTagName('div')[1].name));}</script>
А как сделать пустое имя тега и что из этого получится - не пробовал.

Автор: AKS 2.5.2007, 18:39
12345c, ничего не пойму! Что значит:
Цитата

nodeName - это совсем не из "коллекции элементов формы"

?
Взял я, к примеру, коллекцию form.elements. Вот я ее перебираю и спрашиваю каждый элемент коллекции: "Есть у тебя nodeName?" О чем речь? О имени тэга. Правильно?

Автор: DenVdmj 27.1.2009, 15:14
Рылся в гугле, искал код собирающий данные с форм, опять на винграде обнаружилось интересное обсуждение smile
Что забавно, кругом примеры с использованием фрэймворков, джиквери и прототип в основном.
Другие имеют или очень раздутый код или работают некорректно. Код от АКС'а один из самых простых, но тоже не идеален, например:
Код

          case 'select':
              options = currentNode.options;
              optArr = [];
              while (currentOption = options[i2++]) {
                  if (currentOption.selected) {
                      optArr.push(currentOption.value
                                    || currentOption.text);
                  }

нет обнуления i2 для каждого нового  select'а, очевидный недосмотр.

Но на самом деле тут еще большие проблемы, попробуйте выбрать несколько опций (например "select №1, value №1", "select №2, value №2", "select №3, value №3") на такой форме:
Код

    <input type="reset"  value="Сброс" /> <br /><br />

    <input type="text"      disabled="" value="123" name="input_text_disabled"     /><br />
    <input type="text"      value="0" name="input_text"/><br />
    <input type="password"  name="input_password" /><br />
    <input type="file"      name="input_file"     /><br />
    <input type="hidden"    name="input_hidden"   /><br />

    <hr />

    <input type="checkbox"  name="input_checkbox1" />checkbox 1<br />
    <input type="checkbox"  name="input_checkbox2" />checkbox 2<br />
    <input type="checkbox"  name="input_checkbox3" />checkbox 3<br />

    <hr />

    <input type="radio"     name="input_radioA" value="r1" />radio 1<br />
    <input type="radio"     name="input_radioA" value="r2" />radio 2<br />
    <input type="radio"     name="input_radioA" value="r3" />radio 3<br />

    <select name="select1" multiple="multiple" size="3">
        <option value="S1-V1">select №1, value №1</option>
        <option value="S1-V2">select №1, value №2</option>
        <option value="S1-V3">select №1, value №3</option>
    </select><br />

    <select name="select2" multiple="multiple" size="3">
        <option value="S2-V1">select №2, value №1</option>
        <option value="S2-V2">select №2, value №2</option>
        <option value="S2-V3">select №2, value №3</option>
    </select><br />

    <select name="select3">
        <option value="S3-V1">select №3, value №1</option>
        <option value="S3-V2">select №3, value №2</option>
        <option value="S3-V3">select №3, value №3</option>
    </select><br />

    <textarea name="textarea"></textarea><br />

Вызов:
     getUrlFromMap(getMapAusForm('testform', true))
покажет:
    =Сброс&input_text_disabled=123&input_text=0&input_radioA=r3&select1=S1-V1
в то время как Prototype:
    $('testform').serialize()
сериализует так:
    input_text=0&input_password=&input_file=&input_hidden=&select1=S1-V1&select2=S2-V2&select3=S3-V3&textarea=
В чем дело? Правильно ли поступает прототайп, что добавляет, например, пустой input_file? Как написать правильный сериализатор формы?
Ответы для себя нашел тут:
http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2
(перевод: http://pyramidin.narod.ru/html401/forms.html#h-17.13.2, http://www.google.com/search?hl=ru&client=opera&rls=ru&hs=SnQ&q=%22%D0%A1%D0%BA%D1%80%D1%8B%D1%82%D1%8B%D0%B5+%D0%AD%D0%A3+%D0%AD%D0%A3%2C+%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D1%8B%D0%B5+%D0%BD%D0%B5+%D0%BE%D1%82%D0%BE%D0%B1%D1%80%D0%B0%D0%B6%D0%B0%D1%8E%D1%82%D1%81%D1%8F+%D0%B8%D0%B7-%D0%B7%D0%B0+%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BE%D0%BA+%D0%B2+%D1%82%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0%D1%85+%D1%81%D1%82%D0%B8%D0%BB%D0%B5%D0%B9%2C+%D0%BC%D0%BE%D0%B3%D1%83%D1%82+%D0%B1%D1%8B%D1%82%D1%8C+%D0%B4%D0%B5%D0%B9%D1%81%D1%82%D0%B2%D1%83%D1%8E%D1%89%D0%B8%D0%BC%D0%B8%22&btnG=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA&lr=)

После прочтения рекомендаций родился такой вариант (не без оглядки на код АКС'а):
Код

    //
    // Создает объект содержащий successful-controls указанной формы.
    //

    function formToObj(form) {

        var els = form ? form.elements : '', map = {}, el, i = 0;

        while ( el = els[i++] )
            if ( el.name != '' && !el.disabled )    // Элементы без имени и disabled не successful-controls
                switch ( el.type.toLowerCase() ) {
                case 'checkbox':
                case 'radio':                       // Только выбранные (checked) checkbox'ы и
                    if ( el.checked )               // radio-элементы считаются successful-controls
                        map[el.name] = el.value;
                    break;
                case 'select-multiple':
                    var opt = el.options, lst = [], j = 0, o;
                    while ( o = opt[j++] )
                        if ( o.selected )                         // Только выбранные (selected) опции (options)
                            lst[lst.length] = o.value || o.text;  // считаются successful-controls
                    if ( lst.length )                             // Добавляем масссив значений опции если он не пустой
                        map[el.name] = lst;
                    break;
                case 'select-one':                                // select-one добавляем скаляром (не в масссив)
                    if(!el.value) break;
                default:
                    map[el.name] = el.value;
                case 'reset':                                     // reset не отправляется даже если имеет name
                    break;
                };
        return map;
    }

    function buildQueryString(map, amp) {
        // требуется заплатка для движков без forEach (MSIE)
        var url = [], k, v;
        for (k in map) {
            v = map[k];
            ( v instanceof Array ? v : [v] ).forEach(
                function(e){ url.push(encodeURIComponent(k) + '=' + encodeURIComponent(e)) }
            )
        }
        return url.join(amp || '&');
    }



Рекомендациям этот код полностью не соответствует, например должна отправляться только нажатая кнопка submit, и еще всякое по мелочам, но, допустим, в отличии от прототайпа не "отправляется" reset, и в url-строку не добавляются лишние амперсанды. Ну и если посмотреть сколько кода тратят популярные фреймворки на решение одной только этой задачи, то становится просто плохо.

PS. Фрагмент тестового html с формой:
Код

<form
    action="some/action"
    onsubmit="alert( buildQueryString( formToObj(this) ) ); return false" >

    <input type="submit" name="submit" />

    <input type="reset" name="reset" value="Сброс"      />

    <br /><br />

    <input type="text"      disabled="" value="123" name="input_text_disabled"     /><br />
    <input type="text"      value="0" name="input_text"     /><br />
    <input type="password"  name="input_password" /><br />
    <input type="file"      name="input_file"     /><br />
    <input type="hidden"    name="input_hidden"   /><br />

    <hr />

    <input type="checkbox"  name="input_checkbox1" />checkbox 1<br />
    <input type="checkbox"  name="input_checkbox2" />checkbox 2<br />
    <input type="checkbox"  name="input_checkbox3" />checkbox 3<br />

    <hr />

    <input type="radio"     name="input_radioA" value="r1" />radio 1<br />
    <input type="radio"     name="input_radioA" value="r2" />radio 2<br />
    <input type="radio"     name="input_radioA" value="r3" />radio 3<br />

    <select name="select1" multiple="multiple" size="3">
        <option value="S1-V1">select №1, value №1</option>
        <option value="S1-V2">select №1, value №2</option>
        <option value="S1-V3">select №1, value №3</option>
    </select><br />

    <select name="select2" multiple="multiple" size="3">
        <option value="S2-V1">select №2, value №1</option>
        <option value="S2-V2">select №2, value №2</option>
        <option value="S2-V3">select №2, value №3</option>
    </select><br />

    <select name="select3">
        <option value="S3-V1">select №3, value №1</option>
        <option value="S3-V2">select №3, value №2</option>
        <option value="S3-V3">select №3, value №3</option>
    </select><br />

    <textarea name="textarea"></textarea><br />

</form>

<br /><a href="http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2">www.w3.org. Forms. 17.13.2 Successful controls</a>
<br /><a href="http://pyramidin.narod.ru/html401/forms.html#h-17.13.2">Спецификация HTML 4.01. Формы 17.13.2 "Действующие" ЭУ"</a>






Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)