index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
%form{:novalidate => "novalidate"} %div{:class => "repeater"} - (1..3).each do |x| %section{:class => "repeatable"} -#%a{:class => "remove", :style => "display:#{x == 0 ? "none" : "block"}", :href => "javascript:void(0);", :onclick => "if($('#person .repeatable').length > 1){$(this).closest('section.repeatable').remove(); $('#count').val($('#person .repeatable').length);}"} [Remove] %h2 Person #{x} %div{:class => "half-holder clearfix"} %label{:class => "half"} %input{:type => "radio", :name => "gender_#{x}", :value => "male", :required => "required", :data => {:structure => "gender_"}} Male %label{:class => "half"} %input{:type => "radio", :name => "gender_#{x}", :value => "female", :required => "required", :data => {:structure => "gender_"}} Female %div{:class => "half-holder clearfix"} %label{:class => "half"} First Name: %input{:type => "text", :maxlength => "255", :id => "first_name_#{x}", :name => "first_name_#{x}", :required => "required", :placeholder => "John", :class => "textbox", :data => {:structure => "first_name_"}} %label{:class => "half"} Last Name: %input{:type => "text", :maxlength => "255", :id => "last_name_#{x}", :name => "last_name_#{x}", :required => "required", :placeholder => "Smith", :class => "textbox", :data => {:structure => "last_name_"}} %div{:class => "half-holder clearfix"} %label{:class => "half"} Phone: %input{:type => "tel", :maxlength => "30", :id => "phone_#{x}", :name => "phone_#{x}", :required => "required", :placeholder => "+1 (123) 555-1234", :class => "textbox", :data => {:structure => "phone_"}} %label{:class => "half"} E-Mail: %input{:type => "email", :maxlength => "255", :id => "email_#{x}", :name => "email_#{x}", :required => "required", :placeholder => "name@email.com", :class => "textbox", :data => {:structure => "email_"}} %label Notes: %textarea{:rows => "6", :maxlength => "65535", :id => "notes_#{x}", :name => "notes_#{x}", :placeholder => "Enter whatever you like here...", :class => "textbox", :data => {:structure => "notes_"}} -#%input{:type => "button", :class => "add_btn", :id => "add_btn", :value => "Add Person"} %input{:type => "hidden", :id => "count", :name => "count", :value => "3"} %input{:type => "submit", :id => "submit", :name => "submit", :value => "Submit"} |
style.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
* { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } html { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; min-height: 100%; } body { max-width: 1024px; margin: 0 auto; padding: 1rem; font-size: 16px; font-family: sans-serif; color: #000; line-height: 1.15; } .repeater { padding-bottom: 1rem; border-bottom: 2px solid #C0C0C0; margin-bottom: 1rem; } .repeatable { margin-bottom: 1rem; border: 2px solid #E1E1E1; padding: 1rem; background-color: #F7F7F7; -webkit-border-radius: 0.25rem; -moz-border-radius: 0.25rem; border-radius: 0.25rem; } h2 { margin: 0 0 0.5rem 0; border-bottom: 2px solid #C0C0C0; } .remove { display: block; float: right; } .half-holder { width: 100%; padding-bottom: 0.5rem; } label { display: block; } .half { float: left; width: 49%; margin-right: 2%; } .half:nth-child(even) { margin-right: 0; } input[type="text"], input[type="tel"], input[type="email"], textarea { margin: 0; padding: 0.25rem; width: 100%; } input[type="submit"], input[type="button"] { padding: 0.5rem; } input[type="submit"] { width: auto; } input[type="radio"] { width: auto; margin-right: 0.5rem; } textarea { resize: vertical; overflow-y: scroll; } .add_btn { display: block; width: 50%; text-align: center; margin: 0 auto; } /* CLEARFIX */ .clearfix:before, .clearfix:after { content: ""; display: table; } .clearfix:after { clear: both; } .clearfix { *zoom: 1; } |
script.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
// To add javascript functionality - if javascript is enabled $(document).ready(function () { // insert an "add more" button after the last element $('#count').before('<input class="add_btn" id="add_btn" type="button" value="Add Person">'); // add the "remove" link to each section $('.repeatable').prepend('<a class="remove" href="javascript:void(0);">[Remove]</a>'); updateRemoveLinks = function () { // if "repeatable" element count is greater than 1... if ($('.repeatable').length > 1) { // ...show the "remove" link $('.repeatable').children('.remove').css({'display':'block'}); // otherwise... } else { // don't display the "remove" link $('.repeatable').children('.remove').css({'display':'none'}); } } updateRemoveLinks(); // DESTROY!!! >.< // this method does all the checking necessary for deleting an element destroy = function () { // when the user clicks the "remove" link on a section... $('.remove').click(function(){ // ...get that link's "repeatable" parent (container) and remove it $(this).parent('.repeatable').remove(); // now, for each remaining repeatable element... $('.repeatable').each(function(){ var r = this; // store "this", to avoid scope problems var num = $(r).index() + 1; // store the index+1 of the current "repeatable" in the collection (zero-based indexes) // RE-NUMBER ALL THE THINGS!!! >.< // change the heading to reflect the index+1 value $(r).children('h2').html('Person ' + num).text(); // go through all text box elements within... $(r).find('input, textarea').each(function(){ // ...change their "structure" data attributes to reflect the index+1 value of the "repeatable" element dattr = $(this).data('structure') + num; $(this).attr({ 'id':dattr, 'name':dattr // update the "for" attribute on the parent element (label) }).parent('label').attr('for',dattr); }); //adjust the counter $('#count').val($('.repeatable').length); }); updateRemoveLinks(); }); } // now, call the "destroy" method, so that when the page loads, this method is declared and will affect all "repeatable" elements - this has something to do with adding/removing dynamic elements, I wrote this so long ago, that I don't recall exactly how it works... destroy(); // when the user clicks the "add more" button... $('.add_btn').click(function(){ var original = $('.repeatable').last().find(':checked'); console.log(original); // clone the previous element (a "repeatable" element), and insert it before the "add more" button $(this).prev('.repeatable').clone().insertBefore(this).html(); // get the number of repeatable elements on the page var num = $('.repeatable').length; // again, get the previous element (a "repeatable" element), and change the header to reflect it's new index $(this).prev('.repeatable').children('h2').html('Person ' + num); // now, go through all text boxes within the last "repeatable" element... $('.repeatable').last().find('input, textarea').each(function(){ // ...change their "structure" data attributes to reflect the index+1 value of the "repeatable" element dattr = $(this).data('structure') + num; $(this).attr({ 'id':dattr, 'name':dattr // update the "for" attribute on the parent element (label) }).parent('label').attr('for',dattr); // clear the input field contents of the new "repeatable" // if the type of the input is "radio"... if ($(this).attr('type') == 'radio') { // remove the checked attribute $(this).removeAttr('checked'); // for all other inputs... } else { // clear the value... $(this).val(''); } if (original.length == 1) { original.prop('checked',true); } //adjust the counter $('#count').val($('.repeatable').length); }); // run the "destroy" method... I forget why... just do it, and don't gimme no lip. destroy(); updateRemoveLinks(); }); }); |