How to change input pseudo class with Javascript? - javascript

I'm trying to build a 'radio button' selection style list, but not actually using input type="radio". My backend developer has advised me I need to create this using type="checkbox" in our specific case. I believe this can be done with JS.
So how can I make it so that when 1 option is in checked state, the other is unchecked using JS? Here is what I have so far:
http://codepen.io/rjtkoh/pen/VLZrMo
<label for="toggle-1">
<input type="checkbox" id="toggle-1">
<div>option A</div>
</label>
<label for="toggle-2">
<input type="checkbox" id="toggle-2">
<div>option B</div>
</label>
and CSS:
/* Checkbox Hack */
input[type=checkbox] {
position: absolute;
top: -9999px;
left: -9999px;
}
/* Default State */
div {
background: green;
width: 400px;
height: 100px;
line-height: 100px;
color: white;
text-align: center;
margin-bottom: 20px;
}
/* Toggled State */
input[type=checkbox]:checked ~ div {
background: red;
}
I've had a look at other threads talking about changing pseudo classes via JS, but my case dealing with input types confuses me.

Why do you need to do this with a checkbox if the sematics work for radio? There may be a legitimate reason, but without knowing this, I'd suggest you use a radio group instead, which will work without any JavaScript whatsoever, you just need to set a common name attribute on both inputs:
input[type=radio] {
position: absolute;
top: -9999px;
left: -9999px;
}
/* Default State */
div {
background: green;
width: 400px;
height: 100px;
line-height: 100px;
color: white;
text-align: center;
margin-bottom: 20px;
}
/* Toggled State */
input[type=radio]:checked + div {
background: red;
}
<label for="toggle-1">
<input type="radio" name="option" id="toggle-1">
<div>option A</div>
</label>
<label for="toggle-2">
<input type="radio" name="option" id="toggle-2">
<div>option B</div>
</label>

Here is a pure javascript solution since I don't see jQuery tags in your question:
If you REALLY need to do this with checkboxes you can do this:
html
<div id="radio_group">
<label for="toggle-1">
<input type="checkbox" id="toggle-1">
<div>option A</div>
</label>
<label for="toggle-2">
<input type="checkbox" id="toggle-2">
<div>option B</div>
</label>
</div>
javascript
var options = document.getElementById('radio_group').childNodes;
var checkboxes = document.getElementsByTagName('input');
function uncheck() {
for (var i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].type == 'checkbox') {
checkboxes[i].checked = '';
}
}
}
function checkBox(e) {
e.preventDefault();
if(e.target.nodeName == 'DIV') {
uncheck();
e.target.previousElementSibling.checked = 'checked';
}
}
for (var i = 0; i < options.length; i++) {
options[i].addEventListener('click', checkBox, false);
}
heres a fiddle --> https://jsfiddle.net/tL68Lsub/

If you really need the checkbox to work as a radio, you can use:
$("input[type='checkbox']").click(function () {
if ($(this).is(":checked")) {
$("input[type='checkbox']").not(this).removeAttr("checked");
$(this).attr("checked", "true");
}
})

Related

Connecting few "if" conditions

I have 12 <a> tags and they are linked to image. What i want to do is to connect all of them in one if as a .clicked == true. For some reason it does not work so here i am with another question.
A sample of my <a> tag.
<div id="container">
<div id="slider">
</div>
<div id="wypelniacz">
<img style="top: 22%; right: 60%;" alt="" src="greenapple.png" class="apl" id="apple1" onclick="imageSwap(1) ; this.onclick=null;">
</div>
</div>
JS
function imageSwap(id)
{
document.getElementById("apple" + id).src = "redapple.png";
}
To make it easier to explain i have posted a picture of what am i doing.
When apple is not clicked it remains green, but when i click on it, it will change to red.
Then, when all 12apples are red the whole div copntent will be deleted and swapped for one image. How can i possibly do that? Thanks in advance!
So add a class for attribute and check how many you have
function imageSwap(id) {
var img = document.getElementById("apple" + id);
img.src = "redapple.png";
img.classList.add("selected");
var count = document.querSelectorAll(".selected").length;
console.log(count);
}
or just with html and css
document.querySelector(".tree").addEventListener("change", function(evt) {
const checked = document.querySelectorAll(".apple:checked").length;
console.log(checked);
console.log(evt.target.form.checkValidity());
});
.apple {
display: none;
}
.apple + label {
display: inline-block;
width: 100px;
height: 100px;
background-image: url(http://placekitten.com/g/100/100);
}
.apple:checked + label {
background-image: url(http://placekitten.com/100/100);
}
<form class="tree">
<input type="checkbox" class="apple" id="apple1" required><label for="apple1"></label>
<input type="checkbox" class="apple" id="apple2" required><label for="apple2"></label>
<input type="checkbox" class="apple" id="apple3" required><label for="apple3"></label>
<input type="checkbox" class="apple" id="apple4" required><label for="apple4"></label>
<input type="checkbox" class="apple" id="apple5" required><label for="apple5"></label>
<button>Next</button>
</form>
Use event delegation, that is I attach a click event to '.container', and use 'event.target' to manipulate the target apple
toggle target apple class to switch background-color
check all apples, if there is no '.red', then switch to a new image(Here I just remove the '.red' class all)
document.addEventListener('DOMContentLoaded', ev => {
const apples = [...document.querySelectorAll('.apple')]
const container = document.querySelector('.container')
container.addEventListener('click', ev => {
if (ev.target.matches('.apple')) {
ev.preventDefault()
ev.target.classList.toggle('red')
const apples = [...container.querySelectorAll('.apple')]
if (apples.every(apple => apple.classList.contains('red'))) {
apples.forEach(apple => apple.classList.remove('red'))
}
}
})
}, false)
div {
display: flex;
flex-flow: row wrap;
justify-content: space-evenly;
align-items: center;
}
.apple {
width: 5rem;
height: 5rem;
border-radius: 5rem;
background: green;
}
.green {
background: green;
}
.red {
background: red;
}
<div class="container">
</div>

Clear objects properties by button click

There are 5 boxes, which can be changed from 'white'<->'yellow' colors by mouse events (mouseover, mouseout and click). There is also a blue area with text displaying the level of the clicked box.
After clicking into the third box, I got 'hard level' text in blue area and 3 boxes color in yellow.
What I need is to return it to the default level ('easy level' and first box in yellow only) by clicking the reset button.
I have been trying do this like this , but it isn't working:
resetBtn = document.querySelector('#update');
and eventlistener:
resetBtn.addEventListener('click', highlightStars(`#star1`), true)
Here is an example:
window.addEventListener('DOMContentLoaded', changeStars, false);
const resetBtn = document.querySelector('#update');
/* Change level of the game depending on user choice */
function changeStars() {
/* Displaying level text inside blue box */
const updateAltText = currentLevelIndex => {
let levelText = document.querySelector('#level-text');
/* 'currentLevelIndex + 1' replaces event 'currentElement' */
levelText.textContent = document.querySelector(`#star${currentLevelIndex + 1}`).alt;
}
/* Captcha level number - default is 1 */
const getNumber = str => Number(str.match(/\d+/)[0]) || 1;
/* Star index is always one number lower than level number (indexing rules) */
const getStarIndex = event => getNumber(event.target.id) - 1;
let stars = document.querySelectorAll('.star');
const handleStarClick = event => {
/* FIRST - blocking possibility to change star behaviour by mouse events */
gameLevel.removeEventListener('mouseover', highlightStars);
gameLevel.removeEventListener('mouseout', highlightStars);
/* SECOND - making all needed star with yellow color */
const stars = document.querySelectorAll('.star');
for (let i = 0; i <= getStarIndex(event); i++) {
stars[i].classList.add('yellow');
}
};
const highlightStars = event => {
const starIndex = getStarIndex(event);
updateAltText(starIndex);
for (let i = 1; i <= starIndex; i++) {
const star = document.querySelector(`#star${i + 1}`);
star.classList.toggle('yellow');
}
};
// resetBtn.addEventListener('click', highlightStars(`#star1`), true);
resetBtn.addEventListener('click', updateAltText(0), true);
const gameLevel = document.querySelector('.game-level');
gameLevel.addEventListener("mouseover", highlightStars);
gameLevel.addEventListener("mouseout", highlightStars);
gameLevel.addEventListener('click', handleStarClick, {once: true});
}
.stars {
display: flex;
margin: 10px auto;
width: 500px;
}
input[type='image'] {
width: 60px;
height: 60px;
border: thin solid black;
}
.yellow {
background-color: yellow;
}
.game-level {
display: flex;
width: 300px;
height: 100%;
}
.level-block {
display: flex;
width: 200px;
margin-left: 10px;
justify-content: center;
align-items: center;
border: 1px solid hsl(217, 86%, 50%);
border-radius: 25px;
background-color: hsl(212, 29%, 80%);
}
.level-block > span {
font-size: 18px;
}
.reset {
width: 80px;
height: 80px;
}
<div class="stars">
<div class="game-level">
<input type="image" class="star yellow" id="star1" src="" width="60" alt="easy level">
<input type="image" class="star" id="star2" src="" width="60" alt="normal level">
<input type="image" class="star" id="star3" src="" width="60" alt="hard level">
<input type="image" class="star" id="star4" src="" width="60" alt="very hard level">
<input type="image" class="star" id="star5" src="" width="60" alt="impossible level">
</div>
<div class="level-block">
<span id="level-text">Easy level</span>
</div>
</div>
<input type="button" class="reset" id="update" value="RESET">
The following demo uses JavaScript for click events only, all mouse events (ie hover) are pure CSS. The reset behavior simply removes .active class on all buttons then adds .active class to the first button. Instead of the first button title being displayed after a reset -- the reset button title: "Game Reset" is displayed, it might be a little confusing for users if there's no confirmation of a reset. Other behavior is included in demo that is logical and consistent such as toggling, hovering to a temporary state and clicking for a persistent state etc. Details are commented in demo.
// Reference the form
const stars = document.forms.stars;
/*
Register the form to the click event -- when a click occurs anywhere on or within the form, callback function twinkle() is
called
*/
stars.onclick = twinkle;
/**
//A -- twinkle passes a reference to the Event Object... (e)
//B1 - Two Event Object properties are used to reference:
The tag the was clicked by user: event.target
The tag registered to the event: event.currentTarget
//B2 - The HTMLFormElement property: .elements collects all form
controls into a Live HTML Collection (aka NodeList)
//C -- ui.star is a Collection of form controls with [name=star]
The brackets [] and spread operator ... converts the
NodeList into an Array
//D -- Reference the message tag. If the clicked tag was the reset
button -- for...of loop iterates through each [name=star]
and removes the class .active from all [name=star]
//E1 - Next add .active class to the default button
//E2 - Set the legend.message text to the value of clicked button
[title] attribute...
~~~~~~~
//F -- ...But if a button.star was clicked, a check to verify if
clicked tag has the .active class -- then a for...of
loop identical to the one described in line D is used to
remove any .active class.
//G -- After there are no .active, the Boolean declared in line F
determines whether the clicked tag gets the .active class
and its [title] attribute displayed or not
*/
function twinkle(e) {
const active = e.target;
const ui = e.currentTarget.elements;
const starZ = [...ui.star];
const msg = document.querySelector(".message");
if (active.matches("#clear")) {
for (let star of starZ) {
star.classList.remove("active");
}
ui.star1.classList.add('active');
msg.textContent = active.title;
} else if (active.matches(".star")) {
let status = active.classList.contains("active");
for (let star of starZ) {
star.classList.remove("active");
}
if (!status) {
active.classList.add("active");
msg.textContent = active.title;
} else {
active.classList.remove("active");
msg.textContent = "";
}
}
return false;
}
:root {
font: 400 small-caps 2.5vw/1 Arial
}
.levels {
display: table;
width: 96%;
height: auto;
border: 1px solid hsl(217, 86%, 50%);
border-radius:4px;
}
.message {
display: table-caption;
width: 40vw;
height: 6vh;
margin: 0 auto 2vh;
padding: 0.5vh 0;
border: 1px solid hsl(217, 86%, 50%);
border-radius: 1.5rem;
background-color: hsla(212, 29%, 80%, 25%);
text-align: center;
font-size: 1.5rem;
color: #0078D7;
}
#clear {
float: right;
transform: rotate(45deg);
padding: 0;
border: none;
background: none;
font-size: 3.5rem;
cursor: pointer;
}
#clear:focus {
outline: 0;
}
/*
Flex is applied to the button.star'S parent tag so the order
property can be utilized.
*/
.flex {
display: flex;
justify-content: space-evenly;
align-items: center;
width: 70vw;
}
.star {
display: table-cell;
position: relative;
width: 16vw;
height: 24vh;
border: thin solid black;
background: #DDD;
font-size: 3.75rem;
text-align: center;
vertical-align: middle;
cursor: pointer;
}
/*
GSC (General Sibling Combinator: ~ ) provides highlighting across
multiple buttons.
Exp. 5 buttons: [-] [-] [X] ~ [>] ~ [>]
*/
.star.active,
.star:hover,
.star.active ~ .star,
.star:hover ~ .star {
background: gold;
}
/*
HTML layout has button.star in reverse order. Applying order to
each button rectifies the order by APPEARING in order while the
HTML structure remains reversed.
*/
#star1 {
order: 1;
border-top-left-radius: 6px;
border-bottom-left-radius: 6px;
}
#star2 {
order: 2;
}
#star3 {
order: 3;
}
#star4 {
order: 4;
}
#star5 {
order: 5;
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
}
#star1:hover,
#star1.active {
color: #5BC0DE;
}
#star2:hover,
#star2.active {
color: #FF1C8D;
}
#star3:hover,
#star3.active {
color: #00D800;
}
#star4:hover,
#star4.active {
color: #0000D5;
}
#star5:hover,
#star5.active {
color: #D50000;
}
<form id="stars" action="">
<fieldset name="levels" class="levels">
<legend class="message">Novice</legend>
<button id="clear" type="reset" title="Game Reset">πŸ”„</button>
<section class="flex">
<button id="star5" name='star' class="star" title="Master">🟐</button>
<button id="star4" name='star' class="star" title="Expert">🟌</button>
<button id="star3" name='star' class="star" title="Advanced">🟊</button>
<button id="star2" name='star' class="star" title="Intermediate">πŸŸ†</button>
<button id="star1" name='star' class="star active" title="Novice">πŸŸ‚</button>
</section>
</fieldset>
</form>

Hide div when clicked outside without using addEventListener

How can I hide a <div> when I click outside it using onblur? I tried with the code below, but when I click the checkbox it disappears, and when I click outside of it, it won’t disappear.
I then tried using window or document object which works, but is not supported by the platform that I’m currently using.
Is this otherwise possible using JavaScript and/or CSS?
var expanded = false;
function showshow() {
var show = document.getElementById("show");
if (!expanded) {
show.style.display = "block";
expanded = true;
} else {
show.style.display = "none";
expanded = false;
}
}
function hideshow() {
var show = document.getElementById("show");
if (expanded) {
show.style.display = "none";
expanded = false;
}
}
#show {
position: absolute;
width: 200px;
display: none;
border: 1px solid #000000;
background-color: #ffffff;
}
#show label {
display: block;
white-space: nowrap;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
#show label:hover {
background-color: #eff1f4;
}
<form id="input-form">
<button type="button" onclick="showshow()">Select an option</button>
<div id="show" tabindex="1" onblur="hideshow()">
<label for="OptionA">
<input type="checkbox" id="OptionA" value="Option A" />Option A</label>
<label for="OptionB">
<input type="checkbox" id="OptionB" value="Option B" />Option B</label>
<label for="OptionC">
<input type="checkbox" id="OptionC" value="Option C" />Option ABCDEFGHIJKLMNOPQRSTUVWXYZ</label>
</div>
</form>
if you can't use addEventListener on your platform maybe you can try attachEvent that will do the job on old platform.
Exemple:
if (document.addEventListener) { // For all major browsers, except IE 8 and earlier
document.addEventListener("click", myFunction);
} else if (document.attachEvent) { // For IE 8 and earlier versions
document.attachEvent("onclick", myFunction);
}
Best regards :)
You will have to call the hideshow function when the click event is fired on the document's body, then check if the event's target is document.body:
var expanded = false;
function showshow(event) {
var show = document.getElementById("show");
if (!expanded) {
show.style.display = "block";
expanded = true;
} else {
show.style.display = "none";
expanded = false;
}
}
function hideshow(event) {
var show = document.getElementById("show");
var elem = event.target;
// Check if the event's target is the document's body
if(elem && elem.id == "main") {
show.style.display = "none";
expanded = false;
}
}
document.body.onclick = hideshow;
html,
body,
#main {
height: 100%;
width: 100%;
}
#show {
position: absolute;
width: 200px;
display: none;
border: 1px solid #000000;
background-color:#ffffff;
}
#show label {
display: block;
white-space: nowrap;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
#show label:hover {
background-color: #eff1f4;
}
<main id="main">
<button type="button" id="toggle-modal" onclick="showshow(event)">Select an option</button>
<div id="show" tabindex="1" onblur="hideshow(event)">
<label for="OptionA">
<input type="checkbox" id="OptionA" value="Option A" />
Option A
</label>
<label for="OptionB">
<input type="checkbox" id="OptionB" value="Option B" />Option B
</label>
<label for="OptionC">
<input type="checkbox" id="OptionC" value="Option C" />Option ABCDEFGHIJKLMNOPQRSTUVWXYZ
</label>
</div>
</main>

unbind the initial click function on another button click

I have a form which is initially hidden.
There is two steps of click flow to reach the form. I need to reset the initial click action when the form close button is clicked.
Adding the sample code and demo.
Here goes the flow
Click on the Join Now text
Then click on the text Click here to open the form text
Then click on the close icon
There comes the pop up
What I now need to know is,
By clicking on Yes , the form needs to be reset and the screen should look like how it looked initially (Brand name box with join now text) and the initial click action of join now should be refreshed.
By clicking on No, it should remain on the same form.
$(".button").click(function(e){
e.preventDefault();
$(this).hide();
$(".slidethis").fadeIn(800).css("display","inline-block");
$(".wrapper").css("display","block");
});
$(".seconddiv").hide();
//forstdiv click
$(".firstdiv").click(function(){
$(this).hide();
$(".seconddiv").show();
});
//Close button
$(".close_icon").click(function(){
$(".popup").show();
});
Demo Here
P.S: I don't want to refresh the page by closing the form.
Adding this code should do it. It's about reversing your steps:
$("input[value=Yes]").click(function(){
//reset - reverse all the steps
$(".button").show();
$(".slidethis").fadeOut(800).css("display","none");
$(".wrapper").css("display","inline-block");
$(".popup").hide();
$(".seconddiv").hide();
$(".firstdiv").show();
});
$("input[value=No]").click(function(){
$(".popup").hide();
});
Updated fiddle: https://jsfiddle.net/5353ntuf/5/
Working fiddle.
You should add an event click on the both buttons Yes and No, so give them a common class , e.g confirm class:
<input type="button" class='confirm' value="Yes" />
<input type="button" class='confirm' value="No" />
Then add a condition to check which action you're going to perform, like :
//Confirm buttons
$("body").on("click", ".confirm", function() {
$(".popup").hide();
if ($(this).val() == 'No')
{
$('form input').val(""); //Reset form
} else {
$(".seconddiv,.slidethis").hide();
$(".firstdiv,.button").show();
$(".wrapper").css("display", "inline-block");
}
});
Hope this helps.
//slide open the main panel
$(".button").click(function(e) {
e.preventDefault();
$(this).hide();
$(".slidethis").fadeIn(800).css("display", "inline-block");
$(".wrapper").css("display", "block");
});
$(".seconddiv").hide();
//forstdiv click
$(".firstdiv").click(function() {
$(this).hide();
$(".seconddiv").show();
});
//Close button
$(".close_icon").click(function() {
$(".popup").show();
});
//Confirm buttons
$("body").on("click", ".confirm", function() {
$(".popup").hide();
if ($(this).val() == 'No') {
$('form input').val("");
} else {
$(".seconddiv,.slidethis").hide();
$(".firstdiv,.button").show();
$(".wrapper").css("display", "inline-block");
}
});
.wrapper {
background: #9ac366;
display: inline-block;
}
.headcard {
display: inline-block;
text-align: center;
padding: 3% 30px;
float: left;
}
.slidethis {
background: #b67fd8;
padding: 20px 0;
width: 70%;
vertical-align: top;
position: relative;
height: 228px;
display: none;
position: relative
}
.firstdiv,
.seconddiv {
width: 200px;
border: #888 solid 2px;
padding: 20px;
}
.close_icon {
background: #333;
color: #fff;
padding: 4px;
float: right;
margin-top: -20px;
text-decoration: none
}
.popup {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
background: #e4e4e4;
text-align: center;
padding-top: 5%;
display: none
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div class="headcard">
<h4>Brand Name</h4>
Join Now
</div>
<!--this is hidden by default-->
<div class="slidethis">
<div class="firstdiv">
Click here to open the form
</div>
<div class="seconddiv">
<form>
X
<input placeholder="name" />
<input placeholder="email" />
</form>
<!--close pop up-->
<div class="popup">
Closing will clear the form data. Do you want ot close?
<br/>
<input type="button" class='confirm' value="Yes" />
<input type="button" class='confirm' value="No" />
</div>
</div>
</div>
</div>

Setting a limit on number of checkboxes that can be chosen

I have searched other similar questions on here and tried to replicate them, but I am unsure what I am doing wrong.
I am only wanting one checkbox to be able to be checked. I set the limit with limitCal and am checking the siblings.
Anyone see what I am doing wrong?
jQuery.fn.fadeBoolToggle = function (bool) {
return bool ? this.fadeIn(400) : this.fadeOut(400);
}
function packageSelect() {
var limitCal = 1;
$('.calendar-check').on('change', function () {
$(this).parents('.product-wrap:first').find('.checkmark-img').fadeBoolToggle(this.checked);
if ($(this).siblings(':checked').length >= limitCal) {
this.checked = false;
}
$('#next1').fadeBoolToggle($('.product-check:checked').length > 0);
var prods = [];
$('.calendar-check:checked').each(function () { prods.push($(this).val()) });
});
};
packageSelect();
.calendar-check {
display: none;
}
.product-wrap {
width: 100%;
position: relative;
display: block;
}
.checkmark-img {
display: none;
width: 40%;
height: auto;
z-index: 1;
cursor: pointer;
}
.package-check-toggle {
position: relative;
height: 100%;
width: 100%;
display: block;
}
.package-setup {
display: none;
margin: 40px 0;
width: 100%;
}
#calendar-box-wrap {
margin: 20px 0;
}
.calendar-box {
display: inline-block;
vertical-align: top;
width: 25%;
margin: 0 4%;
position: relative;
}
.calendar-selection-img {
width: 100%;
height: auto;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="calendar-box">
<div class="product-wrap">
<label for="cal-2year" class="package-check-toggle">
<img src="images/calendar-package.png" alt="2-year Photo Gift" class="calendar-selection-img">
<img src="images/checkmark-circle.png" class="checkmark-img total-center">
</label>
<input type="checkbox" class="calendar-check" id="cal-2year" value="2-year Calendar">
</div>
</div><div class="calendar-box">
<div class="product-wrap">
<label for="cal-whiteboard" class="package-check-toggle">
<img src="images/calendar-package.png" alt="Whiteboard Photo Gift" class="calendar-selection-img">
<img src="images/checkmark-circle.png" class="checkmark-img total-center">
</label>
<input type="checkbox" class="calendar-check" id="cal-whiteboard" value="Whiteboard Calendar">
</div>
</div>
Here's a simplified version of what you're trying to do. Basically, intercepting the click event of the checkbox allows you to prevent the default action (checking the checkbox). The value of checked for this is going to be the value it would be if the click is successful. So, if the checkbox would be unchecked, let the click happen. Also let the click happen if the number of checked checkboxes is less than or equal to the limit. Otherwise, stop the checkbox from getting checked. I've set the limit for this example to 2 just for demonstration.
var limit = 2;
$('input:checkbox').on('click', function (e) {
if (!this.checked || $('input:checkbox:checked').length <= limit) {
return true;
}
return false;
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="checkbox">
<input type="checkbox">
<input type="checkbox">
<input type="checkbox">
const allCheckBoxs = document.querySelectorAll("input")
let limit = 2
allCheckBoxs.forEach( el => {
el.addEventListener("click", e => {
if(el.checked){
limit--
if(limit < 0){
e.preventDefault();
console.log("Sorry, you are over the limit")
}
}else{
limit++
console.log(limit)
}
})
})
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />
<input type="checkbox" />

Categories