GTM - tracking latest input click value - javascript

My script
function() {
var inputs = document.getElementsByTagName("input"),
selectedRadios = [];
for (var i = 0;i < inputs.length;i++) {
if(inputs[i].type==="checkbox" && inputs[i].checked) {
selectedRadios.push(inputs[i].value);
}
}
return selectedRadios;
}
This script returns an array of all the checked buttons value. So, in my case, if I check three inputs the array will be for example: [167,168,169]
Issue: The array is auto sorted in ascending numerical order, while I want that the array is sorted based on which one was checked first, second, third,... .
To do that I edited the script above in this way
function() {
var inputs = document.getElementsByTagName("input"),
selectedRadios = [],
selectedRadios_latestchoice = [];
// get all checkboxs selected
for (var i = 0;i < inputs.length;i++) {
if(inputs[i].type==="checkbox" && inputs[i].checked) {
selectedRadios.push({filterValue : inputs[i].value, latestchoice : selectedRadios.length});
}
}
// sort value by last choice
selectedRadios.sort(function(a, b) {
return a.latestchoice - b.latestchoice;
}),
// parse data
var obj = JSON.parse(selectedRadios),
for (var s = 0;s < obj.length;s++) {
selectedRadios_latestchoice.push(obj[s].filterValue);
}
// return last value of the array "selectedRadios_latestchoice"
return selectedRadios_latestchoice[selectedRadios_latestchoice.length];
}
The logic is:
push the "input value" and the "array.length",
the "array.length" tell me if the input value was clicked first, second, third ..
sort the input value by the array.length value.
parse the json.
return the latest value of the array.
My attempt return an error. Any help?

There may be a more eligant way to do this, but I was able to use a forEach loop with a nested event listener running a function. In the function we run the event.target through a conditional that checks e.target.checked, if this returns true we push the value into an array, we then reduce that array creating an obj that saves the arr.length as property and the current value as value. Then we push that object into a final array to force the key from auto sorting.
const checkboxes = document.querySelectorAll('.check')
let val = []
const keyValPair = []
checkboxes.forEach((value) => {
value.addEventListener('change', getPositionAndValue)
})
function getPositionAndValue(e) {
if (e.target.checked) {
val.push(e.target.value)
var result = val.reduce(function(result, curr) {
result[val.length] = curr;
return result;
}, {})
keyValPair.push(result)
console.log(keyValPair)
}
}
<input class="check" value="167" type="checkbox">
<input class="check" value="168" type="checkbox">
<input class="check" value="169" type="checkbox">
<input class="check" value="170" type="checkbox">
<input class="check" value="171" type="checkbox">

Related

Change variable value inside forEach

I have three inputs and three variables, my goal is to change variables values with the values inside the inputs
const inputs = [
document.querySelector(".bill-input"),
document.querySelector(".custom"),
document.querySelector(".people-number"),
];
var bill = 0;
var tip = 0;
var people = 0;
i accomplished to do it this way
inputs[0].addEventListener("keyup", (e) => {
bill = Number(e.target.value);
});
inputs[1].addEventListener("keyup", (e) => {
tip = Number(e.target.value);
});
inputs[2].addEventListener("keyup", (e) => {
people = Number(e.target.value);
});
I'm pretty sure this is not the optimal way to do it, so i wanted to ask if there's a way to do it with forEach or any other method that does not require for me to write every single one each time.
Add a data attribute to each input.
Use an object to maintain the state of those inputs instead of n variables.
Have one handler that can update the object properties based on their id.
// Initialise the values object
const values = { bill: 0, tip: 0, people: 0 };
// Cache the inputs, and add listeners to them
const inputs = document.querySelectorAll('input');
inputs.forEach(input => input.addEventListener('keyup', handleChange));
// Grab the id from the input's dataset, and
// set the values object property to match
// the input value
function handleChange() {
const { id } = this.dataset;
values[id] = this.value;
console.log(JSON.stringify(values));
}
input { display: block; }
Bill<input data-id="bill">
Tip <input data-id="tip">
People <input data-id="people">
Additional documentation
Destructuring assignment
Yes, you can use forEach. I used a switch to get the index of the input element (in inputs const) to know what variable update.
Please see snippet below :
var bill = 0;
var tip = 0;
var people = 0;
const inputs = [
document.querySelector(".bill-input"),
document.querySelector(".custom"),
document.querySelector(".people-number"),
];
inputs.forEach(function(item,index){
item.addEventListener("keyup", (e) => {
const val = Number(e.target.value);
switch(index){
case 0 : bill = val; break;
case 1 : tip = val; break;
case 2 : people = val; break;
}
console.log(bill,tip,people)
});
});
<input value="3" type="number" class="bill-input">
<input value="10" type="number" class="custom">
<input value="100" type="number" class="people-number">

Function output replaces first two rows of my main JSON string

I have a JSON query and I am using console.log to present it:
var json_data = {"headers":["Month","Value","Number"],"rows":[["2018-10-01 00:00:00.0","one",209],["2018-09-01 00:00:00.0","one",274],["2018-09-01 00:00:00.0","five",183],["2018-10-01 00:00:00.0","five",164],["2018-09-01 00:00:00.0","four",214],["2018-10-01 00:00:00.0","four",192],["2018-09-01 00:00:00.0","three",128],["2018-10-01 00:00:00.0","three",125],["2018-09-01 00:00:00.0","two",199],["2018-10-01 00:00:00.0","two",169],["2018-09-01 00:00:00.0","seven",10541],["2018-10-01 00:00:00.0","seven",6139],["2018-10-01 00:00:00.0","six",169],["2018-09-01 00:00:00.0","six",233]]};
document.getElementById("original").innerHTML = json_data.rows;
<div style="background:yellow; "id="original"></div>
<div style="background:red;" id="output"></div>
And for the value "one" I have two numbers (209 and 274).
I am then using a function to groupby which works fine (output). My problem is that when I use the console.log for the initial json_data query, the first two rows are different. It seems that my function replaced the first two rows with the rows of the output (red). The function is given here:
function initialGroupBy(rows) {
const
rowMap = new Map(),
result = [],
dataTemp = [];
// Iterate over the rows.
rows.forEach(row => {
const
// Create a key, it is the first elements joined together.
key = row.slice(0,1).join();
// Check if the Map has the generated key...
if (rowMap.has(key)) {
// The map has the key, we need to add up the values
const
// Get the value for the current key.
storedRow = rowMap.get(key);
// Add the value of the current row to the row in the map.
storedRow[2] += row[2];
} else {
// The key doens't exist yet, add the row to the map.
rowMap.set(key, row);
}
});
// Iterate over all the entries in the map and push each value with the
// summed up value into the array.
rowMap.forEach(value => {
result.push(value);
});
for (i = 0; i < result.length; i++)
{
var object2 = {"date": result[i][0].slice(0,7), "num": result[i][2]};
dataTemp.push(object2);
}
return dataTemp;
}
A full snippet can be found here (Compare the first two rows of the yellow box from the two snippets):
var json_data = {"headers":["Month","Value","Number"],"rows":[["2018-10-01 00:00:00.0","one",209],["2018-09-01 00:00:00.0","one",274],["2018-09-01 00:00:00.0","five",183],["2018-10-01 00:00:00.0","five",164],["2018-09-01 00:00:00.0","four",214],["2018-10-01 00:00:00.0","four",192],["2018-09-01 00:00:00.0","three",128],["2018-10-01 00:00:00.0","three",125],["2018-09-01 00:00:00.0","two",199],["2018-10-01 00:00:00.0","two",169],["2018-09-01 00:00:00.0","seven",10541],["2018-10-01 00:00:00.0","seven",6139],["2018-10-01 00:00:00.0","six",169],["2018-09-01 00:00:00.0","six",233]]};
function initialGroupBy(rows) {
const
rowMap = new Map(),
result = [],
dataTemp = [];
// Iterate over the rows.
rows.forEach(row => {
const
// Create a key, it is the first elements joined together.
key = row.slice(0,1).join();
// Check if the Map has the generated key...
if (rowMap.has(key)) {
// The map has the key, we need to add up the values
const
// Get the value for the current key.
storedRow = rowMap.get(key);
// Add the value of the current row to the row in the map.
storedRow[2] += row[2];
} else {
// The key doens't exist yet, add the row to the map.
rowMap.set(key, row);
}
});
// Iterate over all the entries in the map and push each value with the
// summed up value into the array.
rowMap.forEach(value => {
result.push(value);
});
for (i = 0; i < result.length; i++)
{
var object2 = {"date": result[i][0].slice(0,7), "num": result[i][2]};
dataTemp.push(object2);
}
return dataTemp;
}
const damn = initialGroupBy(json_data.rows);
document.getElementById("original").innerHTML = json_data.rows;
document.getElementById("output").innerHTML =JSON.stringify(damn);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="background:yellow; "id="original"></div>
<br><br>
<div style="background:red;" id="output"></div>
I have tried changing the var to const in many cases. Am I missing a fundamental JavaScript case here?
As your value is an Array object, when you save it in your temporary Map, you're actually using a reference to your original data structure row.
So in the first forEach loop, where you sum the values you're actually changing the original array entry.
The solution is pretty simple, just clone the array:
rowMap.set(key, row.slice());
Another possibility is to use a different array to save the totals.
Here is your code with the fix.
var json_data = {"headers":["Month","Value","Number"],"rows":[["2018-10-01 00:00:00.0","one",209],["2018-09-01 00:00:00.0","one",274],["2018-09-01 00:00:00.0","five",183],["2018-10-01 00:00:00.0","five",164],["2018-09-01 00:00:00.0","four",214],["2018-10-01 00:00:00.0","four",192],["2018-09-01 00:00:00.0","three",128],["2018-10-01 00:00:00.0","three",125],["2018-09-01 00:00:00.0","two",199],["2018-10-01 00:00:00.0","two",169],["2018-09-01 00:00:00.0","seven",10541],["2018-10-01 00:00:00.0","seven",6139],["2018-10-01 00:00:00.0","six",169],["2018-09-01 00:00:00.0","six",233]]};
function initialGroupBy(rows) {
const
rowMap = new Map(),
result = [],
dataTemp = [];
// Iterate over the rows.
rows.forEach(row => {
const
// Create a key, it is the first elements joined together.
key = row.slice(0,1).join();
// Check if the Map has the generated key...
if (rowMap.has(key)) {
// The map has the key, we need to add up the values
const
// Get the value for the current key.
storedRow = rowMap.get(key);
// Add the value of the current row to the row in the map.
storedRow[2] += row[2];
} else {
// The key doens't exist yet, add the row to the map.
rowMap.set(key, row.slice());
}
});
// Iterate over all the entries in the map and push each value with the
// summed up value into the array.
rowMap.forEach(value => {
result.push(value);
});
for (i = 0; i < result.length; i++)
{
var object2 = {"date": result[i][0].slice(0,7), "num": result[i][2]};
dataTemp.push(object2);
}
return dataTemp;
}
const damn = initialGroupBy(json_data.rows);
document.getElementById("original").innerHTML = json_data.rows;
document.getElementById("output").innerHTML =JSON.stringify(damn);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="background:yellow; "id="original"></div>
<div style="background:red;" id="output"></div>
A) The const declaration creates a read-only reference to a value. It does not mean the value it holds is immutable -> Link
B) Your problem is that you are actually editing the original object in the initialGroupBy function. Maybe this answer will be helpful.
A different logic applies here and the result is handy:
var json_data = {"headers":["Month","Value","Number"],"rows":[["2018-10-01 00:00:00.0","one",209],["2018-09-01 00:00:00.0","one",274],["2018-09-01 00:00:00.0","five",183],["2018-10-01 00:00:00.0","five",164],["2018-09-01 00:00:00.0","four",214],["2018-10-01 00:00:00.0","four",192],["2018-09-01 00:00:00.0","three",128],["2018-10-01 00:00:00.0","three",125],["2018-09-01 00:00:00.0","two",199],["2018-10-01 00:00:00.0","two",169],["2018-09-01 00:00:00.0","seven",10541],["2018-10-01 00:00:00.0","seven",6139],["2018-10-01 00:00:00.0","six",169],["2018-09-01 00:00:00.0","six",233]]};
function groupBy(accumulator, item) {
const [date,extra,value] = item;
const key = date.slice(0,7);
if(!accumulator[key]){
accumulator[key] = 0
}
accumulator[key] += value;
return accumulator;
}
var damn = json_data.rows.reduce(groupBy,{});
damn = Object.keys(damn).map(function(key){
return {date: key, Value: "Total", num: damn[key]};
})
document.getElementById("original").innerHTML = json_data.rows;
document.getElementById("output").innerHTML =JSON.stringify(damn);
<div style="background:yellow; "id="original"></div>
<div style="background:red;" id="output"></div>

Storing latest input values in an array

I have the following HTML:
<input class="type" id="carbNumber">
<input class="type" id="otherId">
I want to store the input values in an object.
let data = {
inputData: []
}
document.querySelector('input').addEventListener('blur', updateItem)
function updateItem(){
data.inputData.push(this.value)
}
As I have multiple input elements I want to store their values in an array. If I input another value in the same input element - in which I had already input some other value - how can I figure out where I have stored the previous value the first time and therefore replace it with the new value?
You can use the index number from the input elements as they are returned by querySelectorAll:
const data = {
inputData: []
}
// Use the callback argument of `Array.from` and the `i` index:
Array.from(document.querySelectorAll('input'), (inp, i) => {
inp.addEventListener('input', updateItem.bind(inp, i)); // pass `i`
});
function updateItem(i){ // capture `i`
data.inputData[i] = this.value;
console.log('inputData:' + data.inputData);
}
<input id="a">
<input id="b">
<input id="c">
NB: I used the input event to display the results immediately as you type.
Simply do not use an array, use an object.
The object will map the ids of your input elements to their values
"use strict";
(function() {
var values = {};
function updateAllInputs() {
var inputs = document.querySelectorAll('input');
Array.prototype.forEach.call(inputs, function (input) {
// ensures exactly one value per input (if all inputs have ids, adjust as needed)
values[input.id] = input.value;
});
}
// if you still need an array for some reason, you can create one from the object.
function getSnapshotOfInputValuesAsArray() {
return Object.keys(values).map(function(key) {
return values[key];
});
}
}());

Jquery push separate arrays to one array.

I have this piece of code, that checks every checkbox that have been checked. I try to count the times the checkboxes is check.
Example if box 1 and 2 is checked it should parse the values to one array like ["1","2"]. But instead it keeps counting the array like ["1"], ["2"]. Even then try to push the count into the array it does not work. This information i got from Chrome developer tool.
I tried to push it to the array like this:
var count = document.querySelectorAll('input[name="check"]:checked').length;
var arr = [];
arr.push(count);
Maybe it is easier too understand if you guys see my code. Here goes!
This function checks for change in the checkboxes, if they are checked
or not.
function chBox()
{
$("#allCheckboxes").on("change", "input[name=check]", function() {
var array;
if ($(this).is(':checked')) {
var count = document.querySelectorAll('input[name="check"]:checked').length;
var arr = [];
arr.push(count);
document.getElementById("toppings").innerHTML = count;
console.log(arr);
//alert($(this).val());
// alert(document.querySelectorAll('input[name="check"]:checked').length);
//document.getElementById("pieCrust").innerHTML = $(this).val();
}
else {
alert("not checked");
}
});
}
These to input with the name attribute "check", if the one I need to
push one one array instead of multiple arrays.
<!--Pepperoni-->
<input class="form-check-input " name="check" type="checkbox" id="Pepperoni" value="Pepperoni">
<label class="form-check-label " for="Pepperoni">Pepperoni</label>
<!--Sausage-->
<input class="form-check-input chk" name="check" type="checkbox" id="Sausage" value="Sausage">
<label class="form-check-label" for="Sausage">Sausage</label>
You need to declare your array arr outside of $("#allCheckboxes").on(...)
Probably you're confused with your extra Array array, this extra variable is unused in your code.
function chBox() {
$("#allCheckboxes").on("change", "input[name=check]", function() {
var arr = []; // This is the place where you to declare your array.
if ($(this).is(':checked')) {
var count = document.querySelectorAll('input[name="check"]:checked').length;
arr.push(count);
document.getElementById("toppings").innerHTML = count;
console.log(arr);
} else {
alert("not checked");
}
});
}

How to serialize a form into an object (with tree structure)?

I have a form
<form>
<input type="text" name="Name" />
<input type="checkbox" name="Feature.Translate" />
<input type="checkbox" name="Feature.Share" />
<input type="submit" value="Convert into an object" />
</form>
I want to convert it in an object
{
Name: "John Connor's Terminator",
Feature:
{
Translate: true // if checked
// Share wasn't checked
}
}
How can I map the form to an object that has this tree structure?
Add this method to help you build the tree
// add keys to an object as a tree
// ["a", "b", "c"] will generate
// a { b: { c: def } }
// def is the value of the leaf node
var AddToTree = function(obj, keys, def)
{
for (var i = 0, length = keys.length; i < length; ++i)
obj = obj[keys[i]] = i == length - 1 ? def : obj[keys[i]] || {};
};
Create a function for a jQuery selector that will convert the form in an object
$.fn.serializeObject = function()
{
var o = {}; // final object
var a = this.serializeArray(); // retrieves an array of all form values as
// objects { name: "", value: "" }
$.each(a, function() {
var ns = this.name.split("."); // split name to get namespace
AddToTree(o, ns, this.value); // creates a tree structure
// with values in the namespace
});
return o;
};
With these two functions define you can set an event on the submit button:
$(":submit").click(function(e){
// contains the object from the form
// respecting element namespaces
var obj = $("form").serializeObject();
});
Something like the following should work:
function serializeData() {
//this is where we'll store our serialized data
var serializedData = {};
//iterate over input, select, and textarea elements
jQuery("input, select, textarea").each(function(index) {
var $element = jQuery(this);
var name = $element.attr("name");
//we only want to serialize the element if it has a 'name' attribute
if(typeof name != "undefined") {
//split on the . to get an array
var parts = name.split(/\./);
//start building the serialized data
var currentPart = serializedData;
for(var i = 0; i < parts.length; i++) {
//if this particular element doesn't already exist in our hash, create it
//and initialize it to an empty hash
if(typeof serializedData[parts[i]] == "undefined") {
currentPart[parts[i]] = {};
}
//if we're currently looking at the very last element in the array then
//it means that we need to set its value to the value of the corresponding
//input element. Otherwise, it means that there are still keys within the
//array and so we set `currentPart` to the new hash that we just created
if(i == parts.length - 1) {
//if the element is a checkbox or a radio, we need to see if it's checked
//instead of looking at its value
if($element.attr("type").toLowerCase() == "checkbox" || $element.attr("type").toLowerCase() == "radio") {
currentPart[parts[i]] = $element.is(":checked");
}
else {
currentPart[parts[i]] = $element.val();
}
}
else {
currentPart = currentPart[parts[i]];
}
}
}
});
console.log(serializedData);
}
Check out the fiddle.
All you need to do now is to bind serializeData to the submit event on the form.

Categories

Resources