Scenario:
A simple html page exists with few buttons on Top (e.g. day, week) and BOTTOM (filter1 and filter2) of the page and Graph on center.
The graph displays a data based on selected filters.
Problem:
when user selects the filter2 I need to know what the user selected previously on TOP buttons so that the data can be displayed accordingly.
I'm using global variable to track the last selected buttons. This means lots of switch statements. I'm wondering if this is the most elegant way to solve this issue or is there any magic. function exist on Jquery.
I think the easiest way is to use data on the elements themselves.
<button id="day" class="graph-button" data-state="off">Day</button>
<button id="week" class="graph-button" data-state="off">Week</button>
<!-- graph here -->
<button id="filter1" class="graph-button" data-state="off">Filter 1</button>
<button id="filter2" class="graph-button" data-state="off">Filter 2</button>
Then you could have some jQuery something like this:
$(function(){
$('.graph-button').click(function(ev){
ev.preventDefault();
var state = $(this).data('state'),
newState = state == 'off' ? 'on' : 'off',
onButtons = [];
$(this).data('state', newState);
$('.graph-button[data-state="on"]').each(function(i, el){
onButtons.push(this.id);
});
// updateGraphWith(onButtons);
});
});
Let's assume the top buttons have class="topButton" and the filter buttons have class="filterButton" and all buttons have unique ids, then the jQuery will look something like this:
$(function(){
var topButtons = $(".topButton").on('click', function() {
var $this = $(this);
id = $this.attr('id');
if($this.hasClass('selected')) { return; }
topButtons.removeClass('selected');
$this.addClass('selected');
//perform click action here
});
$('.filterButton').on('click', function() {
var $this = $(this);
id = $this.attr('id'),
selectedTopButtonId = $('.topButton').filter('.selected').attr('id');
//perform filter action here
});
});
In both cases, the variable id lets you know which button was clicked.
By setting class="selected" on the selected top button, you can style the button and have something to test without needing to resort to a global variable.
Related
So i'm dynamically loading modal from my controller, with product information. Each product has a calculator to calculate price. The issue is when I open the modal the first time, I can remove the disable class from the calculate button. If i open/close/open it will work for every calculator on the page.
products.blade.php
foreach product
<a
class="btn-icon"
data-toggle="modal"
data-target="#publicModal"
data-url="{!! route('public.products.partials.calculator', ['product_id'=>$pp->product->id]) !!}"
data-title="Calculate cost"
data-size="modal-xl" >
<i class="fas fa-calculator"></i>
</a>
jquery
if(
custSQFT > 0 &&
custWidth > "0.00" &&
custLength > "0.00" &&
totalCost > 0
){
document.getElementById("calc-cost").classList.remove("disabled");
document.getElementById("calc-cost").classList.add("visible");
calcValid = true;
}else{
$('#calc-cost').prop("disabled",true).addClass('disabled')
calcValid = false;
}
Modal
<script type="text/javascript">
$('#publicModal').on('show.bs.modal', function (event) {
if(event.relatedTarget){
var button = $(event.relatedTarget) // Button that triggered the modal
}else{
var button = $('#loginButton');
}
var url = button.data('url') // Extract info from data-* attributes
var size = button.data('size')
var modal = $(this)
if (typeof(size) !== 'undefined') {
modal.find('.modal-dialog').addClass(size)
}
modal.find('.modal-body').load(url);
modal.find('.modal-title').html(button.data('title'))
});
$('#publicModal').on("hidden.bs.modal", function(){
$(".modal-body").html("");
$(".modal-title").html("Loading...");
});
</script>
So when I open up the modal, fill out the forms, I can console.log that I have made it inside the IF statement, but can not remove the visible class.
If I'm understanding correctly, you are having an issue with replacing the element in the dom with the same id.
I think probably easiest fix would be to add a unique id and capture that specific id to take action within your jquery.
Name the id uniquely, perhaps using the product id:
<a id='calc-cost-{{$pp->product->id}}' //... etc
Then you have a number of options to identify the current iteration, but I'll illustrate using blade instead of pure JS/Jquery for simplicity:
$('#calc-cost-'{{$pp->product->id}}).removeClass('disabled'); //etc
Normally, I'd add a data element on the <a> and grab the id through jquery to make it a little cleaner ( $('#calc-cost-'+grabbedIdVar)), but the above should give you an idea of how to go.
HTH.
I have a dynamically generated form with groups of checkboxes representing categories of companies. These eventually get plotted on a dynamic chart (not shown here). Each group of companies is in a div, and each div has a button called Only that should check all the checkboxes in its own category (div) and uncheck all the other checkboxes on the page.
Here's a Fiddle with all the code: https://jsfiddle.net/c2kn78a9/
The Only buttons have this code in them:
// Uncheck all checkboxes outside this div
$(this).closest("div").not(this).find('input[type=checkbox]').prop('checked', false).change();
// Check all checkboxes in this div
$(this).closest("div").find('input[type=checkbox]').prop('checked', true).change();
But it's not working. Any idea how to fix this?
Here's the code for the entire page.
<!-- This button is different than the other buttons -->
<button class="button-text" id="customize-button">Open User Settings</button>
<!-- Placeholder for dynamic form -->
<div id="company-selection-form"></div>
<script type="text/javascript">
function toMachineString(humanString) {
var machineString = humanString.replace(/\s+/g, '-').toLowerCase();
machineString = machineString.replace('&','');
return machineString;
}
// Setup the form
var categories = new Map([
['Tech Giants',['Alphabet','Amazon','Apple','Facebook','Microsoft']],
['Handset Manufacturers',['Apple','Samsung','Motorola','Sony']],
['Semiconductors', ['AMD','Intel','Nvidia']]
// ... more ...
]);
// Build company selection form inputs
let companySelectionHTML = '';
for (let category of categories) {
categoryName = category[0];
categoryList = category[1];
// Setup a div to differentiate each category of companies.
// Will be used for turning on/off categories en masse
companySelectionHTML += `<div id="${toMachineString(categoryName)}">\n`;
// Category heading
companySelectionHTML += `<h4>${categoryName}</h4>\n`;
// Only button
companySelectionHTML += `<button class="only" id="btn-only-${toMachineString(categoryName)}">Only</button>\n`;
categoryList.forEach(companyName => {
companySelectionHTML += `
<label class="checkbox-label">
<input id="x-${toMachineString(companyName)}" class="checkbox" type="checkbox" name="company" value="${companyName}" checked>
<label for="x-${toMachineString(companyName)}">${companyName}</label>
</label>`;
});
companySelectionHTML += '</div>\n</div>\n</div>\n';
}
// Append to DOM
const companySelectionId = document.getElementById('company-selection-form');
companySelectionId.insertAdjacentHTML('beforeend', companySelectionHTML);
// Make the ONLY buttons check all the checkboxes in their div and uncheck everything else
$(document).ready(function() {
$(document).on("click", ".only", function() {
// Uncheck all checkboxes outside this div
$(this).closest("div").not(this).find('input[type=checkbox]').prop('checked', false).change();
// Check all checkboxes in this div
$(this).closest("div").find('input[type=checkbox]').prop('checked', true).change();
});
});
</script>
Thanks!
Your .not(this) is trying to filter out the button element from the single closest div. You need to get all div's on the page and remove the closest div to "this" button.
From your JSFiddle like this:
var temp = $(this).closest("div");
$("div").not(temp).find('input[type=checkbox]').prop('checked', false).change();
OR (to avoid a new variable)
$("div").not($(this).closest("div")).find('input[type=checkbox]').prop('checked', false).change();
Matt G's solution works fine, it deselects all the checkboxes on the page.
I'd suggest to further refine it by first narrowing the selection to only your #company-selection-form
`$("#company-selection-form")
.find("div")
.not($(this)
.closest("div"))
.find('input[type=checkbox]')
.prop('checked', false)
.change();`
Nevertheless, allow me to suggest that you're maybe wasting your time learning this stuff. This programming paradigm is too problematic and anachronistic. It's slow, gets out of hand very quickly, and never brings anything but suffering. Even the slightest update to the UI can force you to revisit (after months sometimes), debug, and rewrite your code. It's never testable, no one would even bother to test this rigorously.
I mean, if your employer holds a gun to your head every day and you have to choose either to do it this way or die, you'd soon choose to die over this ordeal.
I'm a UI Designer working on a multi-page Q&A form, I'm a beginner with jQuery mostly mashing snippets together.
Here's the code: http://codepen.io/covanant/pen/GJZYLq
This part of the form is basically multiple accordions wrapped into tabs, I have most of it working as required but one of the things I need to do, is that whenever I a choice or option, I want to be able to output that option to an element as text right underneath the question.
The element is:
<span class="selected-answer"></span>
You can see it displayed in the first question in the demo, the way that I'd like it to work is that whenever I click the Close All button, it will fadeIn the .selected-answer element and when I click Open All, it will fadeOut the .selected-answer element.
The buttons:
Open All
Close All
jQuery:
// Open All & Close All buttons
$('.closeall').click(function(){
$('.panel-collapse.in')
.collapse('hide');
});
$('.openall').click(function(){
$('.panel-collapse:not(".in")')
.collapse('show');
});
First, it doesn't make sense to give each of your select options the same value attribute. By convention, these should be distinct. If you aren't using the value attribute, you can remove it altogether. Otherwise, you should change it to something like:
<select>
<option value="None Selected">None Selected</option>
<option value="Photocell On">Photocell On</option>
<option value="Off Control Only">Off Control Only</option>
<option value="Photocell On / Off Control Only">Photocell On / Off Control Only</option>
</select>
Once that is sorted out, you need to go up the DOM hierarchy and find the right span element to change.
$('select').on('change', function() {
var span = $(this).closest('div.panel').find('span.selected-answer');
span.text($(this).val());
});
For the checkbox questions, you I would do something like this:
HTML:
<span class="selected-answer">
<ul class="checked-options">
<li data-check="checkbox1">nWifi (nLight)</li>
<li data-check="checkbox2">nLightFixtures</li>
<li data-check="checkbox3">xCella (LC&D)</li>
<li data-check="checkbox4">Daylight Harvesting</li>
<li data-check="checkbox5">xPoint (LC&D)</li>
<li data-check="checkbox6">nWifi (nLight)</li>
</ul>
</span>
CSS:
.checked-options li {
display: none;
}
jQuery:
$('input[type="checkbox"]').on('change', function() {
var checkbox = $(this);
var id = checkbox.attr('id');
if ($(this).prop('checked'))
$('li[data-check="' + id + '"]').show();
else
$('li[data-check="' + id + '"]').hide();
});
As for the fading, this should do the trick:
// Open All & Close All buttons
$('.closeall').click(function(){
$('.panel-collapse.in')
.collapse('hide');
$('.selected-answer').fadeIn();// <-- Fade in
});
$('.openall').click(function(){
$('.panel-collapse:not(".in")')
.collapse('show');
$('.selected-answer').fadeOut();// <-- Fade out
});
Also, depending on whether you want all the questions open or closed by default when the form first loads, you may need to hide all the .selected-answer elements on page load.
Here's the updated codepen.
I agree with VCode on using distinct values for each option in the select elements. But instead of using the value you provide for each option, I think you should use the actual label, that way you can have a more description label, than the option value.
I modified a few of your existing functions to actually populate the selected answer. First I noticed that you already have a function for handling changes to your select - in there I added a small snippet to get the selected answer and pass it to nextQuestion.
$(".panel-body select").change(function() {
var selectElem = $(this);
var answer = selectElem.find("option:selected").text();
nextQuestion(selectElem, answer);
});
Then you also have input elements. Here is your modified input change function:
$(".panel-body input").change(function() {
var inputElem = $(this);
var inputType = inputElem.attr('type');
// common parent for input
var commonParent = inputElem.closest(".panel-body");
var answers = commonParent
.find("input:checked")
.closest("."+inputType)
.find("label")
.map(function(){return this.innerText;})
.get()
.join(", ");
nextQuestion(inputElem, answers);
});
And now as you may have noticed, I added a parameter to the nextQuestion function. I put this code in nextQuestion because you were already accessing the parent there so I wanted to re-use that logic to populate the selected answer.
function nextQuestion(currentQuestion,selectedAnswer) {
var parentEle = currentQuestion.parents(".panel");
if (arguments.length>1) {
parentEle.find('.selected-answer').text(selectedAnswer);
}
if (parentEle.next()) {
parentEle.find(".fa-question").addClass("fa-check check-mark").removeClass("question-mark fa-question").text("");
}
}
Just like VCode mentioned, you can do the fading of the answers using fadeIn/fadeOut
// Open All & Close All buttons
$('.closeall').click(function(){
$('.panel-collapse.in')
.collapse('hide');
$('.selected-answer').fadeIn();// <-- Fade in
});
$('.openall').click(function(){
$('.panel-collapse:not(".in")')
.collapse('show');
$('.selected-answer').fadeOut();// <-- Fade out
});
// hide all .selected-answers
$('.selected-answer').hide();
Here is a link to your codepen with my modifications: http://codepen.io/anon/pen/ZGpXXK
Let's say I have a sample table like this :
http://jsfiddle.net/65BkH/
Now what i want is, if there is this <button id="invite">Invite</button>. I want this button to select the "invite url" of the selected contact. If you see the jsfiddle, if you click the checkbox, it will crop all the others contacts. How could i do that?
I've tried to target the contact, but i can only get the top contact.
$('#linkedin_match tbody .match .m a').attr('href');
what i want is to target the currently selected.
FURTHER PROBLEM :
This button I'm talking about is not the table per say.
$('#btn_save').click(function(e) {
var hrefVar = $('#linkedin_match tbody .match .m').find('a').attr('href');
alert(hrefVar);
});
Now if i used that, it still searches for the first link, even though i've selected the others. So, any changes?.
You can use this:
$('#invite').on('click',function(){
var url = $this.closest('tr').find('a').prop('href');
});
To get all the checked href related with the checked inputs in a array use this:
$('#invite').on('click', function () {
var all_url = [];
$('input:checked').each(function () {
var url = $(this).closest('tr').find(' td.m a').prop('href');
all_url.push(url);
});
});
Demo here
$('.m').click(function(e){
var hrefVar = $(this).find('a').attr('href');
alert(hrefVar);
});
fiddle
I have a set of anchors that I am converting to buttons like so:
var sideMenuAnchors = $("#divLeft a");
sideMenuAnchors.width("120px");
sideMenuAnchors.button();
However when one of these anchors is clicked I want the ui-state-active to remain until another button is clicked ... I have been unable to find a simple solution, is there one ?
I have tried this:
$('#anchor01').unbind('onmouseover').unbind('onmouseout');
and this :
$('#anchor01').disable()
However neither do what I require, as the ui-active-state is still removed on mouseout
Edit
The solution I implemented was to manually add the button classes that I required from jquery-ui, like so:
var sideMenuAnchors = $("#divLeft a");
sideMenuAnchors.addClass("ui-state-default ui-button ui-button-text-only");
sideMenuAnchors.width("120px");
sideMenuAnchors.height("25px");
sideMenuAnchors.removeClass('ui-corner-all');
sideMenuAnchors.first().addClass('ui-corner-top');
sideMenuAnchors.last().addClass('ui-corner-bottom');
sideMenuAnchors.hover( function() {
$(this).addClass("ui-state-hover");
},function() {
$(this).removeClass("ui-state-hover");
});
Since you're transforming hyperlinks into jQuery UI buttons, there are no group relationship between them and they're all considered as independent.
However, if you were transforming radio buttons (with the same name attribute), then jQuery UI would maintain the group relationship and you would obtain the behavior you're aiming for.
So, you can do just that: first, add a radio button and its associated label to each hyperlink, then transform these into buttons:
<form>
<div id="divLeft">
<a id="link1" href="#">Foo</a>
<a id="link2" href="#">Bar</a>
<a id="link3" href="#">Quux</a>
</div>
</form>
$(document).ready(function() {
$("#divLeft a").each(function() {
var $this = $(this);
var text = $this.text();
var radioId = $this.attr("id") + "_radio";
$this.text("").append(
$("<input type='radio' name='buttons'>").attr("id", radioId),
$("<label>").attr("for", radioId).text(text));
});
$("#divLeft a input:radio").width("120px").button();
});
You can see the results in this fiddle.