I don't exactly know how to formulate the question. Below I will describe what I am doing and what unintended consequences are encountered.
I am creating a to-do list, I found the design and functionality in a youtube video. Here is the code so far: https://jsfiddle.net/savvyd/9bu65kso/ .
Tl:dr: this is what I was recreating, mess around with it and you'll quickly understand the functionality https://codingnepalweb.com/demos/todo-list-app-javascript/.
What my method of mimicking the functionality was:
There are three sections in the to-do list: 1. all items, 2. pending items and 3. completed items. When the user clicks on a checkbox there results a behavior, either moving the box to pending or to completed sections. My idea was to attach event handlers to the list items : all, pending and completed, after the user has manipulated the checkboxes to their desire, the event-handlers were intending to 1.collect the current state of the board (after manipulation), delete the previous state of the board and create anew the following correct state of the board.
However, my code goes on an endless loop in this section :
for (let j = 0; j < box.length; j++) {
console.log(j);
console.log(box.length);
let name = box[j].querySelector("p").innerText;
let list = document.querySelector(".all");
let type = box[j].classList.contains("checked") ? "checked" : "unchecked";
boardModel.spawnPattern(name, list, type);
}
Furthemore if the below code was running, it would have a consequence of erasing the box array which was intended to capture the following state of the board.
// for (let i = 0; i < boardModel.textContainer.length; i++) {
// boardModel.removeAllChildNodes(boardModel.textContainer[i]);
// }
// boardModel.allowErase = false;
Is this a problem with event-handlers? Thank you.
Related
I am a begginer programmer and I am working on my first small project. I decided to make a notes app. It is basically completed, except that I wanted to have a button to delete all completed task and I am having problems with it. Basically, it works on localStorage, where I store the task name as a key, wit ha value of either 0(incomplete) or 1(completed). In this last function, I want to delete all the tasks with localStorage value of 1. This is my code. Once I run it, I am having problems with deleting all of the li elements. some get deleted, some not. In the console, I see two errors of tableChild not being declared. It seems weird.
document.querySelector(".btn-delete-completed").addEventListener("click", function () {
let children = listOfTasks.children; /*takes an ul element (listOfTasks) and selects its child elements*/
let arrayLenght = children.length; /*variable to see the lenght of the created arrray*/
let tableChild = 0; /*creates a variable that will be redeclared in the loop dynamically*/
for (let i = 0; i < arrayLenght; i++) { /*for loop that takes a stative arrayLenght instead of dynamic one(since we will be deleting elements)*/
tableChild = children[i]; /*redeclares a tableChild variable to a single li with the text that is a key to localStorage*/
if (localStorage.getItem(tableChild.textContent) == 1) {/*Checks for the value in localStorage, getItem() uses the key that is text of the selected element*/
tableChild.remove();/*removes the element from html*/
localStorage.removeItem(tableChild.textContent); /*removes element from localStorage*/
}
}
});
Here it is. I hope the documentation is good enough. As I said, I am just a begginer and I am posting here first time. If you have any questions, I will gladly answer them in the comments! Once again sorry if I messed something up in this post.
Thanks in advance!
When you delete elements from an array, its length changes. This is not accounted for in your code.
I find it easiest to iterate backwards in that case:
for (let i = children.length - 1; i >= 0; i--){
//...
Idea for making elements visible or invisible:
So… how the loop works right now is it for each category, it loops through each question in each category.
The idea is: Each question can be answered yes or no, and then for each question answered yes, there can be up to 5 dates added.
What I want to do:
-If yes, first date appears:
-If the first date is answered, then a second question appears, and so on.
These questions are stored in a sql server like so:
I want only the inner loop to have this ability to be visible or invisible..
My thought is to do a nested loop and check check on each element.
//Psuedo code
//For each first question which has 5 sub questions that are all set to hidden:
var questioncount = (count of the first questions)
for(int i = 0; i<questioncount; i++){
// set first variable to hold the first questions object.
var element(‘#questionElement’ + i);
// set firstElement to selected answer
var isAnsweredYes = firstElement.(‘Yes’);
for int j = 0; i<subQuestionCount; j++)
if (isAnsweredYes == True){
// jQuery selector to get an element
var query = $('#element' + j);
// check if element is Visible
var isVisible = query.is(':visible');
if (isVisible === true) {
// element is Visible
// do nothing
} else {
// element is Hidden
query.show();
}
else
{
//do nothing
}
}
}
Does my logic seem forward? or can anyone advise me in a better way?
I would use a button that says "Add another date", which is displayed under the last date field as soon as at least one is visible. That way you won't have to decide on a certain number (e.g. 5) as the maximum, plus I think it's a rather intuitive way of extending a form.
On each press of the button, create new input controls; be it in javascript or server side, it makes no real difference.
Background
I'm working on an Angular app which uses ng-repeat to make a table. One of the users found that the table sometimes contains duplicate entries, which I confirmed visually, then promptly wrote a Protractor test for.
The Test
Variable Scoping Issues
While writing the test, I noticed that the scope wasn't behaving in a way that I understood.
Naturally, the for-loop on line 61 has access to linkStorage (line 38), since it is in a higher scope. It logs that all of the objects have been successfully added to the object via the for-loop in the promise on line 47.
However, when I move the confirmation loop outside of the promise, say, before the expect block...
...linkStorage is an empty object.
Looping over the object finds no nested key-value pairs; it is truely empty.
Question (tl;dr)
Why is the linkStorage object populated inside the then statement, but not before the expectation?
Asynchronousity Strikes Again
The first example works is due to asynchronousity. Because the .getAttribute method is non-blocking, the code continues to run past it while it works. Therefore, the console loop is reached before the object has been populated; it's empty.
If you give the asynchronous code some time to run, maybe one second:
...linkStorage is populated.
Complete Solution
Chain multiple promises together to ensure code runs at the correct time.
it('should not have duplicates within the match grid', function() {
// Already on job A, with match grid shown.
var duplicate = false;
var linkStorage = {};
// Save unique links
var uniqueUserLinks = element.all(by.css('div.row table tbody tr td a'));
// get an array of href attributes
uniqueUserLinks.getAttribute('href')
.then(function(hrefs) {
// add the links to the linkStorage object
for (var i = 0; i < hrefs.length; i++) {
// if the link is already there
if( linkStorage[ hrefs[i] ] ) {
// update its counter
linkStorage[hrefs[i]] += 1
duplicate = true;
// there's already one duplicate, which will fail the test
break;
} else {
// create a link and start a counter
linkStorage[hrefs[i]] = 1;
}
};
}).then(function() {
// confirm links have been added to storage
for(var link in linkStorage) {
console.log('link:', link );
console.log('number:', linkStorage[link] );
}
}).then(function() {
expect(duplicate).toBe(false);
});
});
I have a parent page that contains a placeholder.
Span elements that contain IFrames are added to this placeholder as new IFrames are needed.
Each IFrame contains an asp control.
The parent has this code, fired onClick of the parent page:
function saveAll() {
for (i = 0; i < frames.length; i++) {
for (j = 0; j < frames[i].length; j++) {
if(frames[i][j] != null && frames[i][j] != ''){document.getElementById(frames[i][j].toString()).contentWindow.Save();}
}
}
}
which calls a save function within each control, which in turn, fires a button click, which fires the server side save function for that specific control. To someone trying to read this in 30 seconds...the previous sentence might be found to be confusing...so hopefully this might clarify the big picture here:
The problem I'm having is that the server is processing the controls out of order, despite the frames being ordered.
the frames object in the aforementioned javascript is a 2D array organized like this:
frames
[[controlA_instanceA,controlA_instanceB,controlA_instanceC],[controlB_instanceA,controlB_instanceB],
[controlC_instanceA,controlC_instanceB,etc],[etc]]
The loop, the firing of the child controls, and the frames array is all working correctly (hence why I didn't show the code here). However, could someone point me in the right direction on how to enforce the order the server processes the controls in?
Your controls need a sequence number assigned to them. Then the first step of your save all would be to decompose the 2D array into a 1D array organized by the sequence number.
When you are adding an item to the frames array you need to add them as an object with two members:
var seqNo = 0; // at some point early in the script
...
frames[x][y] = {frame: (iFrame), sequence: seqNo}; //When the Control is added
seqNo++;
where (iFrame) is the id of the frame.
Then your save all function would look like this:
function saveAll() {
var iFrames = [];
for (i = 0; i < frames.length; i++)
{
for (j = 0; j < frames[i].length; j++) {
if(frames[i][j] != null && frames[i][j] != '')
{
iFrames.push(frames[i][j]);
}
}
}
iFrames.sort(function(a,b){ return a.sequence - b.sequence});
for ( t = 0; t < iFrames.length; t++)
{
document.getElementById(iFrames[t].frame.toString()).contentWindow.Save();
}
}
In this way you are ensuring that the order is preserved because you sorted the array by the sequence number.
This is to say that the problem could be that the array appears to be sorted but it is not and and not assuming the issue is the order in which the server is responding to the click events.
EDIT:
If the server is responding to the events out of order then you may need a different approach.
If this is an ajax site and you are posting back via a web method then I would suggest creating a new web method that takes an array as a parameter.
Push all the content you wish to save into the array and then pass it back to the server for processing in a specific order.
If this is a classic forms site then you can handle a custom postback in your page load event.
Either way it sounds like you need to re-evaluate the way this is processing.
I got an array [1,2,3,4]
I need first to run over [2] that was chosen, set his styles, and than after run over the other children and reset their styles.
At the moment, my algorithm is as following:
for item in array
if item == chosenEl
set chosen classes
for item in array
if item != chosenEl
reset chosen classes for the rest of the children
Or coffe:
for item in #array
if item.element is element
item.selected = true
item.element.classList.add #selectedClass
item.element.focus()
for item in #array
if item.element isnt element
item.selected = false
item.element.classList.remove #selectedClass
I need to design the function like that, and not as a simple forElse loop, due to some restrictions in me framework.
How can I improve this code?
Thank you.
Explanation- The reason I need this design, is due to duplicated calls for 2 different functions, that conflict one the other.
I'm developing an app for LG TV. The LG TV has its own libraries. Now in my function I set the styles of chosen elements. Byt when I focus to the chosen element, I activate the LG TV onFocus listener, which in his turn control the selected styles.
So when I loop the second or third child, I set again the selected styles, of cleared elements. TLDR but thats how the loops conflicting each other. One interrupting the work of other.
The code was not written by me. I entered an existing project, and I havent written this function, so i cannot just remove the Focus() function.
Instead of looping twice, consider below code
for item in array
{
if item == chosenEl
{
set chosen classes
continue; <--use this to return to next iteration without executing next statements
}
reset chosen classes for the rest of the children
}
It will be O(n) instead of O(n) + O(n)
Edit:
I am sorry, I didn't understand your explanation. If all you need is a function which you can repeatedly call then here it is, call resetStyles multiple times by passing selected value. Please note since you haven't provided exact data types, I assumed them as integers.
<script>
var resetStyles = function(selecteditem)
{
var arr = [1,2,3,4];
for(var i=0; i < arr.length; i++)
{
if(arr[i] == selecteditem)
{
//set class here
continue;
}
//remove class here
}
};
resetStyles(2);//call this by changing the values
</script>
Why are you looping twice? Far easier to just do:
for( var i=0, l=array.length; i<l; i++) {
if( item == chosenEl) {
// set styles
}
else {
// clear styles
}
}