Recursive IDs and duplicating form elements - javascript

I have the following fiddle:
http://jsfiddle.net/XpAk5/63/
The IDs increment appropriately. For the first instance. The issue is when I try to add a sport, while it duplicates, it doesn't duplicate correctly. The buttons to add are not creating themselves correctly. For instance, if I choose a sport, then fill in a position, and add another position, that's all fine (for the first instance). But when I click to add another sport, it shows 2 positions right away, and the buttons aren't duplicating correctly. I think the error is in my HTML, but not sure. Here is the JS I am using to duplicate the sport:
$('#addSport').click(function(){
//increment the value of our counter
$('#kpSport').val(Number($('#kpSport').val()) + 1);
//clone the first .item element
var newItem = $('div.kpSports').first().clone();
//recursively set our id, name, and for attributes properly
childRecursive(newItem,
// Remember, the recursive function expects to be able to pass in
// one parameter, the element.
function(e){
setCloneAttr(e, $('#kpSport').val());
});
// Clear the values recursively
childRecursive(newItem,
function(e){
clearCloneValues(e);
});
Hoping someone has an idea, perhaps I've just got my HTML elements in the wrong order? Thank you for your help! I'm hoping the fiddle is more helpful than just pasting a bunch of code here in the message.

The problem is in your clearCloneValues function. It doesn't differentiate between buttons and other for elements that you do want to clear.
Change it to:
// Sets an element's value to ''
function clearCloneValues(element){
if (element.attr('value') !== undefined && element.attr('type') !== 'button'){
element.val('');
}
}
As #PHPglue pointed out in the comments above, when new positions are added, they are incorrectly replicated (I'm assuming here) to the newly cloned for
There is a similar problem with the add years functionality.
A quick fix would be to initialize a variable with a clone of the original form fields:
var $template = $('div.kpSports').first().clone();
Then change your addSport handler to:
$('#addSport').click(function () {
//increment the value of our counter
$('#kpSport').val(Number($('#kpSport').val()) + 1);
//clone the first .item element
var newItem = $template.clone();
…
});
However, there are no event bindings for the new buttons, so that functionality is still missing for any new set of form elements.
Demo fiddle
Using even a simple, naive string based templates the code can be simplified greatly. Linked is an untested fiddle that shows how it might be done using this approach.
Demo fiddle
The code was simplified to the following:
function getClone(idx) {
var $retVal = $(templates.sport.replace(/\{\{1\}\}/g, idx));
$retVal.find('.jsPositions').append(getItemClone(idx, 0));
$retVal.find('.advtrain').append(getTrainingClone(idx, 0));
return $retVal;
}
function getItemClone(setIdx, itemIdx) {
var retVal = itemTemplate.replace(/\{\{1\}\}/g, setIdx).replace(/\{\{2\}\}/g, itemIdx);
return $(retVal);
}
function getTrainingClone(setIdx, trainingIdx) {
var retVal = trainingTemplate.replace(/\{\{1\}\}/g, setIdx).replace(/\{\{2\}\}/g, trainingIdx);
return $(retVal);
}
$('#kpSportPlayed').on('click', '.jsAddPosition', function() {
var $container = $(this).closest('.kpSports');
var containerIdx = $container.attr('data_idx');
var itemIdx = $container.find('.item').length;
$container.find('.jsPositions').append(getItemClone(containerIdx, itemIdx));
});
$('#kpSportPlayed').on('click', '.jsAddTraining', function() {
var $container = $(this).closest('.kpSports');
var containerIdx = $container.attr('data_idx');
var trainIdx = $container.find('.advtrain > div').length;
$container.find('.advtrain').append(getTrainingClone(containerIdx, trainIdx));
});
$('#addSport').click(function () {
var idx = $('.kpSports').length;
var newItem = getClone(idx);
newItem.appendTo($('#kpSportPlayed'));
});

Related

Outputting an object to a page that keeps close function

I posted this question before, among others. But it was suggested I need to ask a more specific or focused question.
I am working on an output history log on a single page. And I want to make it so each output it's self is contained in box object that can be closed or deleted individually. Like this.
Now I have managed to get everything working to the point where it will nicely output to a box with a close button. However the close button it's self will not function in this case.
So, I am trying to output it like this...
HTML:
<p>History log:</p><br><div style="white-space:pre-wrap"><ul
id="outputListItem" class="boxcontainer"></ul></div>
SCRIPT:
document.getElementById("Add").onclick = function(e) {
convertOutput();
}
function convertOutput(){
//this is the part I have been trying to get working
convertOutput.addEventListener('close', function() {
this.parentElement.style.display = 'none';
}
});
var output = document.getElementById("output").value;
var li = document.createElement('li');
li.className = "containedboxes";
var dateTime = todayDateTime();
li.innerHTML = "<time id='time'>" + dateTime +"</time><br /> <br />"+ output
+"<br /><br /><span class='close'>×</span>";
document.getElementById('outputListItem').prepend(li);
}
And the script to close the box:
var closebtns = document.getElementsByClassName("close");
var i;
for (i = 0; i < closebtns.length; i++) {
closebtns[i].addEventListener("click", function() {
this.parentElement.style.display = 'none';
});
}
It was suggested to me on the last question I posed I should use convertOutput() right after addEventListener() loop immediately after it. If this is how you do it, i am still quite new to JavaScript, so not sore how to properly do this. I created a fiddle for this also, but for some reason I can't get the script to run properly in the fiddle, But all the code is there to see.
I am looking to solve this using vanilla JavaScript.
I created an example for you. Hopefully this helps you get going :) A couple things to note, I use a data attribute to store the index for the item in the array, so you can delete it when you click on the list item.
document.addEventListener('DOMContentLoaded', function(){
let nameEl = document.querySelector("#name");
let submitEl = document.querySelector("#submit-name");
let historyEl = document.querySelector(".history-list");
let historyList = [
{ name: 'Mitch'},
{ name: 'Max'},
{ name: 'Mike'},
];
function addToList(arr) {
// Clear up list and then update it
while(historyEl.firstChild) {
historyEl.removeChild(historyEl.firstChild);
}
// Update the list with the historyList
for(let item in historyList) {
let name = historyList[item].name;
let listContent = document.createElement("li");
listContent.textContent = name;
// We will use the index to remove items from the list
listContent.setAttribute('data-value', item);
listContent.addEventListener("click", removeFromList)
historyEl.appendChild(listContent);
}
}
function removeFromList(index) {
// Takes the index of the object, and will later remove it
console.log("Removed Item " + this.dataset.value);
historyList.splice(index, 1);
addToList(historyList);
}
addToList(historyList);
submitEl.addEventListener("click", function(event) {
if(nameEl.value) {
// Add the name to the start of the history list array.
historyList.unshift({ name: nameEl.value})
nameEl.value = '';
// Update the dom with the new array
addToList(historyList);
}
});
});
<label for="name">Type Name</label>
<input type="text" name="name" id="name">
<button id="submit-name">Submit Name</button>
<ul class="history-list"></ul>
Hopefully this gives you a good idea on how to get the task done and let me know if you have any questions :)
Your boxes don't respond to the click event simply because your script crashes before the events even get attached to it.
The following block right at the beginning:
document.getElementById("Add").onclick = function(e) {
convertOutput();
}
tries to add a click listener to the HTML element Add which does not exist. If you either remove the code block or add the appropriate element your boxes will have it's click functionality.

How to iterate over htmlCollection

I'm having some difficulty with this. In Backbone, I have a function like this:
functionOne: function(){
$('#myTExtbox-' + budgetLine.attr('id')).on('change keyup paste', function(){
that.mySecondFunction(this);
});
}
In this case, the this is a textbox, which is in a table, inside a div. Then:
mySecondFunction: function(tb){
var tbody = tb.parentElement.parentElement.parentElement.parentElement.parentElement;
//gets main parent, which is a tbody, inside a table, inside a div
}
I then want to iterate over tbody, to go through each row and find a textbox in a specific cell. The problem is that this code:
$.each(tbody, function(index, item){
cost = item;
var t= index;
});
Doesn't seem to allow me to get to any of the items. In this example, if I try to do something like:
item.getElementById('test');
I get an error:
TypeError: Object #<HTMLCollection> has no method 'getElementById'
Why can't I iterate over this object and access objects within?
Thanks
UPDATE
Here's a fiddle: http://jsfiddle.net/HX8RL/14/
Essentially, what should happen is this: When a text box changes, I want to iterate over all the rows in the tb's parent table and sum all the Tb values. Keeping in mind, all the tb's in the same cell position, as there could be other tb's in other places that I dont want to include.
There wont be any collection of TBody
try using children() instead
$.each(tbody.children('tr'), function(index, item){
cost = item;
var t= index;
});
Demo Fiddle
Iterate over all input elements directly to get values.
var tbody = tb.parentElement.parentElement.parentElement;
alert(tbody.id);
var input = $('#tbody').find('input');
alert(input);
console.log(input);
for (var i = 0; i < input.length; i++) {
alert(input[i].value);
alert(i);
}
See fiddle-http://jsfiddle.net/HX8RL/18/
I think there are a few things going wrong here. You know you can only have one ID per page? So you have to do document.getElementByid('test') instead.
Since you are also using jQuery you can use the find function, item.find('#test'). But I think this wouldn't solve you problem. Not sure what you want to achieve, maybe I can help you if you explain a bit more in detail what your problem is.
Also
tb.parentElement.parentElement.parentElement.parentElement.parentElement;
can be written as (in jQuery)
$(tb).parents('tbody');
I've setup a fiddle, maybe it can help you.
Code used in fiddle:
var myFuncs = (function() {
function funcA() {
$('input').on('keyup', function() {
funcB(this);
});
}
function funcB(myInput) {
var $table = $(myInput).parents('table');
$table.find('tr > td > input').each(function() {
var $input = $(this);
if($(myInput).attr('id') != $input.attr('id'))
$input.val("I'm called from another input");
});
}
return {
funcA : funcA
}
})();
myFuncs.funcA();

making function with multiple variables to pass javascript/jquery

I have a little snippet I want to make into a function. But I'm new to javascript. Obviously there is something wrong with the way I pass variables or the way I call them...
So in nutshell, this works: http://jsfiddle.net/kkvbz/
But this doesn't: http://jsfiddle.net/PrtD4/
Problem is I need it as a function, so I've to make 1 version work.
Full snippet:
function cutandMakeslides (containerid,liperslide) {
//This is for footer slider, it rewrites 1 ul into several uls that contain 4 li max.
// get the container, useful for later too...
var container = $(containerid);
// get all available UL and LI elements...
var li_elements = container.find("> UL > LI").clone();
// remove the current content so that we can rebuild it for the slider...
container.find("> UL").remove();
// build the slider container...
var slide_container = $("<div />");
// tricky part: looping through the LI's and building each of the slides...
// first create some helpful variables...
var li_elements_per_slide = liperslide;
var li_counter = 0;
// create the first slide, with a UL to hold the LI's...
var current_li_div = $("<div />");
current_li_div.append($("<ul />"));
// loop through the LI's...
li_elements.each(function(index, element){
li_counter++;
var current_li = $(element).clone();
current_li_div.find("> UL").append(current_li);
if (li_counter % li_elements_per_slide == 0)
{
// we've hit 4 in this list, so add the slide and make
// a new one, using same code as before...
container.append(current_li_div);
current_li_div = $("<div />");
current_li_div.append($("<ul />"));
}
});
// we might have an uneven number of LI's, so we need to check for this...
if (li_counter % li_elements_per_slide != 0)
container.append(current_li_div);
} // end function cutandMakeslides
//activate function above
$(function() { cutandMakeslides(".fproductslides",3); });
Problematic parts:
function cutandMakeslides (containerid,liperslide) {
var container = $(containerid);
var li_elements_per_slide = liperslide;
}
$(function() { cutandMakeslides(".fproductslides",3); });
So after extensive testing and moving the code to a fiddle, the problem seems to have resolved itself during moving it to the fiddle, so we believe there was a minor spelling or syntax error...however using a code comparison tool I was unable to find anything...
none of the problematic parts seemed to have an error:
function cutandMakeslides (containerid,liperslide) {
var container = $(containerid);
var li_elements_per_slide = liperslide;
}
$(function() { cutandMakeslides(".fproductslides",3); });
Here's a copy of the working fiddle
From your 'problematic parts' section it appears you are trying to pass a class(".fproductslides" is a class, classes start with '.' and Id's start with '#') instead of an Id as your variable names lead me to believe you want to do...

JQuery: loosing the <tr> id when using replaceWith() method

I am trying to implement a function which will change the elements order in a table. The logic is very easy: Each TR (except the first and the last ones) has a button_up and a button_down. When the user clicks one of those buttons, the element will change his place with the one above or below.
To do this, I am using jQuery and each TR has a unique Id and the buttons have a common class.
When a button with the specific class is clicked, I am executing this JavaScript code:
$('.upArrow').click( function(something)
{
var id = $(this).attr("id");
var n=id.split("_");
var new_id = '#row_'+n[1];
var parent =$(new_id);
var prev = $(new_id).prev();
swap(prev, parent);
init_authors_list_ordering();
});
$('.downArrow').live('click', function()
{
var id = $(this).attr("id");
var n=id.split("_");
var new_id = '#row_'+n[1];
var parent =$(new_id);
var next = $(new_id).next();
swap(next, parent);
init_authors_list_ordering();
});
}
function swap(a, b, direction)
{
var html = a.wrapInner('<tr></tr>').html();
a.replaceWith(b.wrapInner('<tr></tr>').html());
b.replaceWith(html);
}
The problem is that: The first time, everything works fine (my funxtion is not finished, I have to set some other parameters on change but the logic is there). But then, The two elements who changed their place have lost the TR's id, so they can't be touched anymore.
I don't know why the id is lost. This is the replaceWith() method.
Any help is welcome. Thank you.
To swap the elements, simply do:
function swap(a, direction, b) {
a[direction](b);
}
and call like:
var firstElem = $('#divID'),
secondElem = $('#divID');
swap(firstElem, 'before', secondElem); // or after, whatever you need ?
FIDDLE
Your constant <tr></tr> has no id attribute, so it ends up with no id attribute.
EDIT:
Here is what I reference:
function swap(a, b, direction)
{
var html = a.wrapInner('<tr></tr>').html();
a.replaceWith(b.wrapInner('<tr></tr>').html());
b.replaceWith(html);
}

Java Script : get the string equivalent of a elements ID

my naming convention
id=xxxxx //actual field shown in the screen
id=xxxxxHDN // hidden field containing the enable/disabled status of the component from the set from the controller.
Now what I am trying to do is get the satus of xxxxxHDN to be true/false ,
and accordingly set the components status to disabled /enabled .with java script..
var div = document.getElementById("hiddenFields"); // i hava some 30 hidden fields containing the
var j;
for (j=0;j<div.childNodes.length;j++)
if(div.childNodes[j].value){
alert("inside the loop");
var someElementHDN = div.childNodes[j].id; // my aim is to get the ID=xxxxxHDN
var someElementHDNToString = someElementHDN .toString(); // my aim is to get the string value "xxxxxHDN"
var toRemove = 'HDN'; // the part i wanna remove from 'someElementHDNToString' to make it an id for 'xxxxx'
var equivalantComponentIDAsString = someElementToString.replace(toRemove,'');
$('#' + equivalantComponentIDAsString ).attr('disabled', true);
}
}
Invested a lot of time manupulatiing things above , doesent seems to work . I am new to java scrcript , Where am I missing it?
If you have an element with id like 'fooHDN' and want to find another element with id 'foo', then you can do something like:
var otherElement = document.getElementById(someElement.id.replace(/HDN$/,''));
Assuming that you already have someElement and it's a DOM element.
Your posted js code has error: div does not have a "length", do you mean "div.childNodes.length"?
Anyway, since you're using jQuery already, I think it can become easier as below:
Already tested and it works fine.
$("#hiddenFields input[type='hidden'][id$='HDN']").each(
function () {
var elemId = this.id.replace(/HDN$/, '');
$('#' + elemId).attr('disabled', this.value.toLowerCase() == 'false' ? false : true);
}
);

Categories

Resources