如何使用JavaScript在层次结构上动态添加控件?

时间:2015-01-05 00:43:09

标签: javascript jquery forms

我正在尝试动态创建表单。我应该categoriessubcategoriesquestions。这是一个简单的示例,我将动态添加问题元素:



var i = 1; // to hold increment 

$('#add').click(function() {
    var p = $(this).closest('p'),
        i = $(p).length;
    
    $(p).before('<p> <label> Question ' + i + ': <input type="text" id="question_' + i + '"> </label> </p>');    
    return false; 
}); 

$('#del').click(function() {
   
    $("p label").last().remove();    
    i -= 1;
    return false; 
});
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
<div>
    <p> <label> Question 1: <input type="text" id="question_1"> </label> </p>
    <p> <a href="#" id="add"> Add Question </a>  </p> 
        <p>  <a href="#" id="del"> Del Question </a> </p> 
</div>
&#13;
&#13;
&#13;

如何添加categories,以便category可以包含多个subcategories,而这些subcategories也可以包含Test Form Investment // a category Real Estate // a subcategory How much was spent ? // a question What is the time frame ? // a question Automobiles // a subcategory How many vehicles ? // a question What is the total? // a question Charity // a category How much was spent ? // a question Donations // a subcategory When started ? // a question Other // a subcategory What is the timeframe ? // a question 。每个类别也应该能够包含问题。所以我的表单看起来像这样:

{{1}}

我目前的代码只会产生问题。我试图创建类别,但陷入了递归错误。我试着阅读jtree API,但我不确定它是我需要的。我还试图找到一个允许创建这样一个表单的网站,以便我可以看看他们的JavaScript,但找不到这样的网站。有人可以帮我理解如何实现这个目标吗?

2 个答案:

答案 0 :(得分:3)

编辑:交付第二个原型。

答案实际上取决于你正在创造的动态&#34;,我不清楚。因为在我为代表提供有用的答案之前,我不允许要求澄清,所以这里有一个jscript做了一些有用的事情,就像你说的一样。

如果我自己真的这样做,我会整合一个XML文件并完全从JSON或其他东西生成html。或者至少从js填充初始页面。你在这里看到的解决方案非常邋and,并将html紧密地耦合到js。

http://jsfiddle.net/P8X3B/109/(原型问题加法器,其他控件存在,未实现)

新:http://jsfiddle.net/y29vc5k0/28/(原型问题和类别加法器)

/**
 ***
 * questions and categories example
 * by Jason D'Aquila
 * 23 Jan 2015
 * created as prototype of answer to stackoverflow question posted at: http://stackoverflow.com/questions/27772009/
 *
 * relies on at least jQuery 1.4 ; browsers supporting getter definitions with Object.defineProperty
 */

/* GLOBAL */
function cleaner(text) {
    var s = text.replace(/(<|>)/g, '\\$1')
        .replace(/ /g, '_')
        .replace(/[!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, '');
    return s; //can't split a return line
}

/* injective function jQuery objs => String; compact output
 * not actually achieved, but this function isn't called in this program anyway
 */
function injectJQueryObjToStr(jqueryObj) {
    return ("" + jqueryObj.length) + jqueryObj.attr("id") ? " id: " + jqueryObj.attr("id") : jqueryObj;
    //can definitely improve this function
}

canon = ({
    /* contract:  No enumerable property of canon has the name of an html tag
     */
    outputField: $('#out'),
    categoriesList: $('#categories'),

    /* cannot actually canonize this; see below */
    //questionsDropdown:  (function () {  //references must be invocations ofc
    //    return $('#questions_del');
    //}),

    init: function (undef) {
        //*  //single slash comment toggle
        //this.questionsDropDown = (function(nothing) {return nothing;}());
        Object.defineProperty(this, "questionsDropdown", {
            //cannot actually canonize this
            //a setter is only way to "store" a function lookup used with variable access syntax
            configurable: true,
            enumerable: true,
            get: function () {
                return $('#questions_del');
            }
        });
        //*/
        this.init = undef;
        return this;
    }
}).init(); //self-initializing object canon


/*  CLOSURES */
/* referencing contexts:
 * A -- the anonymous function in $('#add') .click 
 * B -- the anonymous function in $('#cat') .click
 */
//referred by:  A, B
var addCategoryIfNotExists = function (desiredName) {
    var category_in = desiredName;
    var f = cleaner;
    //var FF = _compose_ function(x){return 'cat_'+x; } @ cleaner

    if ($('#cat_' + f(category_in)).length) {
        return $('#cat_' + f(category_in));
    } else {

        $("<p></p>").attr({
            id: 'cat_' + f(category_in)
        }).html('<label class="cat_' + f(category_in) + '">' + f(category_in) + '</label>').prependTo(canon.outputField);
        //another option is .clone()

        canon.categoriesList.append($('<option value="' + f(category_in) + '" />'));
        return $('#cat_' + f(category_in));
    }
};

function inputFieldAt(locale) {
    //return $('input', $(locale).closest('p'));
    return $(locale).closest('p').find('input');
}
//consts
var QUESTION_PARENT_ELEMENT_TYPE = "p"; //ideally a preprocessor subs this

/* /CLOSURES */

$('#add').click(
//create closure for i=question #
(function () {
    var i = 1;

    return function () {
        var qid, qidlitl;
        var category_input;
        i = i + 1;
        qidlitl = 'question_' + i;
        qid = '"question_' + i + '"'; //quotes for HTML attr setting

        var category_el;
        //*  //single-slash comment toggle
        //category_input = $('input', $(this).closest('p')).val();
        category_input = inputFieldAt(this).val();
        category_el = addCategoryIfNotExists(category_input);
        //check category_el === canon.outputField.find('#' + 'cat_' + cleaner(category_input) ) 
        /*/
        category_el = document.getElementById("out");
        //*/

        $('<' + QUESTION_PARENT_ELEMENT_TYPE + '></' + QUESTION_PARENT_ELEMENT_TYPE + '>').html('<label for=' + qid + '> Question ' + i + ': </label><input type="text" id=' + qid + '>').appendTo(category_el);
        $("<option></option>").attr({
            "class": "questions_options",
            value: qidlitl
        }).text('Question ' + i + '').appendTo(canon.questionsDropdown);

        return false; //callback contract

    };

})() //SIF to get closure for i = 1 + number of questions ever generated

); //$('#add').click

$('#del').click(function () {
    var qselect = canon.questionsDropdown[0]; //This [0] is the inelegance of mixing frameworks
    $('#' + qselect.options[qselect.selectedIndex].value + '')
        .closest(QUESTION_PARENT_ELEMENT_TYPE).remove();
    qselect.remove(qselect.selectedIndex);

    return false;
});

$('#cat').click(function () {
    //add category and return false exit signal unless add_category returned literal not false (i.e. true)
    var category_input;
    //category_input = $('input', $(this).closest('p')).val();
    category_input = inputFieldAt(this).val();
    var res = addCategoryIfNotExists(category_input);

    //return !!(res && (res === true));  //!! might get eliminated by compiler?
    return res && (res === true) ? true : false; //equality < logical AND < ternary
});

//EOF

html稍有改动。见jsfiddle。

所以,几周后,我了解到你实际上无法规范化大多数DOM查找或jqueries。这是一个有类别和问题的jsfiddle。下一个原型将具有子类别,最终答案将允许您删除没有子类别或问题的类别和子类别。

这个jscript中有一个谜。当您添加问题时,它们会出现在html中的问题之前,即使在包含&lt; \ p&gt;的情况下使用$ .appendTo()也是如此。

答案 1 :(得分:3)

我设计这个的方法是在JS中使用一个名为"category"的类(通过使用constrcutor函数),这样它可以在其自身中包含其他"categories",也许在数组中。我会让这个类还包含一个questions数组。现在,在不考虑html的情况下,我对世界的外观有一个很好的清晰模型。在下一步中,我将编写一个函数,可以使用其中一个category实例并生成dom。这个函数drawCategory将使用一个辅助函数,它可能是JS中的一个内部函数,它知道如何为不包含任何类别的类别生成dom(换言之,叶子节点),drawCategory智能地使用该辅助函数并返回单个类别实例的整个dom。接下来,我简单地迭代我拥有的类别实例的数量,并继续将类别实例传递给drawCategory,它自动执行正确的操作并每次返回正确的dom。为了避免多个dom更新,我只需为每个类别生成dom,将其保存在某处,只需构建一次整个dom并将其附加到某个目标元素。