I have a simple function that is supposed to check if an element has a class. If it doesnt have the class, it should add the class. It should also iterate through all other elements in the series, and deselect them. So only 1 element should be shown at any time. I have looked at this, this, this and this as well as a bunch of others. I know the function should be simple, but I just cant resolve it.
const changeFilter = (itemNumber) => {
// grab element that has been clicked
let selector = document.getElementsByClassName(itemNumber);
// check if element doesnt has the class name that underlines it
if (!selector[0].classList.contains("darken-filter-selector")) {
// add the class if it doesnt have it
selector[0].classList.add("darken-filter-selector")
} else {
// iterate through all the elements, check if it has the class
// remove it if it doesnt have the class
for (let i = 0; i < 7 ; i++) {
if(i !== itemNumber) {
return selector[i].classList.contains("darken-filter-selector") ? selector[0].classList.remove("darken-filter-selector"):null
}
}
}
};
And it should not look like this (what is currently happening)
but rather should only allow for one selected element at a time...
The simplest way is to store the previous itemNumber into global variable like previousItemNumber.
Set the initial value of previousItemNumber to -1 (lower than 0) so that it can be used on changeFilter function.
And on changeFilter function, first, you check if previousItemNumber is existed or not (in other words, already selected item is existed or not when selecting new item) and if existed, remove the className there.
Add the className for the selected item and set the current itemNumber to previousItemNumber.
let previousItemNumber = -1;
const changeFilter = (itemNumber) => {
// grab element that has been clicked
let selector = document.getElementsByClassName(itemNumber);
if (previousItemNumber === itemNumber) {
return;
}
// Remove the class from previous selector
if (previousItemNumber >= 0) {
selector[previousItemNumber].classList.remove("darken-filter-selector");
}
selector[itemNumber].classList.add("darken-filter-selector");
previousItemNumber = itemNumber;
};
You can achieve the desired using input type="checkbox" and a bit of JS:
const EL_checkboxesOne = document.querySelectorAll(".checkboxesOne");
const checkboxesOne = (EL_group) => {
const inputs = EL_group.querySelectorAll('[type="checkbox"]');
EL_group.addEventListener("input", (ev) => {
[...inputs].filter(EL => EL !== ev.target).forEach(EL => EL.checked = false);
});
};
EL_checkboxesOne.forEach(checkboxesOne);
.checkboxesOne {
display: flex;
font: bold 16px/1.4 sans-serif;
color: #666;
}
.checkboxesOne input {
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
clip: rect(0 0 0 0);
clip-path: inset(100%);
}
.checkboxesOne span {
padding: 10px 0;
margin: 0 10px;
user-select: none;
cursor: pointer;
}
.checkboxesOne input:checked + span {
box-shadow: 0 4px green;
}
<div class="checkboxesOne">
<label><input type="checkbox" name="filter" value="all"><span>ALL</span></label>
<label><input type="checkbox" name="filter" value="new"><span>NEW</span></label>
<label><input type="checkbox" name="filter" value="wip"><span>WIP</span></label>
<label><input type="checkbox" name="filter" value="hot"><span>HOT</span></label>
<label><input type="checkbox" name="filter" value="won"><span>WON</span></label>
<label><input type="checkbox" name="filter" value="lost"><span>LOST</span></label>
<label><input type="checkbox" name="filter" value="dnc"><span>DNC</span></label>
</div>
I went with this function, as I thought it was the easiest and required the least modification from my original function
const changeFilter = (itemNumber) => {
// grab element that has been clicked
let selector = document.getElementsByClassName(itemNumber);
// check if element doesnt has the class name that underlines it
if (!selector[0].classList.contains("darken-filter-selector")) {
// add the class if it doesnt have it
selector[0].classList.add("darken-filter-selector");
}
// iterate through all the elements, check if it has the class
// remove it if it doesnt have the class
for (let i = 0; i < 7; i++) {
if (i !== itemNumber) {
let otherEls = document.getElementsByClassName(i);
if (otherEls[0].classList.contains("darken-filter-selector")) {
otherEls[0].classList.remove("darken-filter-selector");
}
}
}
};
This is my suggestion. It uses Event Delegation. Change the style as you like.
options.onclick = function(e) {
if (e.target.tagName != "LI") return;
var selected = options.querySelector(".active");
if (selected) {
selected.removeAttribute("class");
}
e.target.setAttribute("class", "active");
console.log(e.target.dataset.option);
};
#options {
display: flex;
justify-content: center;
}
#options li {
margin: 0 10px 0 10px;
padding: 10px;
cursor: pointer;
list-style: none;
}
#options li.active {
background-color: green;
pointer-events: none;
border-radius: 5px;
}
<ul id="options">
<li data-option="all">ALL</li>
<li data-option="new">NEW</li>
<li data-option="wip">WIP</li>
<li data-option="hot">HOT</li>
<li data-option="won">WON</li>
<li data-option="lost">LOST</li>
<li data-option="dnc">DNC</li>
</ul>
You can also do without using Data Attribute (data-option="..."): just use e.target.innerHTML.
Related
I am currently making use of the following code which helps me in hiding and displaying the choices. But I am unable to hide and unselect them if choice 1 is unchecked.
var x= jQuery("#"+this.questionId+" input[choiceid=2]").closest("li").hide();
var y = jQuery("#"+this.questionId+" input[choiceid=3]").closest("li").hide();
this.questionclick = function(event, element) {
var selectedChoice = this.getSelectedChoices()
console.log(selectedChoice) //use this to get the value of the choice when you want the textbox to appear
if (selectedChoice == "1") {
x.show();
y.show();
alert(selectedChoice);
}
else if (selectedChoice == "2") {
//x.hide();
//y.hide();
alert(selectedChoice+"Else if");
}
else{
x.hide();
y.hide();
alert(selectedChoice+"Else ");
}
}
Some help would be greatly appreciated
Your question does not contain html that you are using. Here is a small demo I have created to demonstrate the grouped checkboxes and binding on click event with them. Play and do changes as per your need.
https://stackblitz.com/edit/grouped-checkboxes-binding-onclick-function
this keyword inside the function refers to the checkbox clicked. you can further checks as you do on normal html checkbox element. e.g this.checkedmeans document.getElementById("myCheck").checked to check if checkbox is checked or not.
HTML
<div class="question">
<h2 class="q-1">Click to write the Question text</h2>
</div>
<ul class="options-list" id="options-list">
<li>
<label>
<input type="checkbox" id="q1-opt1" name="q1['opt1']">
Click to write choice 1
</label>
</li>
<li>
<label>
<input type="checkbox" id="q1-opt2" name="q1['opt2']">
Click to write choice 2
</label>
</li>
<li>
<label>
<input type="checkbox" id="q1-opt3" name="q1['opt3']">
Click to write choice 3
</label>
</li>
<li>
<label>
<input type="checkbox" id="q1-opt4" name="q1['opt4']">
Click to write choice 4
</label>
</li>
</ul>
CSS
.options-list {
list-style-type: none;
margin: 0;
padding: 0;
}
.options-list li label {
display: block;
background: #ddd;
border-radius: 8px;
padding: 10px 20px;
margin: 0 0 10px;
cursor: pointer;
}
.options-list li label:hover {
background: #ccc;
}
.options-list li label > input {
display: none;
}
JS
(function() {
// get questions that you want to disable enable
var q1opt1 = document.getElementById("q1-opt2");
var q1opt2 = document.getElementById("q1-opt3");
// get list wrapping element of all checkboxes
var el = document.getElementById('options-list');
// get all checkboxes inside wrapping element
var options = el.getElementsByTagName('input');
// assign a function each checkbox on click
for( var i=0, len=options.length; i<len; i++ ) {
if ( options[i].type === 'checkbox' ) {
options[i].onclick = function(e) {
// if checkbox id is q1-opt1
// and is checked is checking if this is selected.
// checkbox is hidden with css
// play with the code
if ( this.id == 'q1-opt1' && this.checked ) {
q1opt1.parentElement.style.display = "none";
q1opt2.parentElement.style.display = "none";
} else {
q1opt1.parentElement.style.display = "block";
q1opt2.parentElement.style.display = "block";
}
}
}
}
})();
I'm studying a project by Wes Bos where you were tasked with adding shift click functionality to a series of list items.
I completed this and wanted to go further by then adding the ability to deselect these list items which I did (see commented javascript code).
Then I wanted to take that solution and apply DRY principles and that's where things became difficult.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hold Shift to Check Multiple Checkboxes</title>
</head>
<body>
<style>
html {
font-family: sans-serif;
background: #ffc600;
}
.inbox {
max-width: 400px;
margin: 50px auto;
background: white;
border-radius: 5px;
box-shadow: 10px 10px 0 rgba(0,0,0,0.1);
}
.item {
display: flex;
align-items: center;
border-bottom: 1px solid #F1F1F1;
}
.item:last-child {
border-bottom: 0;
}
input:checked + p {
background: #F9F9F9;
text-decoration: line-through;
}
input[type="checkbox"] {
margin: 20px;
}
input:hover {
cursor: pointer;
}
p {
margin: 0;
padding: 20px;
transition: background 0.2s;
flex: 1;
font-family:'helvetica neue';
font-size: 20px;
font-weight: 200;
border-left: 1px solid #D1E2FF;
}
</style>
<!--
The following is a common layout you would see in an email client.
When a user clicks a checkbox, holds Shift, and then clicks another checkbox a few rows down, all the checkboxes inbetween those two checkboxes should be checked.
-->
<div class="inbox">
<div class="item">
<input type="checkbox">
<p>This is an inbox layout.</p>
</div>
<div class="item">
<input type="checkbox">
<p>Check one item</p>
</div>
<div class="item">
<input type="checkbox">
<p>Hold down your Shift key</p>
</div>
<div class="item">
<input type="checkbox">
<p>Check a lower item</p>
</div>
<div class="item">
<input type="checkbox">
<p>Everything in between should also be set to checked</p>
</div>
<div class="item">
<input type="checkbox">
<p>Try to do it without any libraries</p>
</div>
<div class="item">
<input type="checkbox">
<p>Just regular JavaScript</p>
</div>
<div class="item">
<input type="checkbox">
<p>Good Luck!</p>
</div>
<div class="item">
<input type="checkbox">
<p>Don't forget to tweet your result!</p>
</div>
</div>
<script>
const input = document.querySelectorAll('.item input[type="checkbox"]');
let lastchecked;
function checkFunction (event) {
let inbetween = false;
if (event.shiftKey && this.checked) {
tickBox(true);
} else if (event.shiftKey && !this.checked) {
tickBox(false);
}
lastChecked = this;
}
function tickBox(boolean) {
input.forEach(box => {
if (box === this || box === lastChecked) {
inbetween = !inbetween;
}
if (inbetween) {
box.checked = boolean;
}
});
}
input.forEach(box => box.addEventListener('click', checkFunction));
// const input = document.querySelectorAll('.item input[type="checkbox"]');
// let lastchecked;
//
// function checkFunction (event) {
// let inbetween = false;
// if (event.shiftKey && this.checked) {
// input.forEach(box => {
// if (box === this || box === lastChecked) {
// inbetween = !inbetween;
// }
// if (inbetween) {
// box.checked = true;
// }
// });
// } else if (event.shiftKey && !this.checked) {
// input.forEach(box => {
// if (box === this || box === lastChecked) {
// inbetween = !inbetween;
// }
// if (inbetween) {
// box.checked = false;
// }
// });
// }
// lastChecked = this;
// }
//
// input.forEach(box => box.addEventListener('click', checkFunction));
</script>
</body>
</html>
I expected at that point that all I had to do was call the function where the code was repeated, but this time using a boolean parameter, however it then says that the inbetween variable is undefined. I'm confused at this point. If I define it within the new function it just ticks everything and doesn't change the variable back to false etc.
I hope that makes sense. I'd love to know where I'm going wrong for the future.
Thanks all.
I'd suggest declaring tickBox() inside of checkFunction(). This is feasible because it's never called elsewhere. Then, it can have access to the scope of checkFunction() including the inbetween variable:
const input = document.querySelectorAll('.item input[type="checkbox"]');
let lastchecked;
function checkFunction (event) {
let inbetween = false;
function tickBox(boolean) {
input.forEach(box => {
if (box === this || box === lastChecked) {
inbetween = !inbetween;
}
if (inbetween) {
box.checked = boolean;
}
});
}
if (event.shiftKey && this.checked) {
tickBox(true);
} else if (event.shiftKey && !this.checked) {
tickBox(false);
}
lastChecked = this;
}
input.forEach(box => box.addEventListener('click', checkFunction));
FYI, you can simplify your if/else from this:
if (event.shiftKey && this.checked) {
tickBox(true);
} else if (event.shiftKey && !this.checked) {
tickBox(false);
}
to this:
if (event.shiftKey) {
tickBox(this.checked);
}
Which then means that you don't actually need a separate function for tickBox() any more.
I'm trying to create a new li element using a text input. The problem is while the text is appearing, it doesn't to create an actual li element that I can style. The text is there, but it just forms right next to each other in a row. Here is my html:
const button = document.getElementById('submit');
button.addEventListener ("click", () => {
var taskInput = document.getElementById('task').value;
// Creating the text for list item.
if (taskInput === '') { // Prevents empty list item.
alert("You have to type a task!");
} else {
var listItem = document.createElement("li"); // Create li element.
var text = document.createTextNode(taskInput); // Create text for list item.
listItem.appendChild(text); // Append text to li element.
}
//Add new list item to list
var list = document.getElementById("list");
list.appendChild(text);
});
<body>
<h1>Start your list!</h1>
<div class="input">
<input type="text" id="task">
<button id="submit">Create</button>
</div>
<section id="list">
</section>
</body>
just use <ul> instead of <section> and append to it listeItem and not text.
const button = document.getElementById('submit');
button.addEventListener ("click", () => {
var taskInput = document.getElementById('task').value;
// Creating the text for list item.
if (taskInput === '') { // Prevents empty list item.
alert("You have to type a task!");
} else {
var listItem = document.createElement("li"); // Create li element.
var text = document.createTextNode(taskInput); // Create text for list item.
listItem.appendChild(text); // Append text to li element.
}
//Add new list item to list
var list = document.getElementById("list");
list.appendChild(listItem);
});
<body>
<h1>Start your list!</h1>
<div class="input">
<input type="text" id="task">
<button id="submit">Create</button>
</div>
<ul id="list">
</ul>
</body>
You are appending text you need to append listItem. Check this out:
Your code:
list.appendChild(text);
How it should be:
list.appendChild(listItem);
Best!
<section>: Used to either group different articles into different purposes or subjects, or to define the different sections of a single article. ref for more info
You can add the created list element to this <section> <ol> <li> tags at different possible positions as show below.
var listItem = document.createElement("li"); // Create li element.
listItem.innerHTML = `taskInput`;
var list = document.getElementById("sectionlist");
list.appendChild(listItem);
const button = document.getElementById('submit');
button.addEventListener ("click", () => {
var taskInput = document.getElementById('task').value;
if (taskInput !== '') {
var listItem = document.createElement("li"); // Create li element.
listItem.innerHTML = taskInput;
var val = $("input[name='Radios']:checked").val();
var list = document.getElementById("sectionlist");
// https://stackoverflow.com/a/13166249/5081877
var listCount = $("#sectionlist li").length;
var xpath = '//section/ol/li['+listCount+']';
if( val == "Option 1") {
list.appendChild(listItem);
} else if ( val == "Option 2" ) {
var element = document.evaluate(xpath, window.document, null, 9, null ).singleNodeValue;
list.insertBefore(listItem, element);
} else if ( val == "Option 3" ) {
// https://stackoverflow.com/a/2470148/5081877
var newElem = new XMLSerializer().serializeToString(listItem);
list.insertAdjacentHTML('afterbegin', newElem);
}
} else {
alert("You have to type a task!");
}
});
body {margin: 2em; font-family: Arial, Helvetica, sans-serif;}
span {
/* style this span element so we can display nicely, this stlying is not neccessary */
margin: 10px 0;
display: block;
width: 100%;
float: left;
}
input[type="radio"] {
/* hide the inputs */
opacity: 0;
}
/* style your lables/button */
input[type="radio"] + label {
/* keep pointer so that you get the little hand showing when you are on a button */
cursor: pointer;
/* the following are the styles */
padding: 4px 10px;
border: 1px solid #ccc;
background: #efefef;
color: #aaa;
border-radius: 3px;
text-shadow: 1px 1px 0 rgba(0,0,0,0);
}
input[type="radio"]:checked + label {
/* style for the checked/selected state */
background: #777;
border: 1px solid #444;
text-shadow: 1px 1px 0 rgba(0,0,0,0.4);
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<body>
<div id="myFORM">
<h3>Insert Element into the DOM tree at a specified position.</h3>
<span>
<input id="Radio1" name="Radios" type="radio" value="Option 1" checked="checked">
<label for="Radio1">Node.appendChild() - Insert as END Ele.</label>
</span>
<span>
<input id="Radio2" name="Radios" type="radio" value="Option 2" >
<label for="Radio2">Node.insertBefore() - Insert as Last but one Ele.</label>
</span>
<span>
<input id="Radio3" name="Radios" type="radio" value="Option 3" >
<label for="Radio3">Element.insertAdjacentHTML() - Insert as Start Ele.</label>
</span>
</div>
<h1>Start your list!</h1>
<div class="input">
<input type="text" id="task">
<button id="submit">Create</button>
</div>
<section id="list">
<ol id="sectionlist">
<li>Default list Data</li>
</ol>
</section>
</body>
For more information regarding inserting element you can refer my previous post.
#See
Element.insertAdjacentElement()
Element.insertAdjacentHTML()
Node.appendChild()
Node.insertBefore()
I have two parent divs: .inputs and .infoBoxes. Each of them have an equal number of children. When the user clicks into the first .input in .inputs, the first .infoBox in .infoBoxes should slideDown(). Same for second, third, etc. I'd like to do this without re-writing the same code for each pair. So far I have:
var $inputs = $('.inputs').children();
var $infoBoxes = $('.infoBoxes').children();
for(var i = 0; i < $inputs.length; i++ ) {
$($inputs[i]).find('.input').focus(function() {
$($infoBoxes[i]).slideDown();
})
$($inputs[i]).find('.input').blur(function() {
$($infoBoxes[i]).slideUp();
})
}
This isn't working but I have tried replacing i with the indexes of each div.
$($inputs[0]).find('.input').focus(function() {
$($infoBoxes[0]).slideDown();
})
$($inputs[0]).find('.input').blur(function() {
$($infoBoxes[0]).slideUp();
})
repeat...
repeat...
repeat...
This works but isn't very DRY. I'm looking for a better solution that won't have me repeating a bunch of code.
First code will not work, because you using same variable for all internal functions. You should wrap it into function, which will create local variable for index. Try following code:
var $inputs = $('.inputs').children();
var $infoBoxes = $('.infoBoxes').children();
for(var i = 0; i < $inputs.length; i++ ) {
(function(ix) {
$($inputs[ix]).find('.input').focus(function() {
$($infoBoxes[ix]).slideDown();
})
$($inputs[ix]).find('.input').blur(function() {
$($infoBoxes[ix]).slideUp();
})
})(i);
}
slideDown is used for showing elements. I am guessing you want to hide elements, since you are clicking on them and you cant click an hidden element. Use hide or slideUp to hide elements.
$(".input, .infobox").on("click", function() {
var ind = $(this).index();
$(".infobox:eq(" + ind + "), .input:eq(" + ind + ")").hide(500);
});
.input,
.infobox {
widht: 100%;
height: 50px;
text-align: center;
margin: 5px 0;
color: white;
}
.input {
background: red;
}
.infobox {
background: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="inputs">
<div class="input">1</div>
<div class="input">2</div>
<div class="input">3</div>
<div class="input">4</div>
<div class="input">5</div>
</div>
<div class="infoboxes">
<div class="infobox">1</div>
<div class="infobox">2</div>
<div class="infobox">3</div>
<div class="infobox">4</div>
<div class="infobox">5</div>
</div>
I have created a modal with checkboxes that when checked, are added to the DOM. The issues that I am having that I have been trying to troubleshoot for days are that whether the checkboxes are checked or unchecked, the tag is added to the DOM, not just when checked.
I also cannot figure out how to remove the tag from the DOM when the associated checkbox is unchecked. I have the amount of checkboxes that are able to be checked max out at 6, which is what I am looking to have, but is there a way to max the amount of child divs within a parent div there could be? That way theres another safeguard to fall back on so that no more than 6 tags can be selected at one time?
Here is a jsfiddle http://jsfiddle.net/co5w7c9j/ with what I have, hopefully I explained enough without making it sound too confusing.
Below is my jquery that I have written thus far, I think I am missing a step somewhere to achieve what I am looking for.
Thank you for taking the time to look through my code.
// When specilaty is checked, add tag to profile page
$('[name=specialty]').click(function() {
$newTag = $("<div class='specTag'>" + $(this).attr('value') + "<div class='xOut'>x</div></div>");
$(this).attr('value');
$('.Specialties').append($newTag);
/* if ($('.Specialties > .specTag').has(('[name=specialty]:checked').attr('value'))) {
$('.Specialties > .specTag').has((this).txt()).remove();
} */
// Count number of checkboxes selected and display in modal
var increment = 0;
$('[name=specialty]:checked').each(function() {
if (this.checked) {
increment++;
} else {
increment--;
}
$('#specCount').html(increment);
});
// Disable checkboxes when 6 (maximum) are selected
$("input[type=checkbox][name=specialty]").click(function() {
var bol = $("input[type=checkbox][name=specialty]:checked").length >= 6;
$("input[type=checkbox][name=specialty]").not(":checked").attr("disabled", bol);
});
// Create array of checked items - add on checked - remove on uncheck
specialtyArray = $('[name=specialty]:checked').map(function() {
return $(this).val();
// if item is in the array, then remove it from the DOM
if (jQuery.inArray($('[name=specialty]:checked').val(), specialtyArray) > -1) {}
});
console.log(specialtyArray.get());
});
// When Specialties modal closes, uncheck all checked boxes, reset count
$(document.body).on('click', '.close', function() {
$('.modal-body > #updateSpecForm > .columns').children().removeAttr('checked');
$('#specCount').html(0);
})
// Fade out specialty tags when x is clicked
$(document.body).on('click', '.xOut', function() {
$(this).parent().fadeOut('slow');
$(this).parent().remove();
});
Try
// When specilaty is checked, add tag to profile page
$('input[name=specialty]').change(function() {
var value = this.value;
//if checked add a new item else remove item.
if (this.checked) {
var $newTag = $("<div class='specTag'>" + value + "<div class='xOut'>x</div></div>").attr('data-id', value);
$('.Specialties').append($newTag);
} else {
//use the attribute value which is the same as the input value to find out the item to be removed
$('.Specialties').find('div.specTag[data-id="' + value + '"]').remove()
}
//cache the result since it is used multiple times
var $checked = $('input[name=specialty]:checked');
// Count number of checkboxes selected and display in modal
var increment = $checked.length;
$('#specCount').html(increment);
// Disable checkboxes when 6 (maximum) are selected
var bol = increment.length >= 6;
//use prop instead of attr to set the disabled state
$("input[type=checkbox][name=specialty]").not(":checked").prop("disabled", bol);
// Create array of checked items - add on checked - remove on uncheck
var specialtyArray = $checked.map(function() {
return $(this).val();
});
console.log(specialtyArray.get());
});
// When Specialties modal closes, uncheck all checked boxes, reset count
$(document.body).on('click', '.close', function() {
$('.modal-body > #updateSpecForm > .columns').children().prop('checked', false);
$('#specCount').html(0);
})
// Fade out specialty tags when x is clicked
$(document.body).on('click', '.xOut', function() {
$(this).parent().fadeOut('slow', function() {
$(this).remove();
});
//uncheck the corresponding checkbox
$('input[name=specialty][value="' + $(this).closest('.specTag').attr('data-id') + '"]').prop('checked', false)
});
.Specialties {
background-color: #FFFFFF;
width: 350px;
height: 135px;
margin-left: 249px;
margin-top: 125px;
top: 0;
position: absolute;
z-index: 1;
}
.specTag {
background-color: #51b848;
color: #FFFFFF;
font-weight: 200;
letter-spacing: 1px;
font-size: 12px;
width: 150px;
height 30px;
padding: 8px;
position: relative;
margin-left: 10px;
margin-bottom: 5px;
border-radius: 5px;
display: inline-block;
}
.xOut {
background-color: #FFFFFF;
width: 25px;
padding: 3px;
position: absolute;
right: 5px;
text-align: center;
color: #333333;
top: 5px;
border-radius: 0 3px 3px 0;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<form action="#" method="GET" id="updateSpecForm">
<!-- ATHLETIC TRAINER OPTIONS -->
<div class="columns" id="athleticTrainer">
<input type="checkbox" name="specialty" value="Boot Camp" />Boot Camp
<br />
<input type="checkbox" name="specialty" value="Children's Fitness" />Children's Fitness
<br />
<input type="checkbox" name="specialty" value="Circuit Training" />Circuit Training
<br />
<input type="checkbox" name="specialty" value="Core Training" />Core Training
<br />
<input type="checkbox" name="specialty" value="Cycling/Spinning" />Cycling/Spinning
<br />
<input type="checkbox" name="specialty" value="Dance" />Dance
<br />
<input type="checkbox" name="specialty" value="Flexibility/Balance" />Flexibility/Balance
<br />
<input type="checkbox" name="specialty" value="Meal Planning" />Meal Planning
<br />
<input type="checkbox" name="specialty" value="Men's Fitness" />Men's Fitness
<br />
<input type="checkbox" name="specialty" value="Women's Fitness" />Women's Fitness
<br />
</div>
<div class="Specialties">
<!-- SHOW BELOW DIV ONLY IF LOGGED IN -->
<!-- <div class="updateOn">+ Update My Specialties</div> -->
<!-- ***PRO CAN ADD UP TO 6 SPECIALY TAGS*** -->
</div>
</form>
Sometimes it's easier to compartmentalize code by setting parts of it into functions so that conditional aspects are easier to read through .
The biggest issue in your code was not testing if checkboxes were checked or not in the click handler.
Since the checkbox needs to do the same as the click on new tag does when it is unchecked, all logic flows through the change event of checkbox. Note that the click handler on X of tag triggers the change also
var maxChecked = 6;
// use change handler on checkboxes, will get triggered also below in another click handler
var $checkboxes = $('[name=specialty]').change(function() {
var value = $(this).val();
if(this.checked ){
addTag( value);
}else{
removeTag( value );
}
checkBoxStatus();
});
function removeTag(checkBoxValue){
/* we stored the checkbox value as data attribute, use that to filter*/
$('.specTag').filter(function(){
return $(this).data('value') === checkBoxValue;
}).slideUp(function(){
$(this).remove();
})
}
function addTag( checkBoxValue){
$newTag = $("<div class='specTag'>" + checkBoxValue + "<div class='xOut'>x</div></div>");
/* store the value in elment data so we can reference back to checkbox */
$newTag.data('value', checkBoxValue);
$('.Specialties').append($newTag);
}
/* use this to both disable and enable checkboxes */
function checkBoxStatus(){
var limitReached = $checkboxes.filter(':checked').length === maxChecked;
$checkboxes.not(':checked').prop('disabled',limitReached);
}
$(document.body).on('click', '.xOut', function () {
var $element = $(this).parent(),
$checkbox = $checkboxes.filter(function(){
return this.value === $element.data('value');
/* trigger change to remove element and reset disabled checkboxes */
}).prop('checked',false).change();
});
DEMO
Working fiddle:
http://jsfiddle.net/co5w7c9j/1/
// When specilaty is checked, add tag to profile page
$('[name=specialty]').click(function() {
$newTag = $("<div class='specTag'>" + $(this).attr('value') + "<div class='xOut'>x</div></div>");
$(this).attr('value');
$('.Specialties').append($newTag);
EnableDisableCheck();
// Create array of checked items - add on checked - remove on uncheck
specialtyArray = $('[name=specialty]:checked').map(function(){
return $(this).val();
// if item is in the array, then remove it from the DOM
if (jQuery.inArray($('[name=specialty]:checked').val(), specialtyArray) > -1) {
}
});
console.log(specialtyArray.get());
});
// When Specialties modal closes, uncheck all checked boxes, reset count
$(document.body).on('click', '.close', function () {
$('.modal-body > #updateSpecForm > .columns').children().removeAttr('checked');
$('#specCount').html(0);
})
// Fade out specialty tags when x is clicked
$(document.body).on('click', '.xOut', function () {
$(this).parent().fadeOut('slow');
$(this).parent().remove();
var text = $(this).parent().text();
$('[name=specialty]:checked').filter(function () {
return text.indexOf($(this).val()) > - 1;
}).removeAttr('checked');
EnableDisableCheck();
});
function EnableDisableCheck(){
if($('[name=specialty]:checked').length >=5)
{
$('[name=specialty]').attr("disabled","disabled");
}
else
{
$('[name=specialty]').removeAttr("disabled");
}
}