I have the following javascript http://jsfiddle.net/847Kf/4/ that contains a counter at line 14 and 36. The code works but when i delete an item that i added and add a new item the counter continues from where it left.
For example: i insert 4 items 1 , 2 , 3 , 4 now i delete the second item and add a new item so i will have 1 , 3 , 4 , 5. How can i make that counter when i delete an item the counter should reset and redisplay the values according the example:
1, 3, 4, 5 - deleted an item and now the counter should change the values for the remaining item to: 1, 2, 3, 4.
HTML:
<ul class="tags" data-prototype="<div><label class=" required">__name__</label><div id="task_tags___name__"><div><label for="task_tags___name___name" class=" required">Name</label><input type="text" id="task_tags___name___name" name="task[tags][__name__][name]" required="required" maxlength="255" /></div></div></div>">
</ul>
Javascript:
// setup an "add a tag" link
var $addTagLink = $('Add a tag');
var $newLinkLi = $('<li></li>').append($addTagLink);
jQuery(document).ready(function() {
// Get the ul that holds the collection of tags
var $collectionHolder = $('ul.tags');
// add the "add a tag" anchor and li to the tags ul
$collectionHolder.append($newLinkLi);
// count the current form inputs we have (e.g. 2), use that as the new
// index when inserting a new item (e.g. 2)
$collectionHolder.data('index', $collectionHolder.find(':input').length);
$addTagLink.on('click', function(e) {
// prevent the link from creating a "#" on the URL
e.preventDefault();
// add a new tag form (see code block below)
addTagForm($collectionHolder, $newLinkLi);
});
});
function addTagForm($collectionHolder, $newLinkLi) {
// Get the data-prototype explained earlier
var prototype = $collectionHolder.data('prototype');
// get the new index
var index = $collectionHolder.data('index');
// Replace '$$name$$' in the prototype's HTML to
// instead be a number based on how many items we have
var newForm = prototype.replace(/__name__/g, index);
// increase the index with one for the next item
$collectionHolder.data('index', index + 1);
// Display the form in the page in an li, before the "Add a tag" link li
var $newFormLi = $('<li></li>').append(newForm);
// also add a remove button, just for this example
$newFormLi.append('x');
$newLinkLi.before($newFormLi);
// handle the removal, just for this example
$('.remove-tag').click(function(e) {
e.preventDefault();
$(this).parent().remove();
return false;
});
}
The main problem is on how you count the elements.
When creating a new tag, you update the quantity on the data, but it doesn't get noticed when deleting.
You have two options:
Update on the removal the value $collectionHolder.data('index', index - 1)
take the quantity on each creation, not setting it on document ready
I've noticed another issue with deleting. When you you create every element, you are attaching a delete event to every .remove-tag in the DOM, on the 3rd creation, the first element would have 3 events attached, and the 2nd two.
Two avoid that I'd do something like:
$('ul.tags').on('click','.remove-tag',function(e){
e.preventDefault();
$(this).parent().remove();
})
What that means is something like "Look on dom for ul.tag, and if a click event is triggered on a .remove-tag do this" so it keeps alive and active for the new tags created.
Because you have so many id's / name's / class's etc which incorporate the index. I would recommend regenerating the list on delete.
ie:-
function regenerateTagList(){
var vals = $('.tags li.tag input').toArray().map(function(v, i) { return v.value; });
$('.tags li.tag').remove();
$('ul.tags').data('index', 0);
for(var i = 0; i < vals.length; i++){
addTagForm($('ul.tags'), $newLinkLi);
$('.tags li.tag input:last').val(vals[i]);
}
}
And add a class to the li for the tag so you can select them from the add link:-
var $newFormLi = $('<li></li>', { class:"tag" }).append(newForm);
then call after you remove:-
$('.remove-tag').click(function(e) {
e.preventDefault();
$(this).parent().remove();
regenerateTagList();
return false;
});
Working Fiddle
This modification will do the change without regenerating the whole thing:
$('.remove-tag').click(function(e) {
e.preventDefault();
$(this).parent().remove();
var count = 0;
$('.tags li').each(function() {
$(this).find('div label').first().text(count);
count++;
});
return false;
});
It finds all the li's, then finds the first label in each li and changes it to the count variable. It could be simplified if the first label had a class called 'count'.
Related
I have a form that shows up when each row in a table is double clicked. The values of this form can be updated and the form should be submitted with all row changes. But each time I double click on a row and edit the values of that form for that row, the previous values I had changed get overwritten. In order to work around this, I tried adding all the changes to a map with the row id as the key and the values of the form as the value. But the form still won't update with the new values. Here is a fiddle to demonstrate what I mean:
https://jsfiddle.net/4fr3edk7/2/
If I double click on the row that says "Adam Smith" and change that name to John Doe, when I double click on the second row and then double Click on "Adam Smith" again, it should say "John" on the first textbox and "Doe" on the second one. But the new value never seems to save.
This code snippet loops through each key, then loops through each value of that key:
for(var i = 0; i<key.length; i++){
var getval = globalchanges[key[i]];
for(var k=0; k<getval.length; k++){
$("#input1").val(getval[0]);
$("#input2").val(getval[1]);
}
}
How can I get the new changes to save? (The table rows don't have to show the changes, just the textbox values). Any help would be appreciated.
First, as mentioned by #Taplar you are binding the click event multiple times. Your approach is close enough, the idea of storing the changes is valid. You should have 2 functions, store the changes on button click and the second one to retrieve the changes by id.
Updated Fiddle
This function will get the values of the form and will store in on a global object
function setMap(id){
var firstrow = $("#input1").val();
var secondrow = $("#input2").val();
globalchanges[id] = [firstrow,secondrow];
}
This other function will check if the global object has values for the passed id, if not, it will use the values on the row
function getMap(id, tr){
if(globalchanges[id] != undefined && globalchanges[id].length == 2){
$("#input1").val(globalchanges[id][0]);
$("#input2").val(globalchanges[id][1]);
}
else{
$("#input1").val($(tr).find('td').eq(1).text());
$("#input2").val($(tr).find('td').eq(2).text());
}
}
Please note there are also changes on the dbclick and click events, they should be separated
$("#table tr").dblclick(function(){
$("#txtbox-wrapper").css({"display" : "block"});
var id = $(this).find('td').eq(0).text();
$('#id').val(id);
getMap(id,this);
});
$("#savebtn").click(function(){
var id = $('#id').val();
setMap(id);
});
And that we added and additional input to store the id on the form.
You are going to need to rethink your logic because of this part
$("#table tr").dblclick(function(){
$("#txtbox-wrapper").css({"display" : "block"});
var id = $(this).find('td').eq(0).text();
$("#input1").val($(this).find('td').eq(1).text());
$("#input2").val($(this).find('td').eq(2).text());
$("#savebtn").click(function(){
addToMap(id);
});
});
-Every time- you double click a table row you are adding a new click binding to the savebtn element. This means if you double click both rows, when you click that button it will execute addToMap for both ids. You may have other issues with your logic relying on only two other inputs for multiple rows, but this double/triple/+ binding is going to bite you.
There are few changes required in your logic as well as implementation.
1: Do not bind save event inside row click.
2: You are selecting the value in row double click event from td element. You need to update this element to keep your logic working
3: Keep track of which row is getting updated.
Updated Code
var globalchanges = {};
var rowSelected = null;
$("#table tr").dblclick(function() {
$("#txtbox-wrapper").css({
"display": "block"
});
rowSelected = $(this).find('td').eq(0).text();
$("#input1").val($(this).find('td').eq(1).text());
$("#input2").val($(this).find('td').eq(2).text());
});
$("#savebtn").click(function() {
addToMap(rowSelected);
});
function addToMap(row) {
var array = [];
var changes = {};
var firstrow = $("#input1").val();
var secondrow = $("#input2").val();
array.push(firstrow, secondrow);
globalchanges[row] = array;
makeChanges(row);
}
function makeChanges(row) {
var key = Object.keys(globalchanges);
console.log(key);
$("#table tr td").each(function(k, v) {
if ($(v).text() == key) {
$(v).next().html(globalchanges[row][0]);
$(v).next().next().html(globalchanges[row][1]);
globalchanges = {};
}
});
}
Working fiddle : https://jsfiddle.net/yudLxsgu/
I have a script running to show a number in the select list options when the user check one specific value it will display a number refering to how much times he can pay his bill.
Note this code:
var tabelaParcelas = [2,3,4,6,8,10,12];
$(document).ready(function(){
update();
});
$('input[type=checkbox]').click(function(){
update();
})
function update(){
var list = $('#instNum2'); // use selector only once whenever possible for better performance
// clear any existing options from the dropdown list
list.html("")
// count checked boxes
var checked_boxes = 0
$(".check_list").each(function(){
if(this.checked){
checked_boxes++;
}
});
// append options to the select list
for(var i=0;i<checked_boxes;i++){
var option = $("<option>", {value: tabelaParcelas[i], "text": tabelaParcelas[i]});
list.append(option);
}
}
That's correct! But the thing is, I need to display 1x as well, and that's what's not showing at all, 1x should be visible always no matter how much boxes you select, and other thing, when I select more than 7 boxes, the option keep producing empty options, while it should only keep displaying 12x without appending any new...
var tabelaParcelas = [2,3,4,6,8,10,12];
$(document).ready(function(){
update();
});
$('input[type=checkbox]').click(function(){
update();
})
function update(){
var list = $('#instNum2'); // use selector only once whenever possible for better performance
// clear any existing options from the dropdown list
list.html("")
// count checked boxes
var checked_boxes = 0
$(".check_list").each(function(){
if(this.checked){
checked_boxes++;
}
});
//add limit to check_boxes
//This section was not in original code
// Could also do if(checked_boxes > tabelaParcelas.length) {checked_boxes = tabelaParcelas.length;}
//Would make it more dynamic.
//This is added to limit the number of options added to the length of the array at the top.
//This will prevent blank options from being added as requested in the question.
if(checked_boxes > 6) {
checked_boxes = 6;
}
//add 1x option to empty list
//This was not in original code
//This was added as the list was cleared when this function is run and they always wanted 1x to appear as an option.
list.append("<option value='1' selected>1</option>");
// append options to the select list
for(var i=0;i<checked_boxes;i++){
var option = $("<option>", {value: tabelaParcelas[i], "text": tabelaParcelas[i]});
list.append(option);
}
}
I wan't to replace a list item of an unordered list and generate 'n' list items in its place after clicking on it (n is input by the user on prompt when a list item is clicked).
I referred to the following link, but still couldn't make out a solution for my case:
Generating list item x number of times, based on user input
This is what I tried but its not working:
HTML:
<ul id="list" onclick="inputNumber()">
<li>list-1</li>
<li>list-2</li>
<li>list-3</li>
</ul>
CSS:
li {
list-style: none;
display: inline;
}
JavaScript:
function inputNumber() {
var index = $(this).index();
// to record the index of the clicked item
var inputNum = prompt("Divide By:");
// input and store 'n' by the user
var oldList = document.getElementById("list");
// the main old list
var garbage= oldList.removeChild(oldList.childNodes[index+1]);
// remove the clicked item from the main list
var newList=document.createElement("li");
// create new list item
var i;
for (i=1;i<=inputNum;i++)
{
list.append(newList.clone().text('list-'+i));
// loop to clone and append 'n' list items in place of the previous one
}
}
JSfiddle :
https://jsfiddle.net/yoshi2095/k0jt8n7x/44/
Please help. Thanks.
edit:
only the 'clicked' item should get replaced by the 'n' items input by the user at the same place of that 'clicked' item. Other list items should remain at their respective places as before.
JSFiddle
There were a few issues you had in your code. One was you were forgetting $() around functions that were only for jQuery. You also were not removing the child elements correctly, the jQuery functions $(this) and .remove() and JavaScript .children together work to remove only the clicked element. You were also only creating one new element to add to the list, but you needed to create multiple, so I added that inside the for loop.
$("li").click(function() {
var inputNum = prompt("Divide By:");
var oldList = document.getElementById("list");
for (var i=1;i<=inputNum; i++)
{
var newList=document.createElement("li");
$(this).parent().append($(newList).text('list-'+i));
}
$(this).remove();
});
I'm making a basic list using vanilla javascript. I am able to add items, and change their class when they are clicked. Now, I want the items that have been selected (so their class has been changed) to be removed when they are clicked. At the bottom of the code, I am trying to loop through the list, then if the element in the list has the selected class, an event listener will remove the element when clicked, but this isn't working for me. Any ideas on what I'm doing wrong? (live demo: http://codepen.io/nicolaswilmot/pen/oXLgyq)
Here is the code:
var list = document.getElementById("theList"); // Get the list
// Add new item to top of list
function addItem(e) {
var userTxt = document.getElementById("userInput"); // Get user text
var newItem = document.createElement("li"); // Create new list item
var itemTxt = document.createTextNode(userTxt.value); // Get the text for item
newItem.appendChild(itemTxt); // Add text to list item
list.insertBefore(newItem, list.firstChild); // Put new item at top of list
newItem.className = 'defaultItem'; // Set default class for li
document.getElementById("userInput").value = ''; // Clear the input box
e.preventDefault(); // Prevent page from reloading when page is submitted
// Changes list item class
function changeClass () {
newItem.className = 'selectedItem';
}
// Initialize array for list items
listArray = [];
// Loop through list, add items to array, update class and counter
// when items are clicked
for (var i=0; i<list.children.length; i++) {
listArray.push(newItem);
listArray[i].addEventListener("click",changeClass);
listArray[i].addEventListener("click",countStuff);
}
}
var docForm = document.getElementById("theForm"); // Get the form element
docForm.addEventListener('submit',addItem,false); // Call addItem function when form is submitted
docForm.addEventListener('submit',countStuff,false); //Call counter when form submitted
// Function for the list item counter
function countStuff() {
// Get div container for counter
var itemCount = document.getElementById("counter");
// Get all list items that have not been selected (default class)
var unselectedItems = document.querySelectorAll('li.defaultItem');
//If more than one item, display plural "items"
if (unselectedItems.length > 1) {
itemCount.innerHTML = 'You still need '+unselectedItems.length+' items!';
} else if (unselectedItems.length == 0) {
itemCount.innerHTML = 'You have all items!';
} else {
itemCount.innerHTML = 'You still need '+unselectedItems.length+' item!';
}
}
// Loop through the list
for (var i=0; i<list.children.length; i++) {
// Remove items that are in selected state
if (list.childNodes[i].className='selectedItem') {
list.childNodes[i].addEventListener('click',function () {
list.removeChild([i])},false);
}
}
The placement of your code where you are trying to remove the element once it has the selectedItem class does not make sense, because that code will only run once on page load when the page has no items in the list. Instead, in the same function where you add the selectedItem class, you can bind an event listener to that DOM element that removes it from the list on the next click. http://codepen.io/anon/pen/vOKEzz
function changeClass () {
newItem.className = 'selectedItem';
//Remove it on click!
newItem.addEventListener('click',function () {
list.removeChild(newItem)
}, false);
}
hi can you please tell me how show child element in right element .My function work fine first time But it fail second time .
I do the following steps
1) Press Add button 2 times.Generate row2 as well as submenu of firstLevel.
2) Expand menu option (+) Button.Click "menu_tc_1".It remove rows on right panel .and press add button it create nested submenu .
3) When you press again "menu_tc_1" it show same number of rows as many you click add button.
4) But when you click "First Level " it should show two rows because it have two child("menu_tc_1","menu_tc_2") .It is showing all rows.Because in array it gives all values.
I need show only child
jsFiddle
$(document).on('click', 'ul li > a', function (e) {
//first method..
console.log($(this).siblings().length);
var selEl = [];
$(this).closest('ul').find('li').each(function (idx, el) {
var id= el.id.replace('menu_','');
selEl.push(id);
});
// alert(id);
console.log('aaa' + selEl);
getViewFromPanel(selEl);
});
function getViewFromPanel(arr) {
console.log(arr);
$('#ultest').html('');
for (var i = 1; i < arr.length; i++) {
$('#ultest').append('<li id=' + arr[i] + '>' + arr[i] + '</li>');
}
}
See this updated fiddle.
The main problem is $(this).closest('ul').find('li').each(function (idx, el) {. find will look for all sub levels, that is why is displays all menus. You only want the children of the next ul:
$(this).closest('li').children('ul').children().each(function (idx, el) {
Also, you skip the first element (probably due to using find) in getViewFromPanel - it should start at 0 index:
for (var i=0;i<arr.length;i++){
There is a problem with the way that you set the active class so when you go back and click add, it is added to the wrong hierarchy. I am not sure what your intention is with the active class, but I think that you should clear the class $('.active').removeClass('active') and set it again when the hierarchy changes.