Dynamically adding form elements to dynamic form using jquery - javascript

I followed this example
How to use jQuery to add form elements dynamically
Is it possible to add form elements dynamically to the dynamically generated form?
This is my code:
<html>
<script src="jquery.js" type="text/javascript"></script>
<script>
$(document).ready(function () {
$('#addRow').click(function () {
$('<div/>', {
'class' : 'extraPerson', html: GetHtml()
}).hide().appendTo('#container').slideDown('slow');
});
$('#addAttribte').click(function () {
$('<div/>', {
'class' : 'extraAttribute', html: GetHtml1()
}).hide().appendTo('#extraAttribute').slideDown('slow');
});
})
function GetHtml() {
var len = $('.extraPerson').length;
var $html = $('.extraPersonTemplate').clone();
$html.find('[name=firstname]')[0].name="firstname" + len;
return $html.html();
}
function GetHtml1() {
var len = $('.extraAttribute').length;
var $html = $('.extraAttributeTemplate').clone();
$html.find('[name=attribute]')[0].name="attribute" + len;
return $html.html();
}
</script>
<div class="extraPersonTemplate">
<input class="span3" placeholder="First Name" type="text" name="firstname">
Add Attribute
<div id="extraAttribute"></div>
</div>
<div class="extraAttributeTemplate">
<input class="span3" placeholder="Attribute" type="text" name="attribute">
</div>
<div id="container"></div>
<i class="icon-plus-sign icon-white"></i> Add another family member</p>
</html>
I realise there will be issues regarding names of the newly added form elements, but at this point I just want to be able to dynamically add even just a line of text to a dynamically generated form.
Edit: Sorry, forgot to mention what the problem was; the page starts off with just a link saying "Add another family member". This will add the extraPersonTemplate. This template also has a "Add Attribute" link which adds an extra form field to this newly added field.
However when I click "Add Attribute", I'd expect it to add extraAttributeTemplate to the bottom of the dynamically added form, but nothing happens.

There are two specific issues.
IDs are supposed to be unique. Having an anchor with an id of addAttribute for every person isn't valid, and only the first element found in the DOM will have the event bound. This isn't a problem at the start because there's only one of them, but does become a problem later on once you start adding additional family members.
Events bound in the ready handler are only bound to elements that exist when the code executes. If you're going to be adding new elements that you want to have those events bound you need to use event delegation:
$(document).on('click', '.addAttribute', function() {
// add an attribute here
// I've changed from an ID to a class selector
// you'll need to find a way to get a reference to the correct elements from a specific anchor
});
I've put together a demo with the changes detailed above.

Related

Clearing a div in Jquery using its i.d

I am trying to remove the user input and replace it with the original placeholder by using the empty() function.
Here is the jQuery file which takes input from user through a <form> and appends it to list with the template() structure:
var template = function(text) {
return '<p><input type="checkbox"><i class="glyphicon glyphicon-star"></i><span>' + text + '</span><i class="glyphicon glyphicon-remove"></i></p>';
};
var main = function() {
$('.form').submit(function() {
var text = $('#todo').val();
var html = template(text);
$('.list').append(html);
$('#todo').empty();
return false;
});
};
$(document).ready(main);
After the user submits their text and it is added to the list I want the 'form' input to empty so you can enter a new item with out deleting what you just typed.
Here is a snippet of the html file that jQuery is interacting with:
<form class="form">
<div class="form-container">
<input id="todo" type="text" class="form-input" placeholder="Add item">
</div>
<button type="submit" class="btn">+</button>
</form>
Why is the line
$('#todo').empty();
not removing the user input and returning it to the original placeholder?
The empty method doesn't delete the attributes or values but it removes the html inside an element. You should use val:
$('#todo').val('');//To empty the value
If you want to remove the placeholder too then use removeAttr method:
$('#todo').removeAttr('placeholder');
.empty() is for other use cases. From the docs...
Remove all child nodes of the set of matched elements from the DOM.
Instead, you can .val('') your <input />. Observe the following simplified example...
$('.form').submit(function() {
$('#todo').val('');
return false;
});
JSFiddle Link - demo
as an observation to your neighboring code you can also pass the event and call .preventDefault() in place of return false;. Alternatively, you can call reset() in your function block as such: this.reset() - reset demo with default behavior prevented.
Try this:
$('form').trigger("reset");
This is the most elegant and correct way since it will work when you have multiple and different type of inputs without needing to do any changes. Much better than trying to empty and resetting placehorders on all fields. This will make sure to set the fields to initial state (having placeholders set too).
Based on your code you could do:
$('.form').submit(function() {
// ...
$(this).trigger("reset");
// ...
});
See it working on an example form based on yours with more fields inputs and select here.
See JQuery reset Doc here: JQuery API Doc/reset

Repeating field inside a Repeating Section using jQuery

I am having a requirement to include a repeating TextArea control (I created small jQuery snippet, that will clone the first textarea and append it to ul on hitting a button and therefore I am calling it as repeating text area control) inside a repeating DIV section (users will be presented with another add button and when that button is clicked it should clone the div and add that div to the main container. Users can hit the button as many times as they want and therefore I am calling it as repeating DIV).
I am not getting any idea of getting this task done. Here is the elaborated requirement. (It's similar to Repeating Field inside a Repeating Section in InfoPath)
Using jQuery I created a repeating textarea controls (TextAreas get added as list items on hitting Add button) and now I will be having a div which will need to have some textboxes and also need to include this repeating textarea field. ID's also need to unique for everything. As I mentioned above, there will be a button after that div and when the user hits that button, the entire div needs to be cloned and needs to be appended to the main container.
There are a huge number of different ways to do this. I recently had a project where I had to do this very thing. Here is a working Fiddle of the following code example:
HTML
<div id="container">
<span id="sholder"></span>
<br />
<input type="button" value="Add Section" class="addsection" />
</div>
<div id="section_template" class="template">
<div class="section">
<span class="taholder"></span>
<br />
<input type="button" value="Add Textarea" class="addtextarea" />
</div>
</div>
The key concept here is that I created a div section with class template, and in the CSS template is set to display: none;. I use it to more easily create a bigger section of HTML later in the CreateSection() function.
jQuery / javascript
$(function() {
//add the click handler to add a new section
$("input.addsection").click(CreateSection);
//add the click handler for the new section
//since the buttons are added dynamically, use "on" on the "document" element
// with the selector for the button we want to watch for.
$(document).on("click", "input.addtextarea", function() {
var section = $(this).closest("div.section");
AddTextarea(section);
});
});
function CreateSection() {
var section = $("#section_template div.section").clone();
var holder = $("#container span#sholder");
//get the current total number of sections
var sectionCount = holder.find("div.section").length;
//create the section id by incrementing the section count
section.attr("id", "section" + (sectionCount + 1));
//add a textarea to the section
AddTextarea(section);
//add the new section to the document
holder.append(section);
}
function AddTextarea(section) {
var sectionID = section.attr("id");
var holder = section.find("span.taholder");
//get the current total number of textareas in this section
var taCount = holder.find("textarea").length;
//create the new textarea element
var ta = $(document.createElement("textarea"));
//create the textarea unique id
var taID = section.attr("id") + "_textarea" + (taCount + 1);
ta.attr("id", taID);
//show the id... can be removed
ta.val("ID: " + taID);
//add the textarea to the section
holder.append(ta);
}
There are several helpful search functions in the above code: closest, find. Also, I'm using the clone function to duplicate that HTML section.
Also of note, I create the new textarea using $(document.createElement("textarea")). document.createElement is the fastest way for JS to create new HTML DOM objects.
And a bit of CSS for the example
div.template {
display: none;
}
div.section {
border: 1px solid black;
}
div.section textarea {
display: block;
}
This example keeps the IDs unique as you can see in the JSFiddle. However, reading those fields if they are posted to the server is an answer to another question.

Change text of child

I'm trying to make a add to favorite system. I have a function which alerts the proper id I want to add.
I use:
<script type="text/javascript">
function addfavo(state_name)
{
alert(state_name);
}
</script>
And in my html I have a loop (with php) which shows all the images with the add to favorite links which looks like.
<div style="margin-top:40px;">
<a onclick="addfavo('<?php echo $imgid ?>')"><b>Add to favourits</b></a>
</div>
So what happens is I have a lot of links to the same function with different parameters, but I only want the link that I click to change the text (to something like added to favorites)
Can some one help me in the right direction?
I have tried adding:
$(this).innerHTML("test");
but it didn't work.
You might want to use the html method:
$(this).html('test');
While html is a jQuery method, innerHTML is a property of a DOM element. If you were using pure JavaScript, you'd probably use:
this.innerHTML = 'test';
However, as you are using the onclick attribute on your HTML tag, this will not point to your current DOM element inside your function scope. In your case, I'd add a class to your elements, like add_favorite and add your text to another attribute:
<div style="margin-top:40px;">
<b>Add to favourits</b>
</div>
And then apply a jQuery event to it:
<script type="text/javascript">
$(function() {
$('.add-favorite').click(function(e) {
var text = $(this).data('text'); // store the text in a variable
$(this).html(text); // replace your element's html with your text
e.preventDefault();
});
});
</script>
Fiddle: http://jsfiddle.net/MH6vY/

How to make a list in a web page that can be edited without reloading

Here's some low-hanging fruit for those more comfortable with Javascript than I...
I want to improve a Moodle plugin's admin UI. (Moodle is a PHP-based web app). What I need to do is take what is currently a text box, with semi-colon delimited entries and replace that with a editable list.
The HTML elements I would use is a select list, a text input field and another hidden textfield. I guess a couple of submit buttons too, one for adding, and the other for removing of entries.
The behaviour would be:
Entries can be added to the select list from the visible textbox upon some kind of submit (this cannot reload the page).
The hidden textbox would contain all the entries from the select list, just semi-colon delimited
There's a function to remove entries from the select list that also does not reload the page.
The hidden textbox is updated with add/remove actions
This seems to me like something that's easy enough. Though I'm having a hard time finding a close enough example to rip off.
This sample code is as close as I've found thus far. There's got to be some good examples of precisely this sort of thing out there. Any decent pointers will be rewarded with + votes.
What you want to do is use JavaScript and manipulate with the DOM of the webpage. Basically, the HTML of a webpage is parsed and rendered by the browser into a tree of elements. Each HTML tag like <select> is an element in the tree. You use JavaScript to interact with this tree by performing operations like removing elements from this tree or adding elements to this tree. (Note that preforming operations on the tree will not refresh the page.)
The standardized API to do these sorts of manipulation in JavaScript is known as the DOM. However, many people, myself included, think that this API is very clunky and not nearly expressive enough. Doing even trivial things require tons of lines of code. For this reason, many developers do not use the DOM directly instead using more powerful libraries, such as jQuery, to make their lives easier.
Below is an example of some HTML + JavaScript that I think mimics most of your requirements. Ideally for learning purposes, this would be written purely using the standard W3C DOM API, but since your problem is not that trivial, I resorted to using jQuery instead.
The HTML:
<select id="list" multiple="multiple"></select>
<input id="removeButton" type="button" value="Remove"></input>
<div>
<input id="optionAdder" type="text"></input>
<input id="addButton" type="button" value="Add"></input>
</div>
<br>
<input id="clearButton" type="button" value="Clear All"></input>
<div>Not So Hidden: <input id="hidden" type="text"></input></div>
The JavaScript:
// Uses jQuery to define an on document ready call back function
$(function(){
// The code in this function runs when the page is loaded
var options = []; // contains all the options
// add new option to drop-down
var addOption = function(optText) {
// Create new option element and add it to the <select> tag
$('<option></option>')
.attr('value', optText).text(optText)
.appendTo( $('#list') );
};
// writes the names of all the options in the "hidden" text box
var fillHidden = function() {
$('#hidden').val('');
var hiddenText = "";
for(var i=0; i< options.length; i++) {
if(hiddenText) {
hiddenText += "; ";
}
hiddenText += options[i];
}
$('#hidden').val(hiddenText);
}
// Bind the click event of the "Add" button to add an option on click
$('#addButton')
.click(function(){
var optText = $('#optionAdder').val();
if(optText) {
addOption(optText);
}
$('#optionAdder').val('');
options.push(optText);
fillHidden();
});
// Bind the click event of the "Remove" button to remove the selected options on click
$('#removeButton')
.click(function(){
$('#list option:selected').each(function(){
var optIndex = $.inArray($(this).val(), options);
if(optIndex > -1) {
options.splice(optIndex, 1);
$(this).remove();
}
fillHidden();
});
});
// Bind the click event of the "Clear" button to clear all options on click
$('#clearButton')
.click(function(){
$('#list').children().remove();
options = [];
fillHidden();
});
});
Here is a jsfiddle demonstrating the code

Question about selecting form children jquery

Again I'm asking question about comment form, I'm making an image website and every image has its own comment form, so when I submit form I do like this :
$('#comment_form').live('submit', function() {
...
So in order to select only this form textearea value I tried using this but I get undefined error here is how I tried :
$('#comment_form').live('submit', function() {
image_comment_text=$(this).closest("#comment_text").val(); //textarea id is comment_text
I tried to used find(), its working but when I submit comments for few images I get comments 2 or 3 times as I should, because find finds all occurrences of textarea with comment_text id .. how can I do this ?
#molf , here is HTML generated by javascript:
var xHTML = "<div class=\"addComment\">";
xHTML += "<form action=\"<?=base_url()?>images/post_comment/" + post + "\" method=\"post\" class=\"comment_form\" name=\"comment_form\">";
xHTML += "<input type=\"hidden\" class=\"comment_post_id\" name=\"comment_post_id\" value=\"" +post + "\"/>";
xHTML += "<textarea class=\"comment\" name=\"comment_text\" rows=\"8\" cols=\"40\"></textarea>";
xHTML += "<input type=\"submit\" name=\"submit\" class=\"post_image_comment\" value=\"Comment\"><span> Don't make it too big!</span>";
xHTML += "</form></div>";
EDIT
When I print to console log the value of textarea I get only one result as I should, now when I try to append the ul comments I get 2 of the same values .. here how it goes ..
<ul class="comments"></ul>
below is the comment form which is not in the document at all, when certain anchor is clicked the form pops out below .comments , when form submits I want to append the comments to add the new comment to list items of existing unordered list comments , here is the whole code :
$('form[name^="comment_form"]').live('submit', function(event) {
r= $(this).find('> .comment').val();
$('<div class="overlay"></div>')
.appendTo('.addComment')
.fadeIn(200, function() {
$('.comments')
.append('<li id="new_append">' + r + '</li>')
.children(':last')
.height($('.comments li:last').height())
.hide()
.slideDown(800, function() {
var bodyHeight = $('html').height();
$('.addComment').fadeOut(500, function() {
$('html').height(bodyHeight);
$('h2#leaveAComment').fadeOut(200, function(){$(this).text('Thank you for your comment!').fadeIn(200)});
});
});
$('html, body').scrollTo( $('#new_append'), 800 );
});
event.preventDefault();
});
EDIT II #patrick
The javascript which loads the comment form is above .. here is HTML :
-------------BEGIN FOR EACH--------------
<div id="image-12" class="image_content">
<img src="..." />
<ul class="comments hidden"> //This is where the comments are appended to <li></li>
</ul>
<div style="float: left; display: block; width: 100%;">
<a id="image_comment-12" class="image_comment" onclick="showComments('12');" href="javascript:;">Add Comment</a>
</div>
<div id="addComment-12">//this is where the comment form loads
</div>
</div>
----------END--- FOR EACH--------- image ...
First of all, change your selector for your form. I think you can select form by name using the id selector, but you're not supposed to duplicate ids on a page, so jQuery live is probably only watching the first form. This is just a guess, though.
Also, it doesn't matter what class/id you use for your textarea. If you're only going to have one textarea per form, you can use the :text selector. When finding children, I like to use the children selector.
$('form[name="comment_form"]').live('submit', function() {
image_comment_text = $(this).find('> :text').val();
});
If you're using name instead of id because you're going to have multiple forms, I would suggest changing the name to comment_form_'image_id', then your selector would be: $('form[name^="comment_form"]')
Notice the ^ which requires the name to start with 'comment_form'. That way, you can have unique form names (comment_form_234, comment_form_235) and still have the desired effect.
Edit:
I looked at your code update, and it looks to me like you're ignoring the context of the current form in your function. For instance, when you use the selector $('.comments').append(... you're appending to all elements on your page which match that selector. In order to retrieve the proper elements, you'll have to always use your selector as $(this).find(' > .comments').append(... which will work within the context of the submitted form.
I took a few minutes to edit your code, I haven't run it or anything, but it should be close to what you're trying to do. I hope it at least gets you started in the right direction:
$('form[name^="comment_form"]').live('submit', function (event) {
r = $(this).find('> .comment').val();
/* get addComment-classed element */
var addComment = $(this).find(' > .addComment:first');
/* get comments-classed element */
var comments = $(this).find(' > .comments:first');
$('<div class="overlay"></div>').appendTo(addComment).fadeIn(200, function () {
/* note comments element, not selector */
$(comments).append('<li id="new_append">' + r + '</li>').children(':last').height(
/* again, element */
$(comments).find(' > li:last').height()).hide().slideDown(800, function () {
var bodyHeight = $('html').height();
/* again, element */
$(addComment).fadeOut(500, function () {
$('html').height(bodyHeight);
$('h2#leaveAComment').fadeOut(200, function () {
$(this).text('Thank you for your comment!').fadeIn(200)
});
});
});
$('html, body').scrollTo($('#new_append'), 800);
});
event.preventDefault();
});
I added comments in the code, but notice that the addComments and comments selectors are 'cached'. If you're going to be accessing these elements multiple times, storing them in a variable before using them will cut back on DOM traversals. This should really solve your comments being added to multiple elements on your page.
How many comment_forms do you have on a each page? For correct HTML you should only have one id='comment_text' and one id='comment_form' per page.
Consider changing your ids to class='comment_text' and finding with .comment_text rather than #comment_text
I think from your latest comments the issue may be the way you are dynamically adding your form/comments textbox to the page.
Once you've entered comments and submitted them do you then remove the form you've dynamically added? I would recommend this as if not I think your DOM structure is getting confused causing the problems you are experiencing.
First, the find() method was the correct way to go if used within the proper context.
Second, it sounds like you are re-using IDs. This is not allowed. An ID can be used only once on a page.
The closest() function searches 'up' from (and including) the DOM element. The find() function searches the content of the element.
EDIT:
I assume the form is being submitted when the user clicks the submit button. I also assume that there's more to your submit() handler than is shown. Is that correct?
Sometimes you need to add $(this).preventDefault(); in order to keep the form from being submitted in your code, as well as by the default behavior of the 'submit' button.
The following does the same thing (essentially) as find(). It will find the item with the .comment_text class within the form being submitted. So it should only grab the value of one item:
image_comment_text=$(".comment_text", this).val();
You are getting duplicate comments because you aren't removing the previous add comment form. Try adding this to the end of your .slideDown callback function:
$(this).remove();
Here is the full submit function, I made a few changes/additions:
I removed the > from the find('.comment') as it isn't necessary
Changed the new append to a class, then removed it after you scroll to it.
Removed the form when complete
Changed the event.preventDefault(); to return false;... this is more of a personal preference than anything.
I hope this helps :)
$('form[name^="comment_form"]').live('submit', function(event) {
r = $(this).find('.comment').val();
$('<div class="overlay"></div>')
.appendTo('.addComment')
.fadeIn(200, function() {
$('.comments')
.append('<li class="new_append">' + r + '</li>')
.children(':last')
.height($('.comments li:last').height())
.hide()
.slideDown(800, function() {
var bodyHeight = $('html').height();
$('.addComment').fadeOut(500, function() {
$('html').height(bodyHeight);
$('h2#leaveAComment').fadeOut(200, function(){$(this).text('Thank you for your comment!').fadeIn(200)});
$(this).remove();
});
});
$('html, body').scrollTo( $('.new_append:last'), 800 );
$('.new_append').removeClass('new_append');
});
return false;
});

Categories