How to unificare function (connect 3 functions into 1) - javascript

I Would like to merge 3 functions into 1, because I feel like it is possible, but I have no idea how to make it correctly
Here is my solutuion. I do not understand why it only hides location-type2 and does not show it.
I have really no idea what is wrong here, because it hides things correctly, it just does not show them at all.
Also, here is my HTML. I am catching value from "data-value" and using this I am trying to merge this functions, but it does not work.
$("#check3, #check2, #check1").click(function () {
console.log($(this).data("value"));
if ($(this).data("value") === 1) {
if ($("#check1").checked) {
$(".location-type1").removeClass("display-none");
}
else {
$(".location-type1").addClass("display-none");
}
}
else if ($(this).data("value") === 2) {
if ($("#check2").checked) {
$(".location-type2").removeClass("display-none");
}
else {
$(".location-type2").addClass("display-none");
}
}
else {
if ($("#check3").checked) {
$(".location-type3").removeClass("display-none");
}
else {
$(".location-type3").addClass("display-none");
}
}
});
I would like to have this 3 functiones being merged:
$("#check1").click(function(){
if (document.getElementById('check1').checked)
{
$(".location-type1").removeClass("display-none")
}
else {
$(".location-type1").addClass("display-none");
}
});
$("#check2").click(function(){
if (document.getElementById('check2').checked)
{
$(".location-type2").removeClass("display-none")
}
else {
$(".location-type2").addClass("display-none");
}
$("#check3").click(function(){
if (document.getElementById('check3').checked)
{
$(".location-type3").removeClass("display-none")
}
else {
$(".location-type3").addClass("display-none");
}
});
<div class="form-group"> <input type="checkbox" style="display: none" id="check1" data-value="1" checked> <label for="check1">Restauracja</label> </div>
<div class="shop-position location-type1" data-lat="52.4068200" data-lng="16.9299300" data-type="1">
<div class="location-text">
<strong>Vininova1</strong>
<div>Podgórna 14</div>
<div>Poznań</div>
Pokaż na mapie
</div>
</div>

Anytime you find yourself writing the same or very similar code that you've already written, you should stop and think about how to re-write it to be more loosely coupled and therefore more usable. In your case, we really only need one function with a single statement in it.
Also, input elements already have a value attribute, so it's redundant to add a data-value to them.
Additionally, don't use inline styles if you can avoid it. They cause duplication of code and make the code more difficult to read and scale. Instead, use CSS classes.
// Get the checkboxes into a JQuery wrapped set
let inputs = $("#parent input[type='checkbox']");
// Set the event handler up on the parent of the 3 checkboxes
$("#parent").on("click", function (event) {
if(event.target.nodeName === "INPUT"){
// Get the actual checkbox that was clicked with: event.target
// Get the index of the clicked checkbox within the set
// Toggle the use of the class on the location item that corresponds to the index of the clicked checkbox(es)
$("#locations div")[$("#parent input[type='checkbox']").index(event.target)].classList.toggle("display-none");
}
});
.display-none {
display:none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="parent">
<input type="checkbox" value="one">One<br>
<input type="checkbox" value="two">Two<br>
<input type="checkbox" value="three">Three
</div>
<div id="locations">
<div class=".location-type1 display-none">Location 1</div>
<div class=".location-type2 display-none">Location 2</div>
<div class=".location-type3 display-none">Location 3</div>
</div>

Use switch.
If you were using only data("value"), switch would be by this value:
switch(data("value")) {
case...
}
But in your case checks are different. Then:
switch(true) {
case data("value") === 2:
....
case document.getElementById('check3').checked:
....
}

Related

Why is the jquery object not returning its value?

I've been trying to figure this problem out for the last 2 days and have not found any solution so far.
Im trying to attach a .click() listener to all elements of a list, but any time I use this or $(this) none of the jquery functions work, for example using .val() returns undefined even though it has a value.
I'm using fomantic-ui but I've also tried the same code without and it doesn't work either. I'm also using NodeJS and Express, in case that makes a difference.
Further testing showed me that for some reason this doesn't work:
$('#first_name').on('input', () => {
const name = $(this)
const field = name.parent()
if (!name.val().match(/^\p{L}{1,16}$/u)) {
field.attr('class', 'field error')
name.prop('valid', false)
} else {
field.attr('class', 'field success')
name.prop('valid', true)
}
})
But if I change it to this, everything is fine:
$('#first_name').on('input', () => {
const name = $('#first_name') //Only Change...
const field = name.parent()
if (!name.val().match(/^\p{L}{1,16}$/u)) {
field.attr('class', 'field error')
name.prop('valid', false)
} else {
field.attr('class', 'field success')
name.prop('valid', true)
}
})
And also this both return false
console.log($(this) === $('#first_name'), $(this) == $('#first_name'))
//false false
I have tried all sorts of combinations but nothing I can think of works, and nothing I found anywhere online has either. Maybe I just don't understand how this is supposed to work but I've tried reading the jquery documentation but it didn't help me.
Can anyone help me?
You're using an arrow function, so the value of this will be inherited from the parent context. A console.log should show you what that is.
You probably want to use a regular anonymous function, assuming jQuery calls the function with the HTML element set to the context of this.
$('#first_name').on('input', function() {
// ...
});
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
Arrow functions don't have their own bindings to this or super, and
should not be used as methods.
Here I have an example where I use a class instead of an id on the input group.
Your scope issue is resolved by using function(event){ form for the function
I use the .closest('.wrapper') to get the "owner" of the group.
I hooked the event handler using the container ID: $('#inputs-container')
I use a data attribute and set a value for that to do some "creative" css depending upon the condition
IF for some reason you need to get the container, you can use the event.delegateTarget - this is the container element with the id id="inputs-container"
I added the change event also in case someone does a "paste" or you change the value programmatically
I would suggest you use semi-colons on the ends of the lines in the script; at some point not doing so will cause a very hard to find bug
I admit this is a bit of overkill but perhaps someone can get some use of the example even though it is admittedly a bit "verbose". Try it out by entering in text, numbers and spaces in each of the three inputs.
$('#inputs-container').on('input change', '.first-name',function(event) {
const $input = $(this);
const field = $input.closest('.wrapper');
//console.log(field.data('goodness'), field.get(0).dataset);
let val = $input.val();
const regex = /^\p{L}{1,16}$/u;
const isGood = val.match(regex) == val;
//console.log('Good:', val, isGood);
field.get(0).dataset.goodness = isGood ? "success" : "error";
$input.prop('valid', isGood);
});
.wrapper {
border: 1px solid #d0d0d0;
border-width: 0.5em;
padding: 0.5em;
}
.wrapper[data-goodness="error"] {
border-color: #FF0000;
border-width: 0.5em;
}
.wrapper[data-goodness="error"] .err-message:before {
content: "Do Better";
padding-left: 1em;
}
.wrapper[data-goodness="success"] {
border-color: #00FFdd;
}
.wrapper[data-goodness="success"] .err-message:before {
content: "Doing well so far!";
padding-left: 1em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="inputs-container">
<div class="wrapper" data-goodness="cheers">
<div class="field-things">
<div class="field-name-thing">We are happy to have:</div>
<label>First Name</label>
<input type="text" class="first-name" /><span class="err-message"></span>
</div>
</div>
<div class="wrapper" data-goodness="cheers">
<div class="field-things">
<div class="field-name-thing">We are happy to have:</div>
<label>First Name</label>
<input type="text" class="first-name" /><span class="err-message"></span>
</div>
</div>
</div>
<div class="wrapper" data-goodness="cheers">
<div class="field-things">
<div class="field-name-thing">OUT OF SCOPE</div>
<label>First Name</label>
<input type="text" class="first-name" /><span class="err-message"></span>
</div>
</div>

How to test whether an element has a specific class?

I have the following function that refreshes its data as follows
refresh: function($contentholder) {
$contentholder.each(function() {
$this = $(this);
if ($this.hasClass("Codes")) {
//then it does some stuff in here
}
}
}
the $contentholder comes in with data as follows
<div class="Codes" cod="t592">
<h3></h3>
<div class="Items">
<div class="ItemCodes" id="592" time="2016/04/30 12:15" places="1"></div>
</div>
</div>
The only way my if-statement gets detected, is if I check it as follows $this.hasClass("Codes"), but I only want the if-statement to get executed if the hasClass("codes") contains the class ItemCodes
This is what I tried but doesn't work
if ($this.hasClass("Codes").length > 0) //comes back undefined even though there is data
if ($this.hasClass("ItemCodes"))` //doesnt get detected
Basically, I want to do something like this
if ($this.hasClass("Codes").find("ItemCodes"))
How do I achieve this?
You can use find() to determine if there was any child .ItemCodes elements. You will need two separate conditions as these statements cannot be combined.
refresh: function ($contentholder) {
$contentholder.each(function () {
$this = $(this);
if ($this.hasClass("Codes") && $this.find('.ItemCodes').length) {
// do some stuff in here
}
}
}
You can use .is() method combined with :has() pseudo selector as follows:
if ( $this.is('.Codes:has(".ItemCodes")') ) {
//
}
let $that = $('div.Codes');
console.log( $that.is('.Codes:has(".ItemCodes")') );
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="Codes test me" cod="t592">
<h3></h3>
<div class="Items">
<div class="ItemCodes" id="592" time="2016/04/30 12:15" places="1"></div>
</div>
</div>

Show/Hide objects by attribute in HTML list

For example, I have checkbox that can be checked or unchecked.
Whenever it is checked, I want all the elements in the HTML list to be displayed.
Whenever it is not checked, I want just some objects (that are not filtered) to be displayed, like others don't exist (I don't want white spaces etc).
The HTML (Handlebars) list:
<ol>
{{#each personList}}
<li>{{showPerson this}}</li>
{{/each}}
</ol>
Handlebars helper:
Handlebars.registerHelper('showPerson', function(person) {
return person.firstName + " " + person.lastName;
});
My filtering function looks like this:
function filterOldPeople(person) {
return person.age > 60;
}
The thing I want to achieve is something like this:
var filter; // I am getting this boolean value from checkbox
if (filter) {
doFilter(); // will filter the HTML list that is already rendered
}
Now I don't know how the function doFilter() should look like to achieve this. Now with jQuery the problem is I can get the HTML object of list item but not the actual person object so this won't work:
$( "li" ).filter(filterOldPeople).addClass("hide");
And something like that is exactly what I need. How do I achieve this?
Here you go! Only thing you want to do is to place all elements you want to hide in one div and then hide it, it is alot of easier to hide one element than couple of them.
function check() {
if (document.getElementById("test").checked == true) {
document.getElementById("two").style.display = "none";
} else {
document.getElementById("two").style.display = "block";
}
}
<div class="info">
<div id="one">
If you click on me, #two will hide.
<input type="radio" id="test" onclick="check()">
</div>
<div id="two">
Hi, click on the button!
</div>
</div>
If you don't want to use two lists, you could modify using something like the following (add code to function as needed):
HTML
<ul id="theList">
<li class="number">One</li>
<li>Red</li>
<li class="number">Two</li>
<li>Green</li>
<li class="number">Three</li>
<li>Blue</li>
</ul>
<br /><input type="checkbox" id="numbers" checked><label for="numbers">Show Numbers</label>
JS
$('#numbers').change(function() {
$('.number').hide();
});
https://jsfiddle.net/1ngotz5u/
How I would approach this would be:
function filterOldPeople(person) {
return person.age > 60;
}
// Get all li Elements (useful so we don't have to search the dom multiple times)
var liElements = $("li");
// loop through each li and filter it.
function filterAges() {
liElements.each(function () {
if (filterOldPeople($(this).val())) {
$(this).addClass("hide");
}
});
}
// Show all hidden elements by removing the hide class
function showAll() {
liElements.removeClass("hide");
}
// Create our event
$("#checkbox").change(function () {
if ($(this).is(":checked")) {
filterAges();
} else {
showAll();
}
});

Flipping with navigation keys divs that are :checked in input

I need the next & previous buttons to navigate through the divs that was selected in checkboxes. That means that user selects several options press the start button and do some actions in first selected option, then he press next and navigates to next div and again do some actions.
$(document).ready(function () {
$('#roomsubmit').click(function () {
$(".room").hide();
if ($("#node").is(':checked')) {
$("#hallway").show();
$("#hallway").addClass("roomNew");
} else if ($("#node-2").is(':checked')) {
$("#livingroom").show();
$("#livingroom").addClass("roomNew");
} else if ($("#node-3").is(':checked')) {
$("#bedroom").show();
$("#bedroom").addClass("roomNew");
} else if ($("#node-4").is(':checked')) {
$("#playroom").show();
$("#playroom").addClass("roomNew");
}
});
$(function () {
$(".swap").on({
click: function () {
var parent = $(this).closest('.roomNew');
parent.css('display', 'none');
if (this.id == 'previousBtn') {
parent.prev('.roomNew').css('display', 'block');
} else {
parent.next('.roomNew').css('display', 'block');
}
}
});
});
});
.room {
display:none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<label for="node">Hallway
<input type="checkbox" id="node" />
</label>
<label for="node-2">Livingroom
<input type="checkbox" id="node-2" />
</label>
<label for="node-3">Bedroom
<input type="checkbox" id="node-3" />
</label>
<label for="node-4">Playroom
<input type="checkbox" id="node-4" />
</label>
<button id="roomsubmit">Start calculation</button>
<div id="hallway" class="room">Works in hallway
<button class="swap previousBtn" id="previousBtn">prev</button>
<button class="swap nextBtn" id="nextBtn">next</button>
</div>
<div id="livingroom" class="room">Works in livingroom
<button class="swap previousBtn" id="previousBtn">prev</button>
<button class="swap nextBtn" id="nextBtn">next</button>
</div>
<div id="bedroom" class="room">Works in bedrrom
<button class="swap previousBtn" id="previousBtn">prev</button>
<button class="swap nextBtn" id="nextBtn">next</button>
</div>
<div id="playroom" class="room">Works in playroom
<button class="swap previousBtn" id="previousBtn">prev</button>
<button class="swap nextBtn" id="nextBtn">next</button>
</div>
jsfiddle for the above code
I have something for you, Roby. Sorry for delay - had to work it out.
jsFiddle Demo
First, there are now two arrays: arrRooms (an array of objects which map the nodename to the room), and newRooms (an array to store checkmarked rooms - poor name, but oh well).
The arrRooms array is used to get the room name when given the node. This eliminated all those if-else lines.
A new function $('input:checkbox').click was created to add/remove the checked/unchecked nodes from the newRooms array. This leaves you with an array containing just the checked rooms.
Next, the #roomsubmit.click function was simplified to use the above arrays. All it does now is (1) grab the first item in the newRooms array (the checked rooms) (2) display that room's DIV, and (3) remove that item from the newRooms array. This creates a "cafeteria plate" scenario where each time you click #roomsubmit ("Start Calculation" button), the next checked room is displayed.
Important: Note that you have several elements with the same ID (id="nextBtn" and id="previousBtn"). Not allowed, I'm afraid. Every ID must be completely unique in the DOM. However, there is no need to use IDs at all since they are not referenced in the code at the moment. The jQuery selector uses the class, so just delete the ID attributes from the HTML.
By the way, to make the previous button work, you can modify your swap function to work with the above, or else change your approach completely and just create another empty array and when you delete a room from the newRooms[] array, add it into the prevRooms[] array. When the previous button is clicked, grab the room-to-display from the prevRooms[] array, delete it from there, and stick it back into the newRooms[] array. You will work it out... (In fact, you may need three variables: prevRooms array, currRoom var, and newRooms array --- put some legos on a desk to represent the checked rooms, and use three circles to represent the three arrays - and imagine clicking the next/prev buttons and moving the curr room lego from one pile into the next, and you'll quickly work out how to do this.)
javascript/jQuery code:
var tmp;
var arrRooms={'node':'hallway','node-2':'livingroom','node-3':'bedroom','node-4':'playroom','node-5':'cabinet','node-6':'kitchen','node-7':'bathroom','node-8':'wc'};
var newRooms=[];
$('input:checkbox').click(function(){
if ($(this).is(':checked')) {
newRooms.push(this.id);
//alert( JSON.stringify(newRooms) );
}else{
newRooms.splice(newRooms.indexOf(this.id),1); //delete from newRooms
//alert( JSON.stringify(newRooms) );
}
});
$('#roomsubmit').click(function () {
$(".room").hide();
tmp = arrRooms[newRooms[0]];
//alert('tmp = ' +tmp);
$('#' +tmp).addClass('roomNew').show();
newRooms.splice(0,1);
//alert( JSON.stringify(newRooms) );
});
$('.nextBtn').click(function(){
$('#roomsubmit').click();
});
$(function () {
$(".swap").on({
click: function () {
var parent = $(this).closest('.roomNew');
parent.css('display', 'none');
if (this.id == 'previousBtn') {
parent.prev('.roomNew').css('display', 'block');
} else {
parent.next('.roomNew').css('display', 'block');
}
}
});
});

jQuery matching a single class against predefined list where element has multiple classes

I am trying to figure out if there is a way around doing .hasClass() multiple times to see if the active element I am working with has one of currently four specific classes, I am also trying to figure out the most optimized way to do this while the element(s) that are acting as the trigger (or the active element) has multiple classes in it mostly for styling purposes.
Example of the HTML:
<div class="form_row">
<div class="primary_row">1</div>
</div>
<div class="form_row">
<div class="primary_row subexists">1</div>
<div class="primary_row_sub">1a</div>
</div>
<div class="form_row">
<div class="primary_row subexists">1</div>
<div class="primary_row_sub subexists">1a</div>
<div class="secondary_row">2</div>
</div>
<div class="form_row">
<div class="primary_row subexists">1</div>
<div class="primary_row_sub subexists">1a</div>
<div class="secondary_row subexists">2</div>
<div class="secondary_row_sub">2a</div>
</div>
I am in the progress of currently building it up, so this is still a rough draft, but its safe to assume more classes will exist on various elements per the need. The Four main classes I am worried about are primary_row, primary_row_sub, secondary_row, secondary_row_sub. I am building a click handler like:
$('.form_row > div').click(function()
{
//code
});
in this click handler I want to be able to detect if the element clicked is one of the four mentioned above. Where if it is, I want to do something based on which. So determining which class is of the element clicked, rather than building four click handlers one for each type. I am hoping I can keep it optimized and contained to a single handler. Any ideas?
One option:
var classMap = {"one": function () { alert("one");},
"two": function () { alert("two");},
"three": function () { alert("three");}
}
, classes = "";
$('div').click(function (e) {
classes = this.className.split(" ");
for (key in classMap) {
if ($.inArray(key, classes) !== -1) {
classMap[key]();
}
}
});
http://jsfiddle.net/wp9X7/5/
if ($(this).is(".primary_row")) {
...
} elseif ($(this).is(".primary_row_sub")) {
...
} and so on

Categories