当添加了另一个问题时,在“单选按钮”上设置了下拉框时,所有 .addrad 按钮都应将输入元素附加到各自的 .q _ div仅将元素附加到最后一个 .q _ div(在此JSFiddle的第25行上确认)。每个添加按钮都应将克隆的输入元素仅附加到这些按钮所属的div。我应该为 .addrad 上的点击事件的选择器添加变量吗?如果我不需要变量,选择器应该是什么样的?我很感激有关如何解决这个问题的任何信息和见解。
JQuery(在JSFiddle上找到的其他项目):
$(document).ready(function () {
var clonedForm = $('form').clone();
var counter = 1;
var counter1 = 1;
var clone = $('#mp_0 div[name^="answer"]');
var clone1 = $('#checkbox_0 div').children().clone();
var clone2 = $('#radio_0 div').children().clone();
var clone3 = $('#choose_0 div').children().clone();
var newqid = $('#content form').length;
$('.removecb').hide();
$('.removeq').hide();
$('.removerad').hide();
$('.removechoose').hide();
$(document).on("click", ".addrad", function(e) {
var idx = $(this).siblings('div[id^="radio_"] div').find('.radio:last').length;
var newRad = $(clone2).clone();
counter++;
$('div[id^="radio_"] div input[type="text"]:last').attr('name', 'radio_'+counter);
$('.removerad').show();
$('[id^="radio_"]:last div').append(newRad);
e.preventDefault();
});
$(document).on("click", ".removerad", function(e) {
if (counter !=1) {
$('input[type="radio"]:last').remove();
$('input[name^="radio_"]:last').remove();
counter--;
}
if (counter == 1) {
$('.removerad').hide();
}
if (counter < 1) {
counter = 1;
}
e.preventDefault();
});
$(document).on("click", ".nextq", function () {
var newForm = $(clonedForm).clone();
counter = 1;
counter1++;
$('.done, .nextq, .removeAnswer, .removeq').hide();
$('#content').append('<hr>');
$('#content').append(newForm);
$('[class^="q_"]:last b.qnum').empty().append(counter1 + '. ')
$(newForm).children(".group").hide();
$(newForm).children("#text").show();
$('form:last').find('.select_0').attr('class', 'select_' + newqid);
$('[class^="q_"]:last').attr('class', 'q_' + newqid);
$('form:last').find('#mp_0').attr('id', 'mp_' + newqid);
$('form:last').find('#checkbox_0').attr('id', 'checkbox_' + newqid);
$('form:last').find('.done:last, .nextq:last, .removeq:last').show();
return false;
});
$(document).on("click", ".removeq", function (e) {
var newqid = $('form:last[name^="question"]:last').index() - 1;
counter1--;
$('hr:last').remove();
$('form[name^="question"]:last').remove()
$('[class^="q_"]:last input, [class^="q_"]:last textarea, [class^="q_"]:last select, [class^="q_"]:last button, [class^="q_"]:last option').prop('disabled', false);
$('form:last').find('.done, .nextq, .removeq').show();
newqid -1;
$('[type="checkbox"]').prop('disabled', true);
e.preventDefault();
if (counter1 == 1) {
$('.removeq').hide();
}
});
$('.group').hide();
$('#text').show();
$(document).on("change", "select", function () {
counter = 1;
$(this).parent().parent().siblings('.group').hide();
$(this).parent().parent().siblings('[id^="' + $(this).val() + '"]:last, [class^="' + $(this).val() + '"]').fadeIn();
});
});
答案 0 :(得分:6)
这是一个古老的背景问题。当构建可以包含重复部分的复杂接口时,最好提出一种设计,将每个部分视为整个实体 - 在内部表示和管理每个新部分的可创建的js / dom对象 - 然后您有一个中心&#34 ;上下文&#34;是this
。这样,您只需在需要时实例化这些对象,并将它们作为一个整体添加到页面中。你会发现它让生活变得更加轻松。
可悲的是,对于许多JavaScript项目,您都没有看到这一点。主要是因为事物是以思想的速度构建的,因为JS具有高度的草图性。你倾向于看到的是你所拥有的,它是一些基于上下文的jQuery,一些基于全局的jQuery,一些基于id的选择器,一些全局变量和一些存储的dom对象的混合。上下文的混合 - 这是一个阅读,理解和扩展的难题。
我建议你修改你的方法,并设计一个对象来表示你想要创建的每个部分,即RadioButtonSection
,TextSection
,MultipleChoiceSection
等等。每个对象将负责以本地方式创建,管理和删除它自己的子部分。本地化我的意思是依赖于class
选择器,$(this)
或this
上存储的DOM元素,而不是使用id
,全局jQuery选择器,全局变量或特定DOM结构
var RadioButtonSection = {
create: function(){
var obj = Object.create(this);
obj.element = $('<div class="radio-button-section" />');
return obj;
},
addRadio: function(){
this.element.find('.radio-list').append('<input type="radio" />');
/// ... and so on
return this;
},
removeRadio: function(){
/// ... and so on
return this;
},
addToPage: function( target ){
$(target).append( this.element );
return this;
},
removeFromPage: function(){
this.element.remove();
return this;
}
};
为什么这样更好的原因是你划分你的代码,这使得它更容易阅读,你将页面的每个新部分视为一个连贯的对象(而不是一些元素),也意味着你可以开始概括并重用功能。
例如,要创建新的单选按钮区域,只需调用:
var rbs = RadioButtonSection.create();
rbs.addToPage( /* target-element-or-selector-here */ );
然后,您可以使用此对象来管理新区域。此对象添加到页面的任何GUI元素都应在本地工作,因此您不必担心创建或销毁的数量。
如果您更喜欢prototype
方法,则可以使用此模型:
var RadioButtonSection = function(){
if ( this instanceof RadioButtonSection ) {
this.element = $('<div class="radio-button-section" />');
}
else {
return new RadioButtonSection();
}
};
RadioButtonSection.prototype.addRadio = function(){
this.element.find('.radio-list').append('<input type="radio" />');
/// ... and so on
return this;
};
RadioButtonSection.prototype.removeRadio = function(){
/// ... and so on
return this;
};
RadioButtonSection.prototype.addToPage = function( target ){
$(target).append( this.element );
return this;
};
RadioButtonSection.prototype.removeFromPage = function(){
this.element.remove();
return this;
};
您将使用:
var rbs = new RadioButtonSection()
.addToPage( /* target-element-or-selector-here */ )
;
如果没有上述内容 - 因为这意味着需要进行大量的重写 - 您至少应该设计您的函数和事件监听器,以尊重当前的jQuery托管 - DOM上下文,即$(this)
并从中解决。这样你就不会遇到现在的问题。
下面是使用单击按钮作为上下文重写您的函数的示例。
$(document).on("click", ".addrad", function(e) {
counter++;
e.preventDefault();
var context = $(this);
var group = context.closest('.group');
var insert = group.find('div:first');
var newRad = $(clone2).clone();
newRad.find('input[type="text"]:last').attr('name', 'radio_'+counter);
group.find('.removerad').show();
insert.append(newRad);
});
例如,我已经使用了$('.removerad')
全局选择器并将其替换为group.find('.removerad').show();
,它只会找到本地删除按钮,而不是全部页面范围内的删除按钮。在构建复杂的JavaScript UI(本地而非全局)时,您始终需要思考这一点。
用这个&#34;本地&#34;通过给任何包装<div>
一个特定的分组类,你可以让事情变得更容易,这样你就不必依赖.find('div:first')
这样的东西,因为如果你修改你的东西就容易破坏GUI。
在您重写方法以使用基于本地上下文的思维之前,您会发现会出现许多其他奇怪的错误,这些错误都会因为您的代码混合了本地和全局行为而不应该这样做是
关于'radio_' + counter
无效的好点。我没有发现你的代码在其他地方执行以下操作:
var clone2 = $('#radio_0 div').children().clone();
这会以一种意想不到的方式影响newRad
jQuery集合。 .find()
旨在从父级到子级工作,但.children()
方法将返回单独元素的列表,与父级关系分离。
因此,计数器代码的一个修复是使用.filter()
:
newRad.filter('input[type="text"]:last').attr('name', 'radio_'+counter);
但也许更好的是修改var clone2
使用:
var clone2 = $('#radio_0 div').clone();
..并将输入及其包装div插入,并继续使用.find()
。这引出了我的下一个问题。
而不是使用类似的东西:
group.closest('[type="radio"]:last').remove()
每当插入多个元素时,最好使用HTML的强大功能并给它们一个包装器。然后,您可以通过删除包装器将它们全部删除。我建议您修改clone2
,如上所述,给包装div一个类,然后将其作为删除目标。
如果你这样做,那就意味着你还必须修改插入输入的方式,所以包装Divs会一个接一个地插入,如下所示:
insert.after(newRad);
是的,全球counter
会给你带来麻烦。在这些情况下,您最好使用jQuery和DOM的强大功能来跟踪您的计数。例如 - 取自您的.addrad
方法:
e.preventDefault();
var context = $(this);
var group = context.closest('.group');
var items = group.find('div'); // it might be best to give these DIVs a class
var insert = items.filter(':last');
var newRad = $(clone2).clone();
group.find('.removerad').show();
insert.after(newRad);
newRad.find('input[type="text"]:last').attr('name', 'radio_' + items.length);
在上面的代码片段中,我使用items
集合的长度计算出用于新无线电的数字。您也可以为删除功能执行类似的操作。这比手动计数器更好的原因是因为DOM会跟踪你,如果其他代码是 - 无论出于什么原因 - 删除一个项目;您的计数会自动更正,因为每次都会重新计算。
为了方便起见,我在此处添加radio-items
课程:
<div id="radio_0" class="group">
<div class="radio-items">
<input disabled="disabled" type="radio" />
<input class="radio" name="radio_0" type="text" />
</div>
<button type="button" class="addrad">Toevoegen</button>
<button type="button" class="removerad">Verwijderen</button>
</div>
然后找出有多少radio-items
,你可以在任何时候运行:
group.find('.radio-items').length