I am trying to work on a snippet which has 3 progress bars and it can be controlled by 4 buttons to increment or decrement the progress bar value.
It also has a select box to switch between the progress bars.
The problem is that I am unable to individualize each progress bar. When I am changing the value in the first selected progress bar it is working as expected. The problem arises when I am switching between the progress bar using the select box.
I am not able to understand why it is behaving in such manner. I am providing the link below for better understanding.
Scenario: When you select a progress bar from the select box the first time, it will work fine. When you select another progress bar then both the progress bars get effected.
I hope I was able to point out the problem clearly.
I am posting the JavaScript code below. Click here for the complete code.
const progressBars = document.getElementsByClassName('progress-bar'),
buttons = document.getElementsByClassName('btn'),
progressView = document.getElementsByClassName('percent'),
pb_1 = progressBars[0],
pb_2 = progressBars[1],
pb_3 = progressBars[2],
selector = document.getElementById('selector');
function progressControl(selectedValue){
let selectedPB = eval(selectedValue),
progressPercent = selectedPB.firstElementChild,
progress = parseInt(selectedPB.style.width);
for(let i=0; i<buttons.length; i++){
let eachBtn = buttons[i],
controlBtn = parseInt(eachBtn.textContent);
eachBtn.addEventListener('click', function(){
progress += controlBtn;
if(progress > 100){
selectedPB.classList.add('progress-bar-danger');
} else if(progress <= 100 && progress >= 0) {
selectedPB.classList.remove('progress-bar-danger');
} else {
progress = 0;
}
selectedPB.style.width = progress+'%';
progressPercent.textContent = selectedPB.style.width;
});
}
}
selector.addEventListener('change', function(){
let selectedValue = selector.options[selector.selectedIndex].value;
progressControl(selectedValue);
});
Your problem is in this line
eachBtn.addEventListener('click', function(){
Each time you change the select's value, you're adding a new click event for the old button instead of replacing the old event listener.
I can think of 2 solutions:
Move eachBtn.addEventListener outside of the function progressControl so that you add the eventListener only once.
Use eachBtn.onclick = function(){} instead of addEventListener so that you overwrite the eachBtn's existing click handler instead of creating new handlers.
Related
On this page https://fantasy.premierleague.com/a/squad/transfers there is a dropdown where you can select a player's maximum price. It then filters a table below to only show players with a price equal or less than the selected maximum price. With my Chrome extension I have added two buttons which change the selected option
/**
* Increases the price of the /transfers maximum price filter.
*/
function increasePrice() {
const priceFilter = document.getElementById('ismjs-element-price');
if (priceFilter.selectedIndex === 0) return;
priceFilter.selectedIndex -= 1;
priceFilter.dispatchEvent(new Event('change'));
}
/**
* Decreases the price of the /transfers maximum price filter.
*/
function decreasePrice() {
const priceFilter = document.getElementById('ismjs-element-price');
if (priceFilter.selectedIndex === priceFilter.options.length - 1) return;
priceFilter.selectedIndex += 1;
priceFilter.dispatchEvent(new Event('change'));
}
/**
* Adds the increase and decrease price buttons for filtering players by the their maximum price to
* the /transfers sidebar.
*/
function addPriceButtons() {
const priceDiv = document.getElementById('ismr-price');
if (priceDiv.classList.contains('price-filter')) return;
priceDiv.className += ' price-filter';
const increaseButton = document.createElement('div');
const decreaseButton = document.createElement('div');
increaseButton.className = 'price-change-button grid-center';
increaseButton.textContent = '+';
increaseButton.onclick = increasePrice;
decreaseButton.className = 'price-change-button grid-center';
decreaseButton.textContent = '-';
decreaseButton.onclick = decreasePrice;
priceDiv.insertAdjacentElement('beforeend', increaseButton);
priceDiv.insertAdjacentElement('beforeend', decreaseButton);
}
which seems to work fine, as shown in the GIF below
The problem is that it doesn't actually change the players shown in the table. I thought that by adding
priceFilter.dispatchEvent(new Event('change'));
it would work fine. I've also tried 'click' and 'input', but these events don't do anything either.
I've also inspected the select element in Chrome's to see which event listeners it has, and if I could maybe make sense of it. Unfortunately the code is minified, so I can't really tell what to do.
Am I missing something? Where am I supposed to find out how the table is filtered in the first place and how can I trigger this myself?
I encountered a weird problem with the setInterval method that I really don't get.
So I created a website with 25 boxes and the idea was that clicking on any box would change the backgroundColor of that box for half a second after which it would return to the original color. After that animation finished the next box would change its color for half a second and then go back to the original state. This cycle would continue until it reached the last box.
The code I wrote for that is this:
window.onload = function() {
var box = document.getElementsByClassName("box");
for (i = 0; i < box.length; i++) {
box[i].onclick = starter;
}
}
function starter(eo) {
var box = eo.target;
var counter = eo.target.id;
box.style.backgroundColor = "#1A237E";
setTimeout(cont, 500, counter);
}
function cont(counter) {
var box = document.getElementsByClassName("box");
box[counter].style.backgroundColor = "white";
counter++;
box[counter].style.backgroundColor = "#1A237E";
if (counter < box.length) {
setInterval(cont, 500, counter);
}
}
(All the boxes have the class "box" and are labelled with IDs from 0 to 24)
The code actually executes and is technically working. The weird thing is that after about 8 iterations several boxes start to flash up simultaneously(different boxes for each iteration of the cont() function) and the whole code slows down enormously. This happens regardless of the starting position.
I have no idea where I made a mistake in the code, can someone explain to me white it happens?
Many thanks in advance :)
When I change the option of a dropdown menu, I want all the checkboxes to be unchecked. Here's the code that I put inside a function that's called when the dropdown menu changes:
var inputs = document.getElementsByTagName("input");
for(var i = 0; i < inputs.length; i++) {
if(inputs[i].type == "checkbox") {
inputs[i].checked = false;
}
}
This does indeed uncheck the checkbox. However, to recheck the checkbox, it takes two clicks. It appears that dat.gui still thinks the checkbox is checked, so it takes one click to uncheck it, and one more to check it.
How do I make dat.gui update the checkboxes?
Edit: Here's the current state of the problem.
gui = new dat.GUI;
controllers = [];
var menu = {
'This is an example': false,
}
controllers[0] = gui.add(menu, 'This is an example').onFinishChange(
function(value) {console.log('example');} ).listen();
menu['This is an example'] = false;
With this code, the checkbox is unchecked, due to the .listen() call and setting the variable to false. However, it still takes two clicks for the check to show--one to "uncheck" the checkbox, and one to check it.
I've recently come across the same issue with having to click the checkbox twice to get the proper behavior, so here's what worked for me and will hopefully spare other readers a few minutes of head-scratching:
// the usual
var menu = { "foo":false };
// store this reference somewhere reasonable or just look it up in
// __controllers or __folders like other examples show
var o = menu.add(menu, "foo").onChange(function() { });
// some later time you manually update
o.updateDisplay();
o.__prev = o.__checkbox.checked;
First set up data binding by telling dat.gui to listen to the value you need to bind to by including .listen() after your .add()
gui = new dat.GUI;
controllers = [];
var menu = {
'This is an example': false,
}
controllers[0] = gui
.add(menu, 'This is an example')
.listen()
.onFinishChange(
function(value) {
console.log('example');
}
);
Then set your variable that dat.gui is controlling via the checkbox to false.
menu['This is an example'] = false;
Some more info about the details of dat.gui: http://dat-gui.googlecode.com/git-history/561b4a1411ed13b37be8ff974174d46b1c09e843/index.html
I have a grid with a good amount of data which the user can filter or show/hide groups of columns by using combo boxes. Some of the column switches take a long time to load and I want to call setLoading(true,true) on the grid or the combobox to show the user that the browser is working.
I have a listener function that is called when the user makes a selection on the combo box. I call combo.setLoading(true,true) before starting any of the code that takes a while to execute. Then I call combo.setLoading(false,false) at the very bottom of the function.
The loading only shows up for a split second after the code between the two setLoading calls has executed. If I take out the call to remove the loading icon, the icon still only shows up after the code is executed.
Anyone have an idea what is happening? This seems very odd to me.
categorycomboselect: function(combo, records){
combo.setLoading(true,true);
var panel = combo.up('panel');
console.log(panel);
var category = records[0].data.name;
var grid = Ext.ComponentQuery.query('grid')[1];
Ext.suspendLayouts();
grid.suspendedLayout=true;
var columns = grid.columns;//.getView().getGridColumns();
//slow code that shows/hides columns
grid.suspendedLayout=false;
Ext.resumeLayouts(true);
combo.setLoading(false,false);
},
UPDATE
Here is my code with Trimboli's suggestion, it still isn't working. I'm showing/hiding the columns based on a string in their ID. I did it this way because the categories I want to show/hide the columns on are dynamic and the columns are dynamic.
categorycomboselect: function(combo, records){
combo.setLoading(true,true);
setTimeout( function(){
var panel = combo.up('panel');
var category = records[0].data.name;
var grid = Ext.ComponentQuery.query('grid')[1];
Ext.suspendLayouts();
grid.suspendedLayout=true;
var columns = grid.columns;
if(category=='All grade items'){
for(var i = 0; i< columns.length; i++){
columns[i].setVisible(true);
}
}else{
for(var i = 0; i< columns.length; i++){
columns[i].hide();//setVisible(false);
if(!(typeof columns[i].itemId ==='undefined')){
if((columns[i].itemId).indexOf(category)>=0){
columns[i].show();
}
}
}
}
grid.suspendedLayout=false;
Ext.resumeLayouts(true);
combo.setLoading(false,false);
},1);
},
Also, I wasnt sure if
Ext.suspendLayouts();
grid.suspendedLayout=true;
do the same thing or not. And if not, which is better.
What you're essentially doing is this:
showMask();
for (i = 0; i < 10000000; ++i) {
// busy loop
}
hideMask();
If you show/hide a mask without giving the browser a "break" for it to update the DOM, you won't see anything because it all gets flushed out at the end once you relinquish control back to the browser.
If you wanted to let the mask actually show, you need to give a slight delay before continuing on with your busy loop:
showMask();
setTimeout(function() {
// Busy stuff
hideMask();
}, 1);
I have large tables being generated and each row has a checkbox, class "chcktbl".
In the table header, there is a Select All checkbox, class "chckHead".
The select/deselect all function works fine, as does the count of selected charts I have displayed in the table heading.
The function to enable shift+click capability to select a range of checkboxes also works, but in its current format, only selects 10 checkboxes before generating an error message in a popup window:
"Stop running this script? A script on this page is causing your web browser to run slowly. If it continues to run, your computer might become unresponsive."
<script type=text/javascript>
//select all button
$('#chckHead').click(function() {
if (this.checked === false) {
$('.chcktbl:checked').attr('checked', false);
}
else {
$('.chcktbl:not(:checked)').attr('checked', true);
}
countSelected();
});
//count number of boxes checked
function countSelected() {
var numCharts = $('input.chcktbl:checked').length;
$('#numCharts').html(numCharts);
}
//SHIFT+Click to select a range of checkboxes:
// this variable stores the most recently clicked checkbox
// it is used for shift-clicks
var lastClickedBox = 0;
// the checkbox functionality is default to the browser
$('.chcktbl').click(function(event) {
var clickedBox = $('.chcktbl').index(event.target);
if(event.shiftKey) {
setCheckboxes(lastClickedBox, clickedBox);
};
lastClickedBox = clickedBox;
countSelected();
});
// sets all the checkboxes between the specified indices to true
function setCheckboxes(end, start) {
if(start > end) {
var temp = start;
start = end;
end = temp;
};
for(var i = start; i < end; i++) {
$('.chcktbl').eq(i).prop('checked', true);
};
countSelected();
};
</script>
This is a really common feature for selecting a range of items with one click, but I can't find an efficient way to do it. If anyone knows a better way of approaching this or can spot some inefficiency in the code then please let me know.
How about using jquery nextUntil?
I didn't actually test this but this should give you the basic idea and it removes the for loop. I created similar functionality to this before using nextUntil/prevUntil and never got an unresponsive page.
function setCheckboxes(end, start) {
if(start > end) {
var temp = start;
start = end;
end = temp;
};
$('.chcktbl').eq(start).nextUntil(':eq('+(end+1)+')').add().prop('checked', true);
countSelected();
};
I tried you code and it worked well for me. You might want to try this jquery plugin
https://gist.github.com/DelvarWorld/3784055