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



我正在尝试动态创建一个表单。我应该有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; 
});
<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>

如何添加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

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

我设计它的方法是在 JS 中有一个名为 "category" 的类(通过使用 constrcutor 函数),这样它就可以包含其他"categories"本身,也许在一个数组中。我会让这个类也包含一个questions数组。现在,不用过多地考虑html,我有一个关于世界样子的漂亮而清晰的模型。在下一步中,我将编写一个函数,该函数可以采用这些category实例之一并生成 dom。此函数drawCategory将使用一个辅助函数,该函数可能是 JS 中的一个内部函数,该函数知道如何为不包含任何类别(换句话说是叶节点)的类别生成 dom,drawCategory智能地使用该辅助函数并返回单个类别实例的整个 dom。接下来,我只是遍历我有多少个类别实例,并不断将类别实例传递给drawCategory它会自动执行正确的操作并每次返回正确的 dom。为了避免多次 dom 更新,我只会为每个类别生成 dom,将其保存在某个地方,只构建一次整个 dom 并将其附加到某个目标元素。

编辑:第二个原型交付。

答案实际上取决于您"动态"创建的内容,我不清楚。 由于在我为 rep 提供有用的答案之前,我不允许要求澄清,这里有一个 jscript 可以做一些有用的事情,就像你所说的那样。

如果我真的自己这样做了,我会集成一个XML文件并完全从JSON或其他东西生成html。 或者至少从 js 填充初始页面。 你在这里看到的解决方案非常草率,并且将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 查找或 jquery。 这是一个带有类别和问题的 jsfiddle。 下一个原型将具有子类别,最终答案将允许您删除没有子类别或问题的类别和子类别。

这个jscript对我来说是一个谜。 当您添加问题时,它们会出现在 html 中的问题之前,即使 $.appendTo() 用于包含的 <\p> 。

最新更新