I'm trying to write a form builder where users can generate a signup form. I need to limit the amount of items that the user can create however they also need to delete the items.
Originally I had
var limit = 5;
var counter = 0;
if (counter == limit) {
However when the user deleted items the counter remained the same and so they couldnt replace the deleted form element with a new item. So what I want to do is count how many items are currently active. I tried to do this by giving each new element a class (.kid) and then counting the amount of divs with that class but it didnt work.
Could anyone point me in the right direction? This is what I have so far however it doesn't work.
var limit = 6;
var num = $('.kid').length;
function addAllInputs(divName, inputType){
if (num == limit) {
alert("You have all ready added 6 form items");
}
else {
var newdiv = document.createElement('div');
newdiv.setAttribute('id', 'child' + (counter + 1));
newdiv.setAttribute('class', 'kid' );
Cheers all!
You need to capture the current counter in a closure. Decrease the counter when the user deletes an item and increase it after an item is created. Your code sample doesn't reveal how you handle the deletion, but I'll try to illustrate what I mean with a small code snippet:
$(document).ready(function () {
var limit = 5;
var counter = $('.kid').length;
$('#triggers_delete').click(function () {
/* delete the item */
counter--;
});
$('#triggers_creation').click(function () {
if (counter == limit) {
alert('Limit reached');
return false;
}
/* somehow determine divName and inputType
and create the element */
addAllInputs(divName, inputType);
counter++;
});
});
function addAllInputs(divName, inputType) {
/* just create the item here */
}
Is there any reason an approach like this won't work? Every time you go to add a new DIV, the length of the current collection is examined to see if it meets or exceeds the limit. Of course, you may need to refine the scope of your selector if there could be other DIVs of the form with the same class ID.
var limit = 6;
function addAllInputs(divName, inputType){
if ( $('.kid').length >= limit ) {
alert("You have all ready added 6 form items");
}
else {
var newdiv = document.createElement('div');
newdiv.setAttribute('id', 'child' + (counter + 1));
newdiv.setAttribute('class', 'kid' );
}
Edit: Just a note, I am assuming that you are either removing the deleted items from the DOM or differentiating them from active items with a different class or attribute. Otherwise, the approach I suggested will return a count that includes the deleted items as well.
The only real issue is that your num variable is being defined outside of the function. It will get the number of .kid elements at the time the page loads and will not update. Simply move this line inside the function:
var limit = 6;
function addAllInputs(divName, inputType){
var num = $('.kid').length;
...
Try this
var limit = 6;
function addAllInputs(divName, inputType){
if ($('.kid').length == limit) {
alert("You have all ready added 6 form items");
}
else {
var newdiv = $('div', { 'id': 'child' + (counter + 1), 'class': 'kid' } );
$("inputContainerSelector").append(newdiv);
}
Related
Would anyone be so kind as to advise me how to amend this JavaScript please? I'll admit I don't have much experience working with JavaScript and I've tried myself but ended up a bit lost.
To explain, WooCommerce outputs products on my site in .columns-3 and .columns-4, and assigns .first and .last classes accordingly.
If the site is loaded on mobile, the script below will remove the .first and .last tags, and re-assign them to display the products in two columns.
The script currently only targets .columns-3 within function defaultProductRows and function adjustProductRows. I need to also target .columns 4 within the same script, but I'm not sure how to go about adding it.
<script>
$(window).on('load resize', function (){
var windowWidth = $(window).width();
if(windowWidth < 753){ // this is my screen size break point for the rows
adjustProductRows(); // call the function to adjust add last and first classes
} else {
defaultProductRows(); // else if large screen size then get everything back to defalut
}
});
function defaultProductRows(){
var products = $('ul.products.columns-3 li.type-product');
products.each(function(idx, li) {
var product = $(li);
// remove all classes we added
$('ul.products li.adjusted-row.first').removeClass('adjusted-row first');
$('ul.products li.adjusted-row.last').removeClass('adjusted-row last');
if(idx == 0) { // make sure first li tag gets first class
product.addClass('first');
}
else if((idx+1) % 3 == 0) //this will make sure we have 3 rows by adding last classes after each 3 products
{
product.addClass('last');
}
else if(idx % 3 == 0)
{
product.addClass('first'); // make sure all products divided by 3 will have first class
}
else
{
console.log(idx); // just checking for the index
}
});
}
function adjustProductRows() {
var products = $('ul.products.columns-3 li.type-product');
products.each(function(idx, li) {
var product = $(li);
if(idx % 2 == 0) // we are even
{
product.addClass('adjusted-row first');
product.removeClass('last');
}
else // we are odd
{
product.addClass('adjusted-row last');
product.removeClass('first');
}
});
}</script>
Change your selector to include columns-4
From:
var products = $('ul.products.columns-3 li.type-product');
To:
var products = $('ul.products.columns-3 li.type-product, ul.products.columns-4 li.type-product');
This tells jQuery to select li.type-products that are part of either columns-3 or columns-4
I'm trying to count the articles in the top row of the two boxes on the page.
The boxes are separate and I want the count to be like that too -
function calculateArticlesInRow() {
var articleInRow = 0;
$('.promotionWrapper').each(function() {
console.log($(this).prev().length);
if($(this).prev().length > 0) {
console.log($(this).offset().top + '='+ $(this).prev().offset().top);
if($(this).offset().top != $(this).prev().offset().top){
return false;
}
articleInRow++;
}
else {
articleInRow++;
}
});
$('.result1').html('No: of articles in a row = ' + articleInRow);
}
setTimeout(function(){
calculateArticlesInRow();
}, 3000);
The problem I'm having however, is that the script is counting the articles in both boxes when only the articles with a certain top offset should be counted.
I have found other questions like this, but the all seem to use only one box.
Any help would be appreciated.
Here is a fiddle so you can see what I'm trying to do: https://jsfiddle.net/JackofD/de31nojn/
Thanks in advance
I think you should count the .promotionWrapper articles for each .productWrapper section.
i've updated your fiddle.
var articleInRow = 0,
maxArtNo = 0;
// cycle for every .productWrapper
$('.productWrapper').each(function (index, el) {
articleInRow = 0
// cycle for every .promotionWrapper in this .productWrapper
$('.promotionWrapper', el).each(function (innerIndex, innerEl) {
//your code
});
//set the count to the max
if (articleInRow > maxArtNo) maxArtNo = articleInRow;
});
// rest of your code
Try something like this:
$('.contentWrapper').each(function(){
//finds how many articles in the current container
console.log($(this).find('.promotionWrapper').length);
});
But to get each container individually, maybe you should put a separate class on each section tag instead.
You are counting all the sections with "promotionWrapper" class. To count the 1st row only, you need to select its parent section first and then count children of that. Here is the code:
$('.productWrapper:first').children('.promotionWrapper').each(function () {
console.log($(this).prev().length);
if ($(this).prev().length > 0) {
console.log($(this).offset().top + '=' + $(this).prev().offset().top);
if ($(this).offset().top != $(this).prev().offset().top) {
return false;
}
articleInRow++;
} else {
articleInRow++;
}
});
Sorry, my bad. Didnt see the jsfiddle.
Here is a working solution independent of your code:
var articlesInRow = [];
$('.productWrapper').each(function (index) {
articlesInRow[index] = $(this).find('.promotionWrapper').length;
$('.result' + (index+1)).html('No: of articles in row' + (index + 1) + ' = ' + articlesInRow[index]);
});
which i added to your calculateArticlesInRow function.
Please see updated jsfiddle:
https://jsfiddle.net/de31nojn/15/
I have a dropdown that I want to be cloned and have a unique id. I managed to do it and it works on my website.
I am trying to make a "x" button to removed added clones and I can't get it to work.
The javascript:
var counter = 1;
function addInput(divName, template){
if (counter == 5) {
document.getElementById("add_more_text").remove();
} else {
var newdiv = document.createElement('div');
newdiv.innerHTML = document.getElementById(divName).innerHTML;
document.getElementById(template).appendChild(newdiv);
counter++;
}
var selectElements = document.querySelectorAll('select');
for (var i = 0; i < selectElements.length; i++){
selectElements[i].id = 'id-' + i;
selectElements[i].name = 'category' + i;
}
}
function removeInput(divName, template){
document.getElementById(template).removeChild(divName);
counter--;
}
The html:
<div id="template">
<select name="category0"><option>hi</option></select>
x
</div>
<div id="add_more"></div>
+ Add more
DEMO
Any help is much appreciated!
Simpler to modify remove function as follows:
function removeInput(obj) {
if (obj.parentNode.className == 'added') {
obj.parentNode.parentNode.removeChild(obj.parentNode);
counter--;
}
}
And have a link in template like this:
x
Class added is to distinguish new clones that can be removed:
newdiv.className = 'added';
Demo: http://jsfiddle.net/rjXXa/2/
in your onClick property
x
you are passing template and add_more
And in the handler function
function removeInput(divName, template){
the parameters are in a different order, so divName will contain 'template' and template will contain 'add_more'. Even if you fix this,
document.getElementById(template).removeChild(divName); // will throw error
because the div#add_more is not a child of div#template.
For fixing this, you need to pass a reference to the clicked element, like the following
x
and in your function
function removeInput(anchor){
var clone = anchor.parentNode; // div containing the anchor
if(clone.id!='template'){ // make sure we're not removing the original template
clone.parentNode.removeChild(clone);
counter--;
}
}
as in this Fiddle
Update
It's better to remove the add more option from display using css and make it visible later than removing/appending it in DOM, as follows
change the following in addInput() function
if (counter > 4) {
document.getElementById("add_more_text").style.display='none';
}
and in removeInput() function add
if (counter < 5) {
document.getElementById("add_more_text").style.display='block';
}
as in this Fiddle
I am trying to create a section of my webstore where the customer can 'build' their own bundle and choose any combination of 5 items.
I have a set of buttons which, when clicked, add their value to a Fieldset along with a button to remove it in case they misclicked or changed their mind.
All the components work fine, but I don't know how to limit the Fieldset to only five items. Is there a way to either count the lines, then stop accepting input
after five or look for 'Remove' five times?
I'm still fairly new to coding and not too sure what is possible.
This input will end up being submitted in a form.
Here is my Fiddle and below is my Javascript code which i have tried for it :
$(document).ready(function () {
$(".buttons").click(function () {
var intId = $().length + 1;
var item = $(this).html();
var fieldWrapper = $("<div class=\"fieldwrapper\" id=\"field" + intId + "\"/>");
var removeButton = $("<input type=\"button\" class=\"remove\" value=\"Remove\" />");
removeButton.click(function () {
$(this).parent().remove();
});
fieldWrapper.append(size);
fieldWrapper.append(removeButton);
$("#buildyourkit").append(fieldWrapper);
});
});
This will give you the current quantity of elements added to the . Just make sure that there is still room for another before appending a new one.
$("fieldset .fieldwrapper").length
I've forked your fiddle. Just look at the console while adding new items to the fieldset.
You can have a global variable which will count up and disable all buttons if over 5 every time you add a field, and down and enable all buttons every time you remove a field.
Also, it is a bit nicer to just set a live handler listening for any remove buttons, rather than make a new function and bind a new listener for each button, so I demonstrated; but it is not obligatory (your way works, too, given it's just 5 elements).
$(document).ready(function () {
var buttonMaxID = 0;
var buttonCount = 0;
$('$buildyourkit').on('click', '.remove', function () {
$(this).parent().remove();
if (buttonCount-- >= 5) {
$('.buttons').prop('disabled', false);
}
});
$(".buttons").click(function () {
if (++buttonCount >= 5) {
$('.buttons').prop('disabled', true);
}
var item = $(this).html();
var fieldWrapper = $("<div class=\"fieldwrapper\" id=\"field" + (buttonMaxId++) + "\"/>");
var removeButton = $("<input type=\"button\" class=\"remove\" value=\"Remove\" />");
fieldWrapper.append(size);
fieldWrapper.append(removeButton);
$("#buildyourkit").append(fieldWrapper);
});
});
What I propose is designing a manager class to maintain all functions/methods that must interact with the UI. This allows you to define your data set in one place, and keep the UI binds in one place. By doing so, you set yourself up with a cleaner code base, easy refactoring, and quickly make code modifications. Also, you get all this goodness without any global variables, another great bonus.
The code does look like its larger, but once you understand the simplicity of the manager you will see the possibilities I outlined above.
$(document).ready(function () {
//Create a new Kit Manager
var kitManager = new KitManager();
$(".buttons").click(function () {
kitManager.add(this);
});
$(".report").click(function () {
kitManager.getKit();
});
});
function KitManager()
{
//Static amount of items to return
var MAX_ITEMS = 5;
//Where the items should be visually displayed on the UI
var kitLegend = $("#buildyourkit");
//Internal array for storing the items added
var items = []
function add(element)
{
if(items.length < MAX_ITEMS)
{
var itemNumber = items.length + 1;
var item = $(element).html();
var fieldWrapper = $("<div class=\"fieldwrapper\" id=\"field" + itemNumber + "\"/>");
var removeButton = $("<input type=\"button\" class=\"remove\" value=\"Remove\" />");
//Add item to the array collection
items.push(item);
//Bind a remove function to the newly created button
removeButton.click(function () {
kitLegend[0].removeChild(fieldWrapper[0]);
items.splice(itemNumber, 1);
});
//Append UI components to container
fieldWrapper.append(item).append(removeButton);
//Append to main legend
kitLegend.append(fieldWrapper);
}
else
{
//Simple alert to user
alert('You\'ve Reached The Maximum Number of Items');
}
}
//Simple function for demonstration of a reporting feature
//or potential method for returning the stored items
function getKit()
{
for(var i=0,length=items.length;i <length;i++)
{
console.log(items[i]);
}
}
//Expose public method call
return {
add:add,
getKit: getKit
};
}
http://jsfiddle.net/x97S5/
I hope you find the solution acceptable, and if you have any further questions please ask.
For more information on the solution and technique proposed take a look at Key Principles of Maintainable JavaScript
I am a bit of a javascript noob. I want to make different content appear on my page changing once a user has clicked 3 links on the page.
So I assume I want a variable which increases on every click of the link and an if statement that makes the content appear only once the variable reaches my desired number. This is what I've got so far:
<script>
var pass = 0;
function clicked() {
pass = pass + 1;
}
</script>
Then on the links:
LINK
Then on the content
<script>
if (pass > 1) {
document.write('First<br>') }
else {document.write('Second<br>') }
</script>
It isn't working and the variable always equals 0. Any ideas?
You 'content script' is only called once (when pass is still zero).
You need to check it every time when the link is clicked:
var pass = 0;
function clicked() {
pass = pass + 1;
if (pass > 1) {
document.write('First<br>');
} else {
document.write('Second<br>');
}
}
Here is an example:
var count = 1,
topics = ['Hello', 'Well...', 'Goodbye'];
function clickHandler() {
if (count >= 3) {
changeText(Math.round((Math.random() * 2)));
count = 1;
} else {
count += 1;
}
}
function changeText(idx) {
document.getElementById('content').innerHTML = topics[idx];
}
When the link is pressed 3 times you'll get random content from the list (topics).
What is changed
In the clickHandler the counter is being reset so each 3 clicks random content will be shown
I use innerHTML instead of document.write - its better practice
count is checked on each click instead of once like you check pass.
Example here.