JavaScript keeping array in order - javascript

I am trying to keep an array in a particular order, and I've learned about the splice function to insert in a specific index location, but it doesn't seem to do the right thing.
Here's a fiddle of what I have so far: http://jsfiddle.net/drumichael/7r2pV/277/
In section 1, you can select one or many of the options, however "First" should always be 1st, "second" 2nd, and "Fourth" after both of those. If you only choose "First" & "Fourth", they should be in that order regardless of the order in which you checked the boxes.
In section 2 - you may only choose ONE of Options A, B, & C... and that option should always be in the 3rd position of the array (unless you didn't choose ANY items from Section 1 of course, it would be all by itself inside the array).
Honestly I'm not sure where to go from here. I've tried this:
var array_position = $(this).attr('id');
var messagePart = $(this).attr('data-message');
message.splice(array_position, 0, messagePart);
But they still dont end up in the proper order.

Since the id's are numbered in the order you want, you can sort $('.mycheckbox:checked') by id. Then use the $.each function.
$(".mycheckbox").on("change", function() {
var message = [];
$('.mycheckbox:checked').sort(function(a, b) {
return parseInt(a.id) > parseInt(b.id);
}).each(function() {
var messagePart = $(this).attr('data-message');
message.push(messagePart);
});
$(".summary").html(message.join(", "));
});
DEMO:
http://jsfiddle.net/kom7hd5o/
Resource:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

When using splice() the index the item is inserted to should be 0-indexed. For example:
var arr = [1,2,3,5,6];
arr.splice(3, 0, 4);
will insert 4 into the 3rd 0-indexed location.
Also, you need to use 0 as the 2nd parameter in splice when inserting a value like you want.
To make your code work you should change the id parameter of each input to be 0-indexed, and then use splice() to insert the item.
$(".mycheckbox").on("change", function() {
var message = [];
$('.mycheckbox:checked').each(function() {
var messagePart = $(this).attr('data-message');
message.splice($(this).attr('id'), 0,messagePart);
});
$(".summary").html(message.join(", "));
});
Here's a working example:
$(".mycheckbox").on("change", function() {
var message = [];
$('.mycheckbox:checked').each(function() {
var messagePart = $(this).attr('data-message');
message.splice($(this).attr('id'), 0,messagePart);
});
$(".summary").html(message.join(", "));
});
$("input:checkbox").on('click', function() {
// in the handler, 'this' refers to the box clicked on
var $box = $(this);
if ($box.is(":checked")) {
// the name of the box is retrieved using the .attr() method
// as it is assumed and expected to be immutable
var group = "input:checkbox[name='" + $box.attr("name") + "']";
// the checked state of the group/box on the other hand will change
// and the current value is retrieved using .prop() method
$(group).prop("checked", false);
$box.prop("checked", true);
} else {
$box.prop("checked", false);
}
});
.selection_area {
border: 1px solid gray;
margin-bottom: 10px;
padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="selection_area">
<h4>Select as many of these options as desired</h4>
<p>Should appear in numerical order</p>
<input type="checkbox" class="mycheckbox" id="1" data-message="Second" />Second
<br/>
<input type="checkbox" class="mycheckbox" id="0" data-message="First" />First
<br/>
<input type="checkbox" class="mycheckbox" id="3" data-message="Fourth" />Fourth
<br/>
</div>
<h5>Summary of Selections:</h5>
<div class="summary"></div>
Here's an updated JSFiddle.

Related

dynamic link based on checkbox values => Issue when deselecting the checkbox the url value multiplies

I am trying to generate link on the button when multiple Checkbox is clicked based on the value. I have used the below link and it's working fine and I am able to generate link.
Create a dynamic link based on checkbox values
The issue is that when I select the checkbox for the first time it generates a link to /collections/all/blue+green but when I again select/deselect the different value its duplicates and ADDs the values with old Link → to collections/all/blue+green+blue+green
For Live Issue check on mobile View Click on filter on bottom => https://faaya-gifting.myshopify.com/collections/all
$("input[type=checkbox]").on("change", function() {
var arr = []
$(":checkbox").each(function() {
if ($(this).is(":checked")) {
arr.push($(this).val())
}
})
var vals = arr.join(",")
var str = "http://example.com/?subject=Products&checked=" + vals
console.log(str);
if (vals.length > 0) {
$('.link').html($('<a>', {
href: str,
text: str
}));
} else {
$('.link').html('');
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<input type="checkbox" name="selected" value="Blue" class="products"> Blue<br>
<input type="checkbox" name="selected" value="Green" class="products"> Green<br>
<input type="checkbox" name="selected" value="Purple" class="products"> Purple
<br>
<span class="link"></span>
For Live Issue check on mobile View Click on filter on bottom => https://faaya-gifting.myshopify.com/collections/all
I see what's going on now. What's causing the duplicate is actually multiple checkboxes having the checked value.
If you run this code in your console, you should see that you have twice of the item checked as its length:
// Run this in your browser console
$('input[type=checkbox]:checked').length
For example, if I have Wood and Metal checked and clicked Apply, running the code above gives length of 4 instead of just 2. Meaning, you have a duplicate checkbox input for each filter hidden somewhere in your code. I have verified this.
As options, you can:
Try to remove the duplicate checkbox input – Best Option; or
Add class to narrow down your selector to just one of the checkbox input containers.
Here's a screenshare of what's going on: https://www.loom.com/share/2f7880ec3435427a8378050c7bf6a6ea
UPDATED 2020/06/09:
If there's actually no way to modify how your filters are displayed, or add classes to narrow things down, we can opt for an ad hoc solution which is to actually, just remove the duplicates:
// get all your values
var arr = []
$(":checkbox").each(function() {
if ($(this).is(":checked")) {
arr.push($(this).val().toLowerCase())
}
})
// removes duplicates
var set = new Set(arr)
// convert to array and join
var vals = [...set].join(",")
I am bit bad in jquery. Am I doing right?
Should I just add the script which I have written below
$("input[type=checkbox]").on("change", function() {
var arr = []
$(":checkbox").each(function() {
if ($(this).is(":checked")) {
arr.push($(this).val().toLowerCase())
}
})
// removes duplicates
var set = new Set(arr)
// convert to array and join
var vals = [...set].join(",")
var str = "http://example.com/?subject=Products&checked=" + vals
console.log(str);
if (vals.length > 0) {
$('.link').html($('<a>', {
href: str,
text: str
}));
} else {
$('.link').html('');
}
})
Am I right?
Or should I add any values on var set = new Set(arr) or var vals = [...set]
Thank you for you help Algef

Function variables don't work in jQuery?

Im making a jquery function, but im getting trouble with some variables. I cant get the value of #op1 to the input and in #z1 it shows "i" instead of "Start. Also the counter parameter doesnt add up. It only shows "0". In the click event it gets added up.
javascript code:
$(function() {
$(function () {
var inpreco = [];
var altpreco = [];
var cpcounter9 = 0;
$(".opcaopreco").click(function () {
SuperF(this, "#preco", "inpreco", "altpreco", "cpvalor", "cpindex",
"cpactive", "cpcounter9", "preco");
});
function SuperF(element, input, inpArray, secArray, inpValue, secIndex,
inpActive,
counter, msqlip) {
var inpValue = $("#" + element.id).val();
var secIndex = $("#" + element.id).data(secIndex);
var inpActive = $("#" + element.id).data(inpActive);
if (inpArray[0] == "") {
counter++;
$("#" + element.id + "l").addClass("activa");
$(element).data(inpActive, "primary");
inpArray[0] = (inpValue);
input.val(inpArray[0]);
}
$("#z1").html(inpArray[0]);
$("#z2").html(counter);
$("#z3").html(cpcounter9);
};
});
});
html code:
<input id="preco" type="text" name="preco" value=''><br><br>
<div id="op1l" class="input">
<input type="checkbox" id="op1" class="opcaopreco" value="Start" data-cpindex="1" data-cpactivo="">
<label for="op1"></label>
<span class="itext">Test</span>
</div>
<ul id="z">
<li id="z1">z1</li>
<li id="z2">z2</li>
<li id="z3">z3</li>
</ul>
You're passing in strings for your parameters, not elements. So when you index that parameter you're getting the first character in the string.
You need to use the strings as selectors to get their associated elements and then pass their return values into your function:
// use the strings to make a selection
var preco = $('#preco');
var inpreco = $('inpreco');
// etc.
// pass the results of each selection into your function
SuperF(this, preco, inpreco, ...)
You can do this inline as well:
SuperF(this, $("#preco"), $("inpreco"), ...)
Similarly, you have other variables you're trying to pass as strings, rather than passing them by name like this:
SuperF(this, $('#preco'), inpreco, altpreco, cpvalor, cpindex, cpactive, cpcounter9, preco);
That is the reason your function can't access most of the parameters and why your counter remains at 0.

Delete Specific <li> elements using JavaScript

The function does not seem to delete the Node containing the specified value unless it is first value (in this case 'apples'). The for loop also has to execute twice before deletion of any kind. Why is that so?
function removeSpec()
{
var query = document.getElementById('spec').value; /* Value inputted by user */
elements = document.getElementsByClassName('fruit'); /* Get the li elements in the list */
var myList = document.getElementById("myList3"); /* Var to reference the list */
var length = (document.getElementsByClassName('fruit').length); /* # of li elements */
var checker = 'false'; /* boolean-ish value to determine if value was found */
for(var counter = 0; counter < length; counter ++)
{
if (elements[counter].textContent == query )
{
alert("Counter : " + counter);
myList.removeChild(myList.childNodes[ (counter) ]);
checker="true";
}
}
if ( checker == "false")
{
alert("Not Found");
}
}
The corresponding HTML:
<ul id="myList3">
<li class="fruit" >Apples</li>
<li class="fruit" >Oranges</li>
<li class="fruit" >Banannas</li>
<li class="fruit">Strawberry</li>
</ul>
<form>
Value: <input type="text" name="" value="" id="spec">
<br><br>
</form>
<button type="button" style="height:20px;width:200px" href="javascript:void(0)" onclick="removeSpec()" >
Remove Specified
</button>
childNodes returns a list of all child nodes. That includes text nodes. Between every <li> element you have a text node that contains spaces and a line break. So, childNodes returns a list of 9 nodes, but you are assuming list of 4 nodes (document.getElementsByClassName('fruit').length).
You could use .children instead of .childNodes. .children returns a list of only element nodes. Or better yet, use elements, since that's what you are iterating over.
You also need to stop iterating after you found an removed a node, otherwise you will be trying to access a position that doesn't exist anymore.
function removeSpec()
{
var query = document.getElementById('spec').value; /* Value inputted by user */
elements = document.getElementsByClassName('fruit'); /* Get the li elements in the list */
var myList = document.getElementById("myList3"); /* Var to reference the list */
var length = (document.getElementsByClassName('fruit').length); /* # of li elements */
var checker = 'false'; /* boolean-ish value to determine if value was found */
for(var counter = 0; counter < length; counter ++)
{
if (elements[counter].textContent == query )
{
myList.removeChild(myList.children[ (counter) ]);
// better: myList.removeChild(elements[counter]);
checker="true";
break;
}
}
if ( checker == "false")
{
alert("Not Found");
}
}
<ul id="myList3">
<li class="fruit" >Apples</li>
<li class="fruit" >Oranges</li>
<li class="fruit" >Banannas</li>
<li class="fruit">Strawberry</li>
</ul>
<form>
Value: <input type="text" name="" value="" id="spec">
<br><br>
</form>
<button type="button" style="height:20px;width:200px" href="javascript:void(0)" onclick="removeSpec()" >
Remove Specified
</button>
There are other things that could be improved (e.g. why not assign an actual boolean value to checker?), but they are not related to your question.
I run this code. you should add this line
elements[counter].remove();
instead of this line
myList.removeChild(myList.childNodes[ (counter) ]);
Instead of for loop you can consider of doing it the below way.
check this snippet
function removeSpec() {
var query = document.getElementById('spec').value; /* Value inputted by user */
var elements = document.getElementsByClassName('fruit'); /* Get the li elements in the list */
var myList = document.getElementById("myList3"); /* Var to reference the list */
var length = (document.getElementsByClassName('fruit').length); /* # of li elements */
var checker = 'false'; /* boolean-ish value to determine if value was found */
myList.querySelectorAll('li').forEach(function(item) {
if (item.innerHTML == query)
item.remove();
});
}
<ul id="myList3">
<li class="fruit">Apples</li>
<li class="fruit">Oranges</li>
<li class="fruit">Banannas</li>
<li class="fruit">Strawberry</li>
</ul>
<form>
Value:
<input type="text" name="" value="" id="spec">
<br>
<br>
</form>
<button type="button" style="height:20px;width:200px" href="javascript:void(0)" onclick="removeSpec()">
Remove Specified
</button>
Hope it helps
This might sound crazy, but Chrome seems to parse your HTML unordered list into the following:
NodeList[9]
0: text
1: li.fruit
2: text
3: li.fruit
4: text
5: li.fruit
6: text
7: li.fruit
8: text
length: 9
__proto__: NodeList
Essentially, it appears to be creating a text node in your unordered list for each newline inside the tag. This also explains why deletion only occurs after you call the function a second time - it deletes the text node first, then it deletes the actual element on its second try.
Simple converting your HTML to the following form solves the problem (but is not very pretty):
<ul id="myList3"><li class="fruit">Apples</li><li class="fruit">Oranges</li><li class="fruit">Banannas</li><li class="fruit">Strawberry</li></ul>
There are some workarounds that you can try using. For example, you could try using the childNode.remove() method instead, though not all browsers support this.
Alternatively, something like this might also work:
selectedChildNode.parentNode.removeChild(selectedChildNode);
here the problem is in myList.removeChild(myList.childNodes[ (counter) ]); because myList.childNodes node return 8 values instead of 4. We have elements array with 4 nodes, hence the removing from elements array yields a proper result
Try the code snippet below,
function removeSpec() {
var query = document.getElementById('spec').value;
elements = document.getElementsByClassName('fruit');
var myList = document.getElementById("myList3");
var length = elements.length;
var checker = 'false';
for(var counter = 0; counter < length; counter ++)
{
if (elements[counter].textContent == query )
{
alert("Counter : " + counter);
myList.removeChild(elements[counter]);
checker="true";
}
}
if ( checker == "false")
{
alert("Not Found");
}
}
myList is an array of li element so removeChild on myList is logically not correct.
Also, myList.childNodes doesn't make sense here.
Try
myList[counter].parentNode.removeChild(myList[counter]);

Store values from dynamically generated text boxes into array

I'm creating a Time table generating website as a part of my project and I am stuck at one point.
Using for loop, I am generating user selected text boxes for subjects and faculties. Now the problem is that I cannot get the values of those dynamically generated text boxes. I want to get the values and store it into array so that I can then later on store it to database
If I am using localstorage, then it sometimes shows NaN or undefined. Please help me out.
Following is my Jquery code
$.fn.CreateDynamicTextBoxes = function()
{
$('#DynamicTextBoxContainer, #DynamicTextBoxContainer2').css('display','block');
InputtedValue = $('#SemesterSubjectsSelection').val();
SubjectsNames = [];
for (i = 0; i < InputtedValue; i++)
{
TextBoxContainer1 = $('#DynamicTextBoxContainer');
TextBoxContainer2 = $('#DynamicTextBoxContainer2');
$('<input type="text" class="InputBoxes" id="SubjectTextBoxes'+i+'" placeholder="Subject '+i+' Name" style="margin:5px;" value=""><br>').appendTo(TextBoxContainer1);
$('<input type="text" class="InputBoxes" id="FacultyTextBoxes'+i+'" placeholder="Subject '+i+' Faculty Name" style="margin:5px;" value=""><br>').appendTo(TextBoxContainer2);
SubjectsNames['SubjectTextBoxes'+i];
}
$('#DynamicTextBoxContainer, #UnusedContainer, #DynamicTextBoxContainer2').css('border-top','1px solid #DDD');
}
$.fn.CreateTimeTable = function()
{
for (x = 0; x < i; x++)
{
localStorage.setItem("Main"+x, +SubjectsNames[i]);
}
}
I am also posting screenshot for better understanding
I understand you create 2 text boxes for each subject, one for subject, and second one for faculty. And you want it as a jQuery plugin.
First of all, I think you should create single plugin instead of two, and expose what you need from the plugin.
You should avoid global variables, right now you have InputtedValue, i, SubjectsNames, etc. declared as a global variables, and I believe you should not do that, but keep these variables inside you plugin and expose only what you really need.
You declare your SubjectNames, but later in first for loop you try to access its properties, and actually do nothing with this. In second for loop you try to access it as an array, but it's empty, as you did not assign any values in it.
Take a look at the snippet I created. I do not play much with jQuery, and especially with custom plugins, so the code is not perfect and can be optimized, but I believe it shows the idea. I pass some selectors as in configuration object to make it more reusable. I added 2 buttons to make it more "playable", but you can change it as you prefer. Prepare button creates your dynamic text boxes, and button Generate takes their values and "print" them in result div. generate method is exposed from the plugin to take the values outside the plugin, so you can do it whatever you want with them (e.g. store them in local storage).
$(function() {
$.fn.timeTables = function(config) {
// prepare variables with jQuery objects, based on selectors provided in config object
var numberOfSubjectsTextBox = $(config.numberOfSubjects);
var subjectsDiv = $(config.subjects);
var facultiesDiv = $(config.faculties);
var prepareButton = $(config.prepareButton);
var numberOfSubjects = 0;
prepareButton.click(function() {
// read number of subjects from the textbox - some validation should be added here
numberOfSubjects = +numberOfSubjectsTextBox.val();
// clear subjects and faculties div from any text boxes there
subjectsDiv.empty();
facultiesDiv.empty();
// create new text boxes for each subject and append them to proper div
// TODO: these inputs could be stored in arrays and used later
for (var i = 0; i < numberOfSubjects; i++) {
$('<input type="text" placeholder="Subject ' + i + '" />').appendTo(subjectsDiv);
$('<input type="text" placeholder="Faculty ' + i + '" />').appendTo(facultiesDiv);
}
});
function generate() {
// prepare result array
var result = [];
// get all text boxes from subjects and faculties divs
var subjectTextBoxes = subjectsDiv.find('input');
var facultiesTextBoxes = facultiesDiv.find('input');
// read subject and faculty for each subject - numberOfSubjects variable stores proper value
for (var i = 0; i < numberOfSubjects; i++) {
result.push({
subject: $(subjectTextBoxes[i]).val(),
faculty: $(facultiesTextBoxes[i]).val()
});
}
return result;
}
// expose generate function outside the plugin
return {
generate: generate
};
};
var tt = $('#container').timeTables({
numberOfSubjects: '#numberOfSubjects',
subjects: '#subjects',
faculties: '#faculties',
prepareButton: '#prepare'
});
$('#generate').click(function() {
// generate result and 'print' it to result div
var times = tt.generate();
var result = $('#result');
result.empty();
for (var i = 0; i < times.length; i++) {
$('<div>' + times[i].subject + ': ' + times[i].faculty + '</div>').appendTo(result);
}
});
});
#content div {
float: left;
}
#content div input {
display: block;
}
#footer {
clear: both;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="container">
<div id="header">
<input type="text" id="numberOfSubjects" placeholder="Number of subjects" />
<button id="prepare">
Prepare
</button>
</div>
<div id="content">
<div id="subjects">
</div>
<div id="faculties">
</div>
</div>
</div>
<div id="footer">
<button id="generate">Generate</button>
<div id="result">
</div>
</div>

How to add array items to <li> Jquery

I'm working on something really simple, a short quiz, and I am trying to make the items I have listed in a 2-d array each display as a <li>. I tried using the JS array.join() method but it didn't really do what I wanted. I'd like to place them into a list, and then add a radio button for each one.
I have taken the tiny little leap to Jquery, so alot of this is my unfamiliarity with the "syntax". I skimmed over something on their API, $.each...? I'm sure this works like the for statement, I just can't get it to work without crashing everything I've got.
Here's the HTML pretty interesting stuff.
<div id="main_">
<div class="facts_div">
<ul>
</ul>
</div>
<form>
<input id="x" type="button" class="myBtn" value="Press Me">
</form>
</div>
And, here is some extremely complex code. Hold on to your hats...
$(document).ready (function () {
var array = [["Fee","Fi","Fo"],
["La","Dee","Da"]];
var q = ["<li>Fee-ing?","La-ing?</li>"];
var counter = 0;
$('.myBtn').on('click', function () {
$('#main_ .facts_div').text(q[counter]);
$('.facts_div ul').append('<input type= "radio">'
+ array[counter]);
counter++;
if (counter > q.length) {
$('#main_ .facts_div').text('You are done with the quiz.');
$('.myBtn').hide();
}
});
});
Try
<div id="main_">
<div class="facts_div"> <span class="question"></span>
<ul></ul>
</div>
<form>
<input id="x" type="button" class="myBtn" value="Press Me" />
</form>
</div>
and
jQuery(function ($) {
//
var array = [
["Fee", "Fi", "Fo"],
["La", "Dee", "Da"]
];
var q = ["Fee-ing?", "La-ing?"];
var counter = 0;
//cache all the possible values since they are requested multiple times
var $facts = $('#main_ .facts_div'),
$question = $facts.find('.question'),
$ul = $facts.find('ul'),
$btn = $('.myBtn');
$btn.on('click', function () {
//display the question details only of it is available
if (counter < q.length) {
$question.text(q[counter]);
//create a single string containing all the anwers for the given question - look at the documentation for jQuery.map for details
var ansstring = $.map(array[counter], function (value) {
return '<li><input type="radio" name="ans"/>' + value + '</li>'
}).join('');
$ul.html(ansstring);
counter++;
} else {
$facts.text('You are done with the quiz.');
$(this).hide();
}
});
//
});
Demo: Fiddle
You can use $.each to iterate over array[counter] and create li elements for your options:
var list = $('.facts_div ul');
$.each(array[counter], function() {
$('<li></li>').html('<input type="radio" /> ' + this).appendTo(list);
}
The first parameter is your array and the second one is an anonymous function to do your action, in which this will hold the current element value.
Also, if you do this:
$('#main_ .facts_div').text(q[counter]);
You will be replacing the contents of your element with q[counter], losing your ul tag inside it. In this case, you could use the prepend method instead of text to add this text to the start of your tag, or create a new element just for holding this piece of text.

Categories

Resources