I have a table of rows, each row has a checkbox. I have built a function that detects if the shift key had been pressed then, if it has then once 2 checkboxes are checked it will check the boxes between them.
The function works to some degree, however for some reason I have to check a third box to tick the boxes in-between.
How do I alter the code so it checks the boxes on the second check and not have to check a third?
Thanks in advance.
Below is my code.
Vue Data:
data: () => ({
keycheck: false,
checkRows: []
})
Created:
created() {
window.addEventListener('keydown', e => {
if (e.keyCode === 16) {
this.keycheck = true
console.log('The shift key is being held down...')
}
})
window.addEventListener('keyup', e => {
if (e.keyCode === 16) {
console.log('Upper')
this.keycheck = false
this.checkRows = []
//console.clear();
}
})
},
methods: {
checkbox(key) {
if (this.keycheck) {
this.checkRows.push(key)
if (this.checkRows.length === 2) {
console.log(this.checkRows)
for (let i = this.checkRows[0]; i <= this.checkRows[1]; i++) {
let bData = this.displayed_array
bData[i]['rowCheck'] = true
console.log('test')
}
}
}
},
},
There doesn't seem to be anything wrong with the code. However, right now if you're not holding shift, the first click is not registered in the checkRows variable. So you have to hold shift on the first click for it to work in two clicks.
if (this.keycheck) {
this.checkRows.push(key);
I replicated it in this codepen: https://codepen.io/CodingDeer/pen/pozbadW
FYI it's also better to set the array to empty using this.checkRows.length = 0; because it doesn't make a copy of the array.
I figure this out. By adding this.$forceUpdate() it updates the data. This was previously done by the third click.
methods: {
checkbox(key) {
if (this.keycheck) {
this.checkRows.push(key)
if (this.checkRows.length === 2) {
console.log(this.checkRows)
for (let i = this.checkRows[0]; i <= this.checkRows[1]; i++) {
let bData = this.displayed_array
bData[i]['rowCheck'] = true
console.log('test')
this.$forceUpdate()
}
}
}
},
Related
I have from where you can add dynamic unlimited inputs in most pages i have over 500 inputs the bigest one have 1450 inputs.
When you type in any input i like to check for duplicate values in other inputs.
Atm i think to build my code like this
$('.request').keyup(function(){
const current_element = $(this)
$('.request').not(this).each(function(key, element){
if (current_element.val() == element.val()) {
console.log('error')
}
})
})
But this do not look very good to scan each time all elements. Is there any better way i can do this?
Use input values as keys to let JS do the iterating for you. Construct a map object with your initial values (if any), then when an input changes you can check if the key already exists in your object. The values of the object could be arrays of elements with the same input value, to highlight each one as being in an error state until there is only one element in the array. Store the previous value on each input so that you know where to "move" it from on your map object.
I recommend you debounce the keyup event, and also listen on change for pastes, autofills, etc. Otherwise you're slowing down your code trying to make irrelevant matches before the user has finished typing.
This is an untested example but it should get you most of the way there:
(function() {
const inputs = new Map(),
previousValues = new Map();
$('.request').each(function() {
// populate maps
if (this.value) {
previousValues.set(this, this.value);
const same = inputs.get(this.value);
if (same) {
same.push(this);
}
else {
inputs.set(this.value, [this]);
}
}
}).on('keyup change', debounce(function() {
const previousValue = previousValues.get(this);
if (this.value) {
if (previousValue == this.value) {
return; // unchanged
}
previousValues.set(this, this.value);
}
else if (previousValue) {
previousValues.delete(this);
}
else {
return; // unchanged
}
const samePrevious = previousValue && inputs.get(previousValue),
sameCurrent = this.value && inputs.get(this.value);
if (samePrevious) {
if (samePrevious.includes(this)) { // should always exist
samePrevious.splice(samePrevious.indexOf(this), 1); // remove without creating new array
}
switch (samePrevious.length) {
case 0:
inputs.delete(previousValue);
break;
case 1:
// remove error condition from single remaining element in samePrevious
break;
}
}
let clearError = true;
if (sameCurrent) {
if (!sameCurrent.includes(this)) { // should never exist
sameCurrent.push(this);
}
if (sameCurrent.length > 1) {
clearError = false;
// set error condition on all elements in sameCurrent
}
}
if (clearError && currentHasError()) { // currentHasError() is any expression to get current element's status
// remove error condition from current element
}
}));
inputs.forEach(function(same, text) {
if (same.length > 1) {
// set error condition on the elements in same
}
});
})();
i have some problem with my array
let me explain the issue
first i want you guys see my codes to be on the same page
Note: this example code (not full version)
Code:
let checked = []
$.get("https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&page=1&per_page=5", status => { createDiv(status, checked)})
function createDiv(status, checked) {
$(".checkbox").click(function (e) {
if (!checked.some(i => i.id.includes($(this).attr('id'))) && checked.length < 3) {
checked.push({
id: $(this).attr('id'),
})
} else if (checked.some(i => i.id.includes($(this).attr('id')))) {
checked = checked.filter(function (a) { return a.id != `${$(e.target).attr('id')}` })
}})}
So when i click on checked it's push to array object and if i click on the same checked button it's filter the array because is already in array.
the problem is the array let checked = [] only update inside the function, what that means if i console.log checked inside function the array is filtered fine but if i console.log checked outside the array is not update
how can i update the array also outside function
i hope you guys have solution for me :)
You declared checked in the function head. So checked exists in local scope. I think if you change the first checked from let checked = [] to var checked = [] and remove the checked in function head it should work.
var checked = []
function createDiv(status) {
$(".checkbox").click(function (e) {
if (!checked.some(i => i.id.includes($(this).attr('id'))) && checked.length < 3) {
checked.push({
id: $(this).attr('id'),
})
} else if (checked.some(i => i.id.includes($(this).attr('id')))) {
checked = checked.filter(function (a) { return a.id != `${$(e.target).attr('id')}` })
}})}
The array will updated outside because it is a reference to a Array. The problem here is you must get data of the array after you click. It mean you must get data in callback at click function. Because this is a async so you only get data at the callback function if you want data is data that after clicked.
let checked = []
$.get("https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&page=1&per_page=5", status => { createDiv(status, checked)})
function createDiv(status, checked) {
$(".checkbox").click(function (e) {
if (!checked.some(i => i.id.includes($(this).attr('id'))) && checked.length < 3) {
checked.push({
id: $(this).attr('id'),
})
} else if (checked.some(i => i.id.includes($(this).attr('id')))) {
checked = checked.filter(function (a) { return a.id != `${$(e.target).attr('id')}` })
}
console.log(checked);})}
I am writing some JavaScript to manipulate a form. The form originates from Gravity Forms on WordPress so I don't have direct manipulation of the form. I am using Custom HTML to load a JS file. It's working fine, but my question is specific to JavaScript.
I have 3x date fields for MM, DD, and YYYY. I would like to execute a function when all of these fields are completely entered.
Currently, I have this, which I found on another thread and it works for one field's test. It's listening for keyup event and then testing length, and then calling doMyFunction(). The input_9_60_1 is the working ID of the field.
document.querySelector('input[id="input_9_60_1"]').onkeyup = function() {
if (this.value.length === 2) doMyFunction();
}
What I need to do is test input_9_60_1, input_9_60_2, and input_9_60_3 for lengths of 2, 2, and 4 respectively, and if all three are true, then doMyFunction(). Is there a simple way of doing this?
I am thinking I need to set some variables for the 3x length values, and then evaluate against all of them on each input's onkeyup event.
You could make an array of selectors and lengths to check against:
const inputs = ['#input_9_60_1', '#input_9_60_2', '#input_9_60_3']
.map(([sel]) => document.querySelector(sel));
const validLengths = [2, 2, 4];
for (const input of inputs) {
input.addEventListener('keyup', () => {
if (inputs.every((input, i) => input.value.length === validLengths[i])) {
doMyFunction();
}
});
}
The easiest way: use a global variable: the state of your page. it will keep data on the inputs. e.g.:
let state = {
input_9_60_1: false,
input_9_60_2: false,
input_9_60_3: false
}
// Same event for input_9_60_2 and input_9_60_3
document.querySelector('input[id="input_9_60_1"]').onkeyup = function() {
if (this.value.length === 2) {
state.input_9_60_1 = true;
doMyFunction();
}
}
function doMyFunction(){
if(state.input_9_60_1 && state.input_9_60_2 && state.input_9_60_3){
// run your code
}
}
simple and effective
I think I got it myself but please let me know if there's a better way. I'm sure I can shorten this up too...
mm_length = document.getElementById("input_9_60_1").value.length;
dd_length = document.getElementById("input_9_60_2").value.length;
yyyy_length = document.getElementById("input_9_60_3").value.length;
document.querySelector('input[id="input_9_60_1"]').onkeyup = function() {
mm_length = this.value.length;
if (mm_length==2 && dd_length==2 && yyyy_length==4) {
doFunction1();
}
else doFunction2();
}
document.querySelector('input[id="input_9_60_2"]').onkeyup = function() {
dd_length = this.value.length;
if (mm_length==2 && dd_length==2 && yyyy_length==4) {
doFunction1();
}
else doFunction2();
}
document.querySelector('input[id="input_9_60_3"]').onkeyup = function() {
yyyy_length = this.value.length;
if (mm_length==2 && dd_length==2 && yyyy_length==4) {
doFunction1();
}
else doFunction2();
}
function doFunction1() {
// stuff;
}
function doFunction2() {
// stuff;
}
I am making a website, http://phaidonasgialis.xyz. I want to make the tiles to turn and every other tile back. Until now it works except one small bug, the tile that you pressed does not turn back until you click second time.Any help would be thankful.
Here is the code:
$(document).ready(function() {
var previous = [];
$('.flip-card').each(function(i, obj) {
$(this).click(function() {
if ($(this).find('.flip-card-inner').hasClass('flip-card-transform')) {
$(this).find('.flip-card-inner').removeClass('flip-card-transform');
} else {
$(this).find('.flip-card-inner').addClass('flip-card-transform');
previous.push(this);
if (previous.length >= 2 && previous[previous.length - 2] != previous[previous.length - 1]) {
for (var i = 0; i < previous.length; i++) {
if ($(this).find('.flip-card-inner').hasClass('flip-card-transform')) {
$(previous[i - 1]).find('.flip-card-inner').removeClass('flip-card-transform');
console.log("2")
} else {
$(this).find('.flip-card-inner').addClass('flip-card-transform');
console.log("3")
}
}
}
}
});
});
If I understand your question correctly, you would like a card to flip back automatically after a certain period of time? If so then you just should set a Timeout and remove the class flip-card-transform from .flip-card-inner.
Aside note: you should definitely optimize your code by using a variable instead of doing $(this).find('.flip-card-inner')– converting this to jQuery object and searching trough its children every time when you need your flip-card-inner. Also instead of $('.flip-card').each(...) you could directly use $('.flip-card').click(...). And also as Harun Yilmaz suggested in his comment you don't need previous as an array...
So something like this should work:
$(document).ready(function () {
var previous
var timeout
$('.flip-card').click(function () {
var cardInner = $(this).find('.flip-card-inner')
if (cardInner.hasClass('flip-card-transform')) {
cardInner.removeClass('flip-card-transform')
clearTimeout(timeout)
} else {
cardInner.addClass('flip-card-transform')
// Set a Timeout
timeout = setTimeout(function () {
cardInner.removeClass('flip-card-transform')
}, 2000) // set whatever time sutable for you
// Also as Harun Yilmaz suggested in his comment you don't need previous as an array
if (previous && previous.hasClass('flip-card-transform')) {
previous.removeClass('flip-card-transform')
}
previous = cardInner
}
})
})
I have a hotkey which clicks an element on the page and uses the default key 'r'. I also have an input which allows the hotkey to be changed when a key is inputted. When a value is inputted it is cached to local-storage. The only issue is the default key doesn't work with the event, only when the input is set to a value.
let IsInputing = true
let key = 82
setTimeout(() => {
key = localStorage.getItem('savedKey');
input.onchange = function(){
localStorage.setItem('savedKey', key)
}
window.addEventListener("keydown", activate, false);
function activate(key) {
if (IsInputing == false) {
if (key.keyCode == key) {
console.log('key pressed')
element.click();
}
}
}
input.onkeydown = function (key) {
IsInputing = true
key = key.keyCode;
console.log('key changed')
setTimeout(changeKey, 1000)
}
function changeKey() {
IsInputing = false;
}
}, 500);
Your problem is that, the first time the code runs, you set key to 82, and then you set it to whatever localStorage.getItem returns, which will return null if nothing is cached (first time).
let key = 82;
setTimeout(() => {
key = localStorage.getItem('savedKey');
});
This means that your code essentially does:
key = 82;
setTimeout(() => {
key = null; // <-- you overwrite the key with null on the first run
});
Try setting the default value for key only if localStorage doesn't have a previously cached value:
let DEFAULT_KEY = 82;
setTimeout(() => {
key = localStorage.getItem('savedKey') || DEFAULT_KEY;
});
Or more tersely:
setTimeout(() => {
key = localStorage.getItem('savedKey') || 82;
});
Note: to avoid some potential future bugs, you might want to convert your cached value to a number when you return it (localStorage only saves strings).
key = Number(localStorage.getItem('savedKey'));
Or use a string as your default value.
let DEFAULT_KEY = '82';
setTimeout(() => {
key = localStorage.getItem('savedKey') || DEFAULT_KEY;
});
Having consistent types would avoid errors such as unexpected comparisons:
'82' == 82 // true
'82' === 82 // false
As mentioned in the comments to my answer, you have another bug in the activate function.
You are naming the parameter of activate as key, which will shadow the global key variable when you do the key.keyCode == key comparison.
function activate(key) {
// ^^^ you are using the same name as the global key
if (IsInputing == false) {
if (key.keyCode == key) {
console.log('key pressed')
element.click();
}
}
}
If key within activate was for example 'abcd', you code will do 'abcd'.keyCode == 'abcd'.
One way to solve it is to rename the activate's parameter:
function activate(activationKey) {
if (IsInputing == false) {
if (activationKey.keyCode == key) {
console.log('key pressed')
element.click();
}
}
}