I am building a very basic guessing game using jquery and html. I have 6 checkboxes, and if the right sequence is triggered, a hidden div appears. In order to trigger the div, I need to select 1,2,and 3. If you select 1,2,and 4, you get a secret message (div2), and otherwise nothing happens.
I can do the trigger easily by doing nested clicks:
$("#1").click(function(){
$("#2").click(function(){
$("#3").click(function(){
$("#div1").fadeIn();
});
});
});
html:
<input type="checkbox" id="#1">
<input type="checkbox" id="#2">
<input type="checkbox" id="#3">
<input type="checkbox" id="#4">
<input type="checkbox" id="#5">
<input type="checkbox" id="#6">
<div id="div1" style="width:30px;height:30px;display:none;background-color:blue;"></div>
<div id="div2" style="width:30px;height:30px;display:none;background-color:yellow;"></div>
But I am having trouble making it disappear.
If any one of the three is not pressed, I would like for that div to disappear. So let's say you press 1,2,3, div1 appears, and if you deselect 3, that div1 disappears.
I think I can make the question easier to phrase by phrasing it like this: i want to tell jquery- if one, and two, and three, are not 'all' selected, fade out the div.
Rather than using nested clicks, which will get complicated and confusing, you'd be better off creating a generalised listener that will maintain a list of what has/hasn't been clicked. Not only is this easier to maintain, it is also more optimal than having many click handlers assigned.
Others out there who may wish to optimise further may say correctly that you could write this code to directly generate a checked array, rather than a checked object, the reason I have kept it as an object is to support the possibility of a string-based ident rather than just numerical.
updated code
Previous code was slightly buggy, this version now works correctly when you select more checkboxes than you should.
reasons why
why change to use classes more than ids
Whilst ids are very specific, and will be more optimal for the browser to select by, they generally cause confusion and make things laborious, especially in markup that you wish to duplicate (obviously because ids have to be unique). It is often far better to come up with a solution that can work on a general grouping class, than having to label each element with a sequence i.e. cb1, cb2, cb3. As you can see my markup does label the checkboxes sequentially but the code only worries about the grouping class .cb, leaving the sequential classes really only for css styling.
why add a container div
When working on html5 apps, container divs will help you out 9 times out of 10. If you have a collection of elements that will only ever reside in a close visual formation, you will do yourself a favour by wrapping them. This helps when dynamically generating more elements (you can append your new elements directly to the container), it can help with delegating event listeners, and when targeting the elements via jQuery and CSS.
why use change instead of click for checkboxes
change is the event specifically designed to trigger when a change of value occurs, click is designed to fire when a click occurs. You should use the event that best suits what you want. In this case you only wish to update when a checkbox has changed it's value, which can happen with or without a mouse. True, some browsers fire the click event when using keyboard events, but it is better to be clear.
why use data-ident
ids should be used for quick look-up purposes, classes should be used to classify and group, if you have any other information to add to an element you should use the data- prefix. This means you aren't limited by what characters id and class support, and changing data- values doesn't cause any real internal calculations to fired by the browser i.e. applied classes or element registration.
how this code could be improved
The problem with making code more accessible and readable means that it's easier to work out what the code means, and this is bad for a game that should try and hide the solutions away from it's user-base. If this is just a simple game then there isn't much to worry about, but if you are working with something a bit more serious you should try and find a way to obfuscate the solutions :)
working fiddle:
http://jsfiddle.net/RFK92/
code:
/// your list of what is checked
var checked = {};
var updateDivs = function(){
var ident, show, checklist = [];
/// create a useful string from what has been checked
for ( ident in checked ) {
if ( checked[ident] ) {
checklist.push(ident);
}
}
checklist = checklist.join(',');
if ( checklist == '1,2,3' ) {
show = $('#div1');
}
else if ( checklist == '1,2,4' ) {
/// show something else, or not...
}
/// by using a grouping class you can find all divs that could be affected
$('.only-one-div').not(show).fadeOut();
if ( show ) {
/// and single one out for reveal
show.fadeIn();
}
};
$('.cb').change(function(){
var cb = $(this), ident = cb.data('ident');
/// keep track of what is or not checked
checked[ident] = cb.prop('checked'); /// updated to use prop!
/// update your divs
updateDivs();
});
markup:
<div class="cbs">
<input type="checkbox" class="cb cb1" data-ident="1" />
<input type="checkbox" class="cb cb2" data-ident="2" />
<input type="checkbox" class="cb cb3" data-ident="3" />
<input type="checkbox" class="cb cb4" data-ident="4" />
<input type="checkbox" class="cb cb5" data-ident="5" />
<input type="checkbox" class="cb cb6" data-ident="6" />
</div>
<div id="div1" class="only-one-div">one</div>
<div id="div2" class="only-one-div">two</div>
css:
.only-one-div { display: none; }
I would have a look at binding and unbinding your clicks.
Basically, if they have clicked the first proper click then bind the second proper click.
Any incorrect clicks would have you unbind all the clicks and fadeOut the divs and rebind the first necessary click.
http://api.jquery.com/bind/
http://api.jquery.com/unbind/
http://jsfiddle.net/CEb9x/1/
$('input[type=checkbox]').on('change', function(){
if($('input:checked').length == 3){
if ($('.blue:checked').length == 3) { $("#div1").fadeIn(); $("#div2").fadeOut();}
else if ($('input[name="secret"]:checked').length == 3) { $("#div1").fadeOut(); $("#div2").fadeIn(); }
} else { $("#div1, #div2").fadeOut(); }
});
This should do:
$("#1").click(function(){
checkboxClicked();
});
$("#2").click(function(){
checkboxClicked();
});
$("#3").click(function(){
checkboxClicked();
});
$("#4").click(function(){
checkboxClicked();
});
$("#5").click(function(){
checkboxClicked();
});
$("#6").click(function(){
checkboxClicked();
});
...
...
function checkboxClicked() {
if ($('#1').is(':checked') && $('#2').is(':checked') && $('#3').is(':checked') {
$('#div1').show();
$('#div2').hide();
}
else {
$('#div1').hide();
}
if ($('#1').is(':checked') && $('#2').is(':checked') && $('#4').is(':checked') {
$('#div2').show();
$('#div1').hide();
}
else {
$('#div2').hide();
}
}
Related
I'm trying to change the attribute of an object with removeAttribute to take away the hidden status of it but so far nothing seems to work.
My code seems to have no effect. Am I doing something wrong?
function changePage() {
document.getElementById.("p2");
p2.removeAtribute.("hidden") ;
}
I've also tried it all on one line as well like so
function changePage() {
document.getElementById.("p2").p2.removeAtribute.("hidden") ;
}
I've never seen the use of dots before opening parentheses.
E.g.
document.getElementById.("p2").p2.removeAtribute.("hidden") should be document.getElementById("p2").removeAtribute("hidden")
(You are also referencing the element by id after you just retrieved it, which is unnecessary.)
Your first example didn't work because you retrieved the element and did nothing with it, then tried to access a p2 variable that wasn't declared. Again, you also have the . before parentheses.
Here's the js example:
function changeVisibility()
{
var p2 = document.getElementById('p2');
switch (p2.style.visibility)
{
case 'hidden':
document.getElementById('p2').style.visibility = 'visible';
break;
case 'visible':
document.getElementById('p2').style.visibility = 'hidden';
break;
}
}
<div id="p2" style="visibility:hidden">
test
</div>
<br />
<button onclick="changeVisibility()">
change visibility with basic js
</button>
And here's the jQuery example:
function changePage()
{
$('#p2').toggle();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="p2" style="display:none">
test
</div>
<br />
<button onclick="changePage()">
change visibility with basic js
</button>
The basic JS version uses the visibility style, and you can see that it doesn't collapse the element, it only makes it invisible.
jQuery has a nice built-in .toggle function that changes the display of the element. If it is hidden, it collapses the element. When the element is displayed, it is re-assigned whatever the display style is for that element. Building that in basic js would take a lot more work, as you are then tracking state (if you want to make the method reusable). You can make jQuery work similarly to the basic js version if you use the css properties, but toggle is quite nice and simple.
Your main issue is that you were mixing the getting of the element with methods that are only available on jQuery objects. I suggest reading the jQuery tutorials for basic accessors, which can get elements by id, class name, etc.
<div class="btn-group btn-group-toggle" data-toggle="buttons">
<label class="btn btn-secondary active">
<input type="radio" name="options" id="option1" autocomplete="off"
checked> Active
</label>
<label class="btn btn-secondary">
<input type="radio" name="options" id="option2" autocomplete="off">
Radio
</label>
<label class="btn btn-secondary">
<input type="radio" name="options" id="option3" autocomplete="off">
Radio
</label>
</div>
$().button('toggle') : Toggles push state. Gives the button the appearance that it has been activated.
Screenshot of image of RadioButtonList. From the left-most button you can see that it looks like it has been clicked when it is clicked.
Screenshot of the Login-page that I am currently making. When I click on either of the 'radio' buttons their appearance does not changed, so I have to use the method $().button('toggle'), but I do not know where or how to use it.
You just need to add the correct selector to the jQuery function. Try using this instead:
$(".btn-group").button("toggle");
And now your code should work.
Hopefully this helps!
I came across this question because I was stumped by a similar use case. It's my first time working with jquery, so I was a little confused by the syntax at first. (tl;dr diff between JS/jquery see 4.)
I wanted to expand on the 1st answer because I would have appreciated someone else doing the same. I was working through this problem on Bootstrap 4.5.3 for a couple hours, completely stumped as to why my radio buttons wouldn't show the toggle effect, before I realized there was a library requirement for the button() method, which I did not see in the docs.
The required library: If you want this feature to work "the bootstrap way", the buttons.js library before invoking the jquery script, which in an npm-installed package is located at node_modules/bootstrap/js/dist/
Load + init example:
<script src="node_modules/bootstrap/js/dist/buttons.js">
$('.btn-group').button('toggle');
</script>
you can disaggregate the jquery and loading the script if that helps you keep things straight (separate <script /> tags for each):
<script src="node_modules/bootstrap/js/dist/buttons.js"> </script>
<script>$('.btn-group').button('toggle');</script>
Selecting the proper element for your use case: A slightly more specific way to target the buttons is explained by TWBS in another bootstrap example for tooltips - this is targeting the children of an element by data attribute: https://getbootstrap.com/docs/4.5/components/tooltips/#example-enable-tooltips-everywhere
$('[data-toggle="button"]').button('toggle');
More about selecting element by data attribute here: Selecting element by data attribute with jQuery
Right target for use case continued: You can attach it to elements in any number of ways, here's an example using the IDs of each button, the # hash symbol denoting an ID selector (the . period symbol denoting a class, just like css) - you simply put them in an array and pass that to the button() method:
$(['#option1', '#option2', '#option3']).button('toggle');
This could be classes (starting with a period, e.g. ['.button-one', '.button-two'], etc.
Big picture: If you're seeing the pattern here, jquery uses these
$(DOM ELEMENT).method(SCOPE) statements, which are a more terse form of the syntax used by JS, which would be a lot more verbose, something like:
let lis = document.getElementsByTagName('li');
let firstLi = lis[0];
firstLi.addEventListener('click', () => console.log('clicked first li'))
So it's basically the same, just way less verbose. Since jquery was developed specifically to handle the DOM, this makes sense.
If you want to achieve the same look without loading the bootstrap buttons.js library, you can attach/remove the .active class, which when attached mutes the appearance of the radio button, to the label of the buttons in question with something like:
$('.btn').on('click', function () {
$(this).siblings('label').removeClass('active');
$(this).addClass('active');
});
Statement exmplained: When an element with the .btn class is clicked, it will remove all .active classes from any sibling elements with the tag type label, and add the .active class to the one you clicked.
If you examine the button.js library, though, you can see that toggle() does quite a bit more stuff behind the scenes than just applying/removing .active (and has many, many, many conditionals for different possible use cases...):
_proto.toggle = function toggle() {
var triggerChangeEvent = true;
var addAriaPressed = true;
var rootElement = $__default['default'](this._element).closest(SELECTOR_DATA_TOGGLES)[0];
if (rootElement) {
var input = this._element.querySelector(SELECTOR_INPUT);
if (input) {
if (input.type === 'radio') {
if (input.checked && this._element.classList.contains(CLASS_NAME_ACTIVE)) {
triggerChangeEvent = false;
} else {
var activeElement = rootElement.querySelector(SELECTOR_ACTIVE);
if (activeElement) {
$__default['default'](activeElement).removeClass(CLASS_NAME_ACTIVE);
}
}
}
if (triggerChangeEvent) {
// if it's not a radio button or checkbox don't add a pointless/invalid checked property to the input
if (input.type === 'checkbox' || input.type === 'radio') {
input.checked = !this._element.classList.contains(CLASS_NAME_ACTIVE);
}
if (!this.shouldAvoidTriggerChange) {
$__default['default'](input).trigger('change');
}
}
input.focus();
addAriaPressed = false;
}
}
if (!(this._element.hasAttribute('disabled') || this._element.classList.contains('disabled'))) {
if (addAriaPressed) {
this._element.setAttribute('aria-pressed', !this._element.classList.contains(CLASS_NAME_ACTIVE));
}
if (triggerChangeEvent) {
$__default['default'](this._element).toggleClass(CLASS_NAME_ACTIVE);
}
}
};
So, probably best just to load button.js...
Side Note: Arrow functions in jquery more often than not wind up targeting the window object, so might be best to avoid unless you know what you're doing.
What I'm trying to achieve is using a single element, by onclick, is add and remove 2 class names on 2 independent elements simultaneously.
Here is my element I'm using to trigger the js/jquery.
<div class="top-tool-buttons"></div>
Then once clicked turn these elements from this (menu_hide & lockscreen_on):
<div id="somename1" class="list-nav menu_hide"></div>
<div id="somename2" class="screen lockscreen_off"></div>
to this (menu_show & lockscreen_off):
<div id="somename3" class="list-nav menu_show"></div>
<div id="somename4" class="screen lockscreen_on"></div>
Jquery/JS
document.getElementById('#myElementId').addEventListener('click', function() {
.removeClass('menu_hide lockscreen_on').addClass('menu_show lockscreen_off');
}, false);
document.getElementById('#myElementId').addEventListener('click', function() {
.removeClass('menu_show lockscreen_off').addClass('menu_hide lockscreen_on');
}, false);
Open to better practices too as I'm trying to achieve unnoticeable page loading.
You are worrying too much about changing multiple attributes causing screen flickers etc. All these operations will complete in sequence before the page renders :)
You can simplify your code using jQuery though:
JSFiddle: http://jsfiddle.net/TrueBlueAussie/7b75Q/
$('#myElementId').click(function(e) {
e.preventDefault();
// Toggle all 4 classes off or on
$('#somename1').toggleClass('menu_hide menu_show');
$('#somename2').toggleClass('lockscreen_on lockscreen_off');
});
As noted you must have unique ids for elements. Thanks for updating your example. Have adjusted answer to suit.
You don't need to create new elements to do this.
$('#myElementId').on('click',function() {
if($("showname1").hasClass("menu_hide")){
$("#showname1").removeClass("menu_hide").addClass("menu_show");
$("#showname2").removeClass("lockscreen_off").addClass("lockscreen_on");
} else {
$("#showname1").removeClass("menu_show").addClass("menu_hide");
$("#showname2").removeClass("lockscreen_on").addClass("lockscreen_off");
}
});
I've been trying to solve the following problem all sorts of ways today. I've spent hours on it and I can't seem to figure out how to do it.
The basic problem is this: I have a bunch of JavaScript logic going on to figure out if the following variables should be a 1 or 0.
1)active
2)alternative
3)classic
4)vintage
etc....
and all these variables act essentially as filters in my design. I need them to determine whether a product should be displayed or not. So once these values are determined, I want to display product styles accordingly (or hide product styles) by appending or removing a CSS class (let's call it .style) . Each product has 1 or multiple of the filters attached to it. So like a pair of shoes might be classic AND alternative, in which case a 1 in either classic OR alternative variables should cause the shoes to display with the style.
What's the best way to do this display aspect? I was thinking of writing a custom selector but I'm having a very hard time figuring out how to write it.
Something like...
if($(".divProducts").hasClass(active) || $(".divProducts").hasClass(alternative) || etc..)
{
....?
}
but I don't get very far this way because first of all, even if the IF statement clause parses correctly, I won't know which product actually returned a 1 in alternative or active or any of the other filters.
This is really hard to explain.
TLDR; Basically I need this:
If ANY of the elements in some div container have ANY of the filters that are of value 1, apply .style to each of those elements.
Help would be hugely appreciated as this is part of a project due tomorrow actually... I didn't expect to have such a hard time with it :\
By the way, the products keep track of filters with classes. So for example
shoes product->
<div class="blah blah vintageFilter blah blah">...</div>
I want to apply .style class to this div IF vintage variable is 1. Make sense?
I think this approach will be more readable and say more managable
switch(true) {
case $('.divProducts').hasClass(active) :
// some action
break;
case $(".divProducts").hasClass(alternative) :
// some action
break;
default :
//some action
}
This should work:
// First clear the .style class from everything
$('.divProducts').removeClass('style');
// Then add it to the selected items
$('.divProducts.active, .divProducts.alternative').addClass('style');
It would be helpful if you had included some HTML, but since you didn't I'm going to assume that you have something that looks like this:
HTML
<fieldset>
<label><input type="radio" name="type" value="active" />Active</label>
<label><input type="radio" name="type" value="alternative" />Alternative</label>
<label><input type="radio" name="type" value="classic" />Classic</label>
<label><input type="radio" name="type" value="vintage" />Vintage</label>
</fieldset>
<div id="result">You picked: <span id="selection"><span></div>
<div class="products">
<div class="vintage classic">Vintage and Classic</div>
<div class="vintage">Vintage</div>
<div class="classic">Classic</div>
<div class="classic alternative">Alternative and Classic</div>
<div class="alternative">Vintage and Classic</div>
</div>
The solution is simple and you don't even need a conditional. The way I would accomplish it is with the following JavaScript:
JavaScript
$("input[name='type']")
.on("click", function()
{
// Clear result showing state
$("#result").removeAttr("class");
// Get selected value
var typeVal = $(this).attr("value");
$("#result").addClass(typeVal);
$("#selection").text(typeVal);
// Remove match class
$(".products div").removeClass("match");
// Show matching products and add match class
$(".products ." + typeVal).addClass("match");
});
With this CSS:
CSS
.active { color: #f00; }
.alternative { color: #0f0; }
.classic { color: #00f; }
.vintage { color: #999; }
.match { background: #ccc; }
Here's a link to the jsFiddle: http://jsfiddle.net/XQVet/3/
Update: Changed it so that elements wouldn't disappear.
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