Selecting dynamically added elements by class name - javascript

I have am HTML code which for simplicity looks like this:
<div class="main-container">
<div class="group-area group1" id="group1">
<select class="slct" id="slct1">
<option>Group A</option>
<option>Group B</option>
<option>Group C</option>
</select>
<div class="participant-area">
<!-- empty, can be filled with "<div class='participant'></div>" elements -->
</div>
</div>
<button class="add-group">Show another group</button>
</div>
In the above inteface, the user can select the name of the group from the select drop down, and the the participants of that group will be shown in the 'participant-area'. They will be drawn from a presaved list, and will be added using jQuery append:
<script>
$(document).on('change', '.slct', function() {
var number = $(this).attr("id").charAt(4); //gets the number '1' from the id name
var key = $(this).find("option:selected").val(); //gets the value to be used later
var constructedClass = ".group" + number; //result: "group1"
presavedList.forEach(participant => {
$(constructedClass + " .participant-area") //selecting participant area that is inside group1
.append($("<div>").addClass("participant")
.append($("<h2>").text(participant.name))
);
}
})
</script>
However, user can also click on the 'add-group' button at the end of the main container, and have another area just like the first one displayed, that can be used to see participants of a different group. But this time, the classes will be group2 instead of group1, slct2 instead of slct1, and so on. This is done by having a global variable that is incremented whenever the button is clicked:
<script>
var areaNumber = 1;
$(".add-group").click(function () {
areaNumber++;
$(".main-container")
.append($("<div>").addClass( "group"+areaNumber).addClass("group-area").attr("id", "group"+ areaNumber)
.append($("<select>")) //etc... Reconstruct the same one as original
.append($("<div>")) //etc... Reconstruct the same one as original
});
</script>
My problem is related selecting the groupN class of the dynamically created elements (like group2, group3, etc). In the first function above - after a second area has been created and its select value changed - the change is being detected normally and the $(document).on('change', '.slct', function() {...}) is being fired normally. However, the 5th line in that function:
$(constructedClass + " .participant-area").append(//etc)
is not working: the constructedClass is not being detected by the function, even though it exists in the time of firing it - but I believe it's not being detected because it was not present at the time of initial parsing of javascript. Is that correct? Is there any way to solve this? (Be able to select dynamically generated elements by their uniquely generated class names?).
Thank you for reading this far and for any help you can offer.

Do not use incremental id and class attributes. It is an anti-pattern. It makes your code needlessly complex, more verbose, and difficult to maintain.
A much better solution is to group common elements by behaviour using a single class attribute. That way you can use DOM traversal to relate them to each other. It also allows you to clone() content (as it's all identical) without the need to spaghetti-fy your JS by filling it with HTML.
With that said, try this:
let presavedList = [{ name: 'Foo bar' }, { name: 'Lorem ipsum' }]
$(document).on('change', '.slct', function() {
var html = presavedList.map(item => `<div class="participant"><h2>${item.name}</h2></div>`);
$(this).next('.participant-area').html(html);
});
$(".add-group").click(function() {
var $clone = $('.group:first').clone();
$clone.find('select').val('');
$clone.find('.participant-area').empty();
$clone.appendTo('.main-container');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="main-container">
<div class="group-area group">
<select class="slct">
<option value="">Please select...</option>
<option>Group A</option>
<option>Group B</option>
<option>Group C</option>
</select>
<div class="participant-area"></div>
</div>
<button class="add-group">Show another group</button>
</div>

Related

When registering both onchange and onclick on a select, the click event is triggered twice

Goal: Have a select whose option have nested structure when user clicks on the select, but when user selects an option the option should be displayed "normally" (ie with no leading spaces).
Attempted solution using JS and Jquery: My JS is far from sophisticated so I apologize in advance :)
I attempted to use .on("change") and .on("click") to change the selected option value (by calling .trim() since I achieve the "nested" structure with ). I'm also storing the original value of the selected option because I want to revert the select menu to its original structure in case the user selects another option.
The problem: The function registered for .on("click") is called twice, thus the select value immediately resets itself to its original value.
I suspect there is a much, much easier solution using CSS. I will be happy to accept an answer that will suggest such solution.
JSFiddle: https://jsfiddle.net/dv6kky43/9/
<form>
<select id="select">
<option value=""></option>
<option value="a"> a</option>
<option value="b"> b</option>
</select>
</form>
<textarea id="output"/>
var orig;
var output = $("#output");
output.val("");
function onDeviceSelection(event){
output.val(output.val() + "\nonDeviceSelection");
var select = event.target;
orig = select.selectedOptions[0].text;
select.selectedOptions[0].text = select.selectedOptions[0].text.trim()
}
function resetDeviceSelectionText(event) {
output.val(output.val() + "\nresetDeviceSelectionText");
var select = event.target;
if (orig !== undefined){
select.selectedOptions[0].text = orig;
}
}
$("#select").on("change", onDeviceSelection);
$("#select").on("click", resetDeviceSelectionText);
If you are already using jQuery, why not utilize data function to store the original value. This way you will also be able to specify different nest levels.
(function($){
$(document).on('change', 'select', function(event) {
$(this).find('option').each(function(index, element){
var $option = $(element);
// Storing original value in html5 friendly custom attribute.
if(!$option.data('originalValue')) {
$option.data('originalValue', $option.text());
}
if($option.is(':selected')) {
$option.html($option.data('originalValue').trim());
} else {
$option.html($option.data('originalValue'));
}
})
});
})(jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form>
<select id="select">
<option value=""></option>
<option value="a"> a</option>
<option value="b"> b</option>
</select>
</form>
Once caveat I see is, the selected option will appear trimmed on the list as well, if dropdown is opened after a previous selection has been made:
Will it still work for you?
Instead of keeping the state of the selected element i would simply go over all options and add the space if that option is not selected:
function onDeviceSelection(event){
// Update textarea
output.val(output.val() + "\nonDeviceSelection");
// Higlight the selected
const {options, selectedIndex} = event.target;
for(let i = 0; i < options.length; i++)
options[i].innerHTML = (i === selectedIndex ? "":" ") + options[i].text.trim();
}
$("#select").on("change", onDeviceSelection);
Note that you need to use innerHTML to set the whitespace...

jQuery - Each loop to select all items by data attribute, but also dependent on select (option) values

I am in the process of building an e-Commerce shop and have hit a small bump in the road for my actual product page. Based on any product options set that would add to the price if selected, I would like to be able to update the price on the page live when these options have been added. I have managed to iterate through every element with a "data-price-effect" attribute attached to them, HOWEVER, when it comes to a select element, I would need to check if the item is selected as an option, each option has their respective price change attribute of course, but the value would only update to the actual select element.
Here is my code upto now:
function updatePrice(){
$('[data-price-effect]').each(function( index ) {
// do something
});
}
Basic HTML set-up to explain further:
<form>
<input type="text" name="foo" onchange="updatePrice();" data-price-effect="10.00" />
<select name="bar" onchange="updatePrice();">
<option selected value="Item1" data-price-effect="5.00">Item 1</option>
<option selected value="Item2" data-price-effect="8.00">Item 2</option>
<option selected value="Item3" data-price-effect="10.00">Item 3</option>
</select>
</form>
I have NO idea how to even logically do this, not even with some huge messy code. Any pointers here from someone more experienced with Javascript?
Instead of having "updatePrice()" on each element, you could have a listener for all form elements for the function:
var EffectElements = $('form input[data-price-effect], form select');
EffectElements.on('change', function() {
var PriceEffect = 0;
EffectElements.each(function() { // Loop through elements
if ($(this).is('select')) { //if this element is a select
$(this).children().each(function() { //Loop through the child elements (options)
if ($(this).is(':selected')) { //if this option is selected
PriceEffect += parseFloat($(this).attr('data-price-effect'));
}
});
} else {
PriceEffect += parseFloat($(this).attr('data-price-effect'));
}
});
});
You could then use the PriceEffect variable to update your price on the website.
Ultimately it's the IS function doing the dirty work you needed ~_o
Working Example

Getting a Select Value after appending

This is for javascript and jquery.
I have in my body...
<select id="option1_select" name="courseCodeSelectName">
<option></option>
<option>Word1</option>
<option>Word2</option>
</select>
<script>
$("select").change(function () {
functionLoadOpt2() }).trigger("change" );
</script>
<select id="option2_select" name="courseNumSelectName">
<option></option>
</select>
<button onclick="changePage()">Load Textbook Page!</button>
As we see above, the web page has 2 select boxes and a button. Depending on what you select in the first select box loads what is in the second one, using the functionLoadOpt2 function locating higher up in my code.
if (result == "Word1") {
$("#option2_select").append('<option>Letter1</option>');
...
There is more but it follows the same code different values.
Result is the following, above the if statement(just a row up),
var result = (document.getElementById('option1_select').value);
now on the button click, the function changePage() runs,
and all I want is ...
var result = (document.getElementById('option1_select').value);
var result2= (document.getElementById('option2_select').value);
Assume they selected and option for both. Result2 doesnt work. I'd imagine because I'm appending it but how would I work around this. So that when I click changePage() I get the selected value of option1_select and option2_select.
functionLoadOpt2:
function functionLoadOpt2(){
var opt1Val = (document.getElementById('option1_select').value);
$("#option2_select").find('option').remove().end().append('<option></option>');
if (opt1Val == "Word1") {
$("#option2_select").append('<option>Letter1</option>');
$("#option2_select").append('<option>Letter2</option>');
$("#option2_select").append('<option>Letter3</option>');
$("#option2_select").append('<option>Letter4</option>');
$("#option2_select").append('<option>Letter5</option>');
}else if (opt1Val == "Word2") {
$("#option2_select").append('<option>Letter3</option>');//they have similar ones in some cases
$("#option2_select").append('<option>Letter6</option>');
$("#option2_select").append('<option>Letter7</option>');
$("#option2_select").append('<option>Letter8</option>');
$("#option2_select").append('<option>Letter9</option>');
$("#option2_select").append('<option>Letter10</option>');
$("#option2_select").append('<option>Letter11</option>');
$("#option2_select").append('<option>Letter12</option>');
$("#option2_select").append('<option>Letter13</option>');
$("#option2_select").append('<option>Letter14</option>');
$("#option2_select").append('<option>Letter15</option>');
$("#option2_select").append('<option>Letter16</option>');
$("#option2_select").append('<option>Letter17</option>');
//this works
}
}
use jQuery to get and set the value of <select> with .val()
Both your select elements have the same id, fix it the it should be fine
<select id="option1_select" name="courseCodeSelectName">
<option></option>
<option>Word1</option>
<option>Word2</option>
</select>
<select id="option2_select" name="courseNumSelectName">
<option></option>
</select>
<button onclick="changePage()">Load Textbook Page!</button>
Demo: Fiddle
Note: You can improve the script a lot by using proper jQuery constructs, like this

jquery clone drop down list

Hi i have following scenario of drop down list
Whenever i select cat1 options, sub cat 1 options will be populated. But if i add another category
it should only add cat1 options not along with sub cat options.But in my case both of cat 1 and sub cat options are loaded. Following are my code to clone drop down list.
<div class="new-categories">
<div class="new-category">
<select class="category-select" name="categories">
<option></option>
<option value="1">cat 1</option>
</select>
<select class='category-select-sub' style="display:none">
<!-- loaded from ajax -->
</select>
</div></div>
Add another category
$('.add-another-cat').click(function(event){
event.preventDefault();
var $orDiv = $('.new-category:last').after($('.new-category:first').clone());
});
This is how i actually supposed to look like
Thanks.
update: populate ajax result
$('div.new-categories').on('change', 'select.category-select', function () {
var $newselect = $('<select />').addClass('category-select-sub');
$(this).parent().append($newselect);
var cat_id = $(this).val();
$.ajax({
url:baseUrl+'categories/getsubcat',
data:{'id':cat_id},
dataType:'json',
async:false,
type:'POST',
success:function(data){
var subhtml = data;
$('.category-select-sub').show();
$('.category-select-sub').html(subhtml);
}
});
});
Once new cat list has been added and an option is selected, the first sub cat are changing according to new list. How to prevent this?
Your code doesn't make sense to me, but this is what I think you are trying to do. Correct me if I am wrong.
"I would like to clone the div new-category and append it to the div new-categories. I only want the first select list cloned, not anything else.
http://jsfiddle.net/HZp5M/
$('a.add-another-cat').click(function(e) {
e.preventDefault();
//clone the first div
var $newdiv = $('div.new-category:first').clone();
//remove the second select (if there is one)
$newdiv.children('select.category-select-sub').remove();
//append new div
$('div.new-categories').append($newdiv);
});
http://jsfiddle.net/SCArr
<script src="//code.jquery.com/jquery-latest.js"></script>
<div id="clone" class="new-category" style="display:none;">
<select class="category-select" name="categories">
<option></option>
<option value="1">cat 1</option>
</select>
<select class='category-select-sub' style="display:none">
<!-- loaded from ajax -->
</select>
</div>
<div class="new-categories">
</div>
Add another category
<script>
$('.add-another-cat').click(function(event){
event.preventDefault();
var $orDiv = $('.new-category:last').after($('#clone').clone().removeAttr('id').show());
});
$('.new-categories').html($('#clone').clone().removeAttr('id').show());
</script>
I would like to see how you have the second sub select menu as it might affect the solution.
Solution 1 - better creation with an empty state
the problem is in your HTML structure
<div class="new-category">
<select class="category-select"> ... </select>
<select> ... </select>
</div>
When you clone .new-categories you clone both select elements.
You need to reconstruct your HTML so you will clone only what you want.
There will be something you will need to create by yourself without a clone.
For example, something like this:
$('.new-category:last').after( $("<div/>")
.addClass("new-category").append($('.category-select:last').clone()).append($("<select/>").addClass(".category-select-sub").hide());
Solution 2 - empty the sub select after clone
a jsfiddle that shows how to empty a select
What to do about auto-populating the sub select affecting all?
This is easy, your code explicitly refer to all sub selects. see the you code saying
$('.category-select-sub').show().html(subhtml);
This code means - set this HTML to all ".category-select-sub" elements. But you want only a specific element with this class - not all..
You should only refer to the sub select you created - which is easy as you already have a reference to it - so the success function should have something like this :
$newselect.show().html(subhtml);

JQuery add form elements iterate ids

I don't care about the content in the form, just want to add a new set of them every time I click add. I will however be adding some conditional fields. But with that in mind, do I actually need to iterate ids? And is the clone event the best way to handle this?
<div id="wrapper">
<div id="condition-1">
<select id="trigger-1" class="trigger">
<option value="0">Select...</option>
<option value="1">View Count</option>
<option value="2">Comment Count</option>
</select>
<select id="operator-1" class="operator">
<option value="0">Select...</option>
<option value="1">Is Greater Then</option>
<option value="2">Less Than</option>
</select>
<div id="input-1" class="input">
<input id="number-1"></input>
</div>
</div>
<div id="add-1" class="add">Add more</div>
I've been digging through a lot of examples of jQuery .clone() ,have not been able to apply any examples to a structure like this. My goal is to add a new set of form elements every time I click "Add more". I don't care if it clones the actual content, just want to clone and then iterate the id's appropriately.
I tried using something similar to:
var cur_num = 1;
var cloned = $("#condition-" + cur_num).clone(true, true).get(0);
++cur_num;
cloned.id = "condition-" + cur_num; // Change the div itself.
$(cloned).find("*").each(function(index, element) { // And all inner elements.
if(element.id)
{
var matches = element.id.match(/(.+)_\d+/);
if(matches && matches.length >= 2) // Captures start at [1].
element.id = matches[1] + "-" + cur_num;
}
});
$(cloned).appendTo($("#condition-wrapper"));*/
The above block of code will clone and append the block I want. But as it iterates the first condition id to #condition-2, every block after is coming up #condition-2 and I have no idea how to change the children of condition-1 to trigger-2 operator-2. Any help is appreciated, thank you!
try this
$(function(){
$('.add').bind('click',function(){
var $this=$(this),
$cloned=$this.prev().clone(),
re=/(\d+)$/,
counter=1,
cid=$cloned.attr('id').replace(re,function(n){
return counter+=parseInt(n);
});
$cloned.attr({id:cid})
.find('[id]')
.each(function(i,o){
var iid=$(o).attr('id').replace(re,counter);
$(o).attr({id:iid});
})
.end()
.insertBefore($this);
});
})

Categories

Resources