Leaflet GeoJSON filters - javascript

I have managed to get the below working with help from people on here but stuck at a JS problem. Currently, my problem is the map now only shows the two markers where carp and tench both = 1 in the GeoJSON. What I am trying to do is something more dynamic:
I'd like to have the checkboxes off as default
I'd also like to fix the problem where only the results that match carp = 1 and tench = 1 show on the map. I'd like the filter to be dynamic with each selection instead of restricting the results from the off
https://jsfiddle.net/chrisjenner/0ez7k8um/
Filter code:
<div class="input" id="carp">
<input type="checkbox" class="carp" name="1" value="1" checked="true">
<label for="1">Carp</label>
</div>
<div class="input" id="tench">
<input type="checkbox" class="tench" name="2" value="1" checked="true">
<label for="2">Tench</label>
</div>
const lakes = L.geoJSON(null, {
style: function(feature) {
return {
color: feature.properties.color
};
},
filter: (feature) => {
const isCarpChecked = checkboxStates.carp.includes(feature.properties.carp)
const isTenchChecked = checkboxStates.tench.includes(feature.properties.tench)
return isCarpChecked && isTenchChecked //only true if both are true
}
}).bindPopup(function(layer) {
return layer.feature.properties.name;
}).addTo(map);
function updateCheckboxStates() {
checkboxStates = {
carp: [],
tench: []
}
for (let input of document.querySelectorAll('input')) {
if (input.checked) {
var numValue = parseInt(input.value);
switch (input.className) {
case 'carp':
checkboxStates.carp.push(numValue);
break
case 'tench':
checkboxStates.tench.push(numValue);
break
}
}
}
}
for (let input of document.querySelectorAll('input')) {
input.onchange = (e) => {
lakes.clearLayers()
updateCheckboxStates()
lakes.addData(lakes_data)
}
}
updateCheckboxStates()
lakes.addData(lakes_data)

Related

How to automatically select checkboxes if user input in "wall_amount" exceeds 3

I would like my program to automatically select all checkboxes (Specifically "Side 1, Side 2, Side 3 and Side 4") if the wall_amount input is above 3. How would this be done?
I have tried this on javascript lines 10-12. Thanks
HTML
<label for="wall_amount">Number of Walls</label>
<input type="number" value="1" min="1" max="4" step="1" id="wall_amount" name="wall_amount"></input>
<div>
Please choose where you want the walls placed
<label for="wall_side1">Side 1</label>
<input type="checkbox" id="wall_side1" name="wall_side1"></input>
<div style="display: inlineblock;">
<label for="wall_side2">Side 2</label>
<input type="checkbox" id="wall_side2" name="wall_side2"></input>
<img class="img2" src="images/reference.png" alt="Bouncy Castle">
<label for="wall_side3">Side 3</label>
<input type="checkbox" id="wall_side3" name="wall_side3"></input>
</div>
<label for="wall_side4">Side 4</label>
<input type="checkbox" id="wall_side4" name="wall_side4"></input>
</div>
Javascript
var base_length = Number(document.getElementById("base_length").value);
var base_width = Number(document.getElementById("base_width").value);
var walltype = Number(document.getElementById("walltype").value);
var checkbox_side1 = document.getElementById("wall_side1");
var checkbox_side2 = document.getElementById("wall_side2");
var checkbox_side3 = document.getElementById("wall_side3");
var checkbox_side4 = document.getElementById("wall_side4");
var wall_amount = Number(document.getElementById("wall_amount").value);
$("input:checkbox").click(function() {
let max = $("#wall_amount").val();
var bol = $("input:checkbox:checked").length >= max;
$("input:checkbox").not(":checked").attr("disabled", bol);
});
$("wall_amount").on('keyup', function () {
$('checkbox_side1').prop('checked', +$(this).val() > 3);
});
You can use the function setAttribute to check checkboxes. For example, this code (based on your example) will check your element with the id wall_side1.
checkbox_side1.setAttribute("checked", true)
Anyway, try adding this to your code as a function. Then add a conditional statement that runs the function every time your variable exceeds a certain amount.
I am still relatively new at answering questions so I hope this helps!
const checkboxes = [
"wall_side1",
"wall_side2",
"wall_side3",
"wall_side4"
].map((id) => document.getElementById(id));
const amountInput = document.getElementById("wall_amount");
amountInput.addEventListener("change", (event) => {
const value = parseInt(event.target.value || 0);
if (value === 4) {
checkboxes.forEach(
checkbox => {
checkbox.disabled = true;
checkbox.checked = true;
}
);
} else {
checkboxes.forEach(
checkbox => {
checkbox.disabled = false;
}
);
}
});

Remove Associated Checkbox Values from Array

I have (2) checkboxes: 1) Numbers, 2) Countries. When you check either of these checkboxes an associated form appears: (Associated Number -> 1, 2, 3) and (Associated Countries -> Nigeria, Morocco, Sierra Leone). When you check any checkboxes in these associated forms all the checked values get pushed in an array called “checkedValues.”
How can I specifically remove the associated values of 1) Numbers (1, 2, 3) OR 2) Countries ("Nigeria", "Morocco", "Sierra Leone") from the checkedValeus array, when 1) Numbers or 2) Countries from the Main Category is unchecked?
For example, if Numbers in main category is unchecked remove all its associated values from the checkedValues array.
const numbersForm = document.querySelector('.numbers');
const countriesForm = document.querySelector('.countries');
const numbersCheckbox = document.querySelector('#numbersCheckbox');
const countriesCheckbox = document.querySelector('#countriesCheckbox');
let checkedValues = [];
numbersCheckbox.addEventListener('change', (e) => {
if (numbersCheckbox.checked) {
numbersForm.style.display = 'block'
} else {
numbersForm.style.display = 'none'
}
})
countriesCheckbox.addEventListener('change', (e) => {
if (countriesCheckbox.checked) {
countriesForm.style.display = 'block'
} else {
countriesForm.style.display = 'none'
}
})
// Push checked values to table
const numbers = document.querySelectorAll('.num');
const country = document.querySelectorAll('.country');
const values = document.querySelectorAll('.values')
const pushToTable = function (e, form) {
if (e.target.checked) {
form.push(e.target.value)
console.log(form)
}
else {
form.splice(form.indexOf(e.target.value), 1);
console.log(form)
}
}
numbers.forEach(function (sample) {
sample.addEventListener('change', (e) => pushToTable(e, checkedValues))
});
country.forEach(function (sample) {
sample.addEventListener('change', (e) => pushToTable(e, checkedValues))
});
function uncheckAllNum() {
numbersCheckbox.addEventListener("change", (e) => {
for (let i = 0; i < numbers.length; i++) {
if (!e.target.checked) {
numbers[i].checked = e.target.checked;
numbers[i].dispatchEvent(new Event("change"))
}
}
})
};
uncheckAllNum()
function uncheckAllCountries() {
countriesCheckbox.addEventListener("change", (e) => {
for (let i = 0; i < country.length; i++) {
if (!e.target.checked) {
country[i].checked = e.target.checked;
country[i].dispatchEvent(new Event("change"))
}
}
})
};
uncheckAllCountries()
.numbers {
display: none
}
.countries {
display: none
}
<div class="categories">
<h2>Main Categories</h2>
<input type="checkbox" id="numbersCheckbox">Numbers
<br>
<input type="checkbox" id="countriesCheckbox">Countries
</div>
<div class="numbers">
<h3>Associated Numbers</h3>
<input class="num values" type="checkbox" value=1> 1
<br>
<input class="num values" type="checkbox" value=2> 2
<br>
<input class="num values" type="checkbox" value=3> 3
</div>
<div class="countries">
<h3>Associated Countries</h3>
<input class="country values" type="checkbox" value="Nigeria"> Nigeria
<br>
<input class="country values" type="checkbox" value="Morroco"> Morroco
<br>
<input class="country values" type="checkbox" value="Sierra Leone"> Sierra Leone
</div>
First of All, for in both uncheckAllNum and uncheckAllCountries, I don't see any need to put that logic inside functions as long as you are only executing them once from the same code, and if you intend to use it later you'll have a problem where you keep adding unnecessary event listeners for the same element that all do the same job.
Secondly, You can use the same event listener to handle toggling the display of the forms and updating the checked status of the checkboxes. This will help to minimize your code and make it neater.
Finally, you don't need to keep track of the toggled checkboxes, you can make use of the :checked pseudo-class and document.querySelectorAll().
You can do something like this to get all the checked checkboxes:
document.querySelectorAll('.country:checked, .num:checked')
And if you need to get the values of all checked items, you can do something like this:
const getChecked = () =>
[...document.querySelectorAll('.country:checked, .num:checked')].map(
element => element.value
)
Where it uses the spread syntax [...value] to transform the NodeList to an array which allows the use the .map() function to map each checkbox element to its value.
You can use getChecked () to get an array of the values of selected checkboxes instead of checkedValues in your code.
A working example with refactored javascript code: Example on jsFiddle

Computed Methods not updating on change checkbox value in vue js

I have made a codepen demonstrating a problem I'm having with a checkbox not working. On changes, the value of clipsData does not get updated.
https://codepen.io/bozlurrahman/pen/BeZVzR?editors=1010
<div id="video-clips-wrap">
<div>{{clipsData}}</div>
<li v-for="(clip, index) in clips" v-bind:key="index">
<div class="vl-subsource-container">
<input type="checkbox" value="issubsource" v-model="clip.subsourcesettings" v-on:change="viewSubSource(index)"><label>Not Update on change: {{clip.issubsource}}</label>
<div v-if="clip.subsourcesettings.length">
<label>Dynamic Contents</label>
</div>
</div>
<div class="meditations-options">
<label>Meditations: </label>
<input type="checkbox" value="motivation" v-model="clip.meditations"><label>1. Motivation</label>
<input type="checkbox" value="gratitude" v-model="clip.meditations"><label>2. Gratitude</label>
</div>
</li>
</div>
var video_clip_data_var = "[{\"meditations\":[\"motivation\",\"gratitude\"]}]";
var VideoClip = new Vue({
el: '#video-clips-wrap',
data: {
clips: [],
loading: false,
},
created: function () {
this.clips = JSON.parse(video_clip_data_var);
for (var i = 0; i < this.clips.length; i++) {
// if( typeof this.clips[i].meditations == "string" )
// this.clips[i].meditations = this.clips[i].meditations.split(',');
this.clips[i].subsourcesettings = "issubsource".split(',');
this.clips[i].subsources = [];
}
},
methods: {
viewSubSource: function (index) {
console.log(`this.clips[`+index+`].subsourcesettings`,this.clips[index].subsourcesettings);
console.log(`this.clips`,this.clips);
// this.clipsData = JSON.stringify(this.clips);
},
},
computed: {
clipsData: function () {
return JSON.stringify(this.clips);
},
}
});
Is there any one who can help me to fix this problem? When clicking on the check box, the hidden content should show directly.
Thanks.
Replace that
this.clips[i].subsourcesettings = "issubsource".split(',');
this.clips[i].subsources = [];
to
Vue.set(this.clips[i], 'subsourcesettings', "issubsource".split(','))
Vue.set(this.clips[i], 'subsources', [])
Here you can find more details about your problem.

Trying to set an increment controller

I'm building a simple trivia app and so far its great, but I have a problem with proceeding to the next question.
oddly enough when the user Answer correctly to the question and presses the submit button it does increment to the next question but if they answer correctly again it does nothing.
Here is my code:
<div class="app">
<h2 id="question"></h2>
<button class="options" type="button" value="val"></button>
<button class="options" type="button" value="val"></button>
<button class="options" type="button" value="val"></button>
<button class="options" type="button" value="val"></button>
</br>
<button id="submit" type="button" name="button">Submit</button>
<button id="back" type="button" name="button">Back</button>
</div>
var data = {
currentQuestion: 0,
questions:[
{
answers:[1,3,5,6],
question:'how much is 3+3',
correctAnswer:6
},
{
answers:[1,3,5,2],
question:'how much is 1+1',
currectAnswer:2
},
{
answers:[1,8,5,6],
question:'how much is 4+4',
correctAnswer:8
},
{
answers:[1,8,10,6],
question:'how much is 4+6',
correctAnswer:8
}
]
}
var options = document.querySelectorAll('.options');
var question = document.querySelector('#question');
var backBtn = document.querySelector('#back');
var submitBtn = document.querySelector('#submit');
function init() {
newQuestion();
optionClick();
evaluate();
back();
}
function newQuestion() {
question.textContent = data.questions[data.currentQuestion].question;
for(var i = 0; i< data.questions.length; i++) {
options[i].textContent = data.questions[data.currentQuestion].answers[i]
}
}
function optionClick() {
options.forEach(function(elem) {
elem.addEventListener('click', function() {
this.classList.toggle('picked')
})
})
}
function evaluate() {
submitBtn.addEventListener('click', function() {
for(i = 0; i < options.length; i++) {
if(options[i].classList.contains('picked') && options[i].textContent == data.questions[data.currentQuestion].correctAnswer && data.currentQuestion <= 6){
options[i].classList.remove('picked')
data.currentQuestion++
newQuestion();
}
}
})
}
As has already been answered in the comments, the problem was a typo, currectAnswer rather than correctAnswer. It's kind of moot answering now, but I also wanted to suggest some other improvements.
The first thing I would do is instead of using a group of buttons, use a group of radio buttons. You can style them to look like a button if you want, plus you get free functionality by doing that. Radio buttons enforce only having one answer selected without any need to check for multiple answers in code.
Instead of using named functions that when called attach an anonymous function as an event handler, just create a named function and then attach them directly to the elements.
Pulling it together it would look something like this:
'use strict';
var data = {
currentQuestion: 0,
questions:[
{
answers:[1,3,5,6],
question:'how much is 3+3',
correctAnswer:6
},
{
answers:[1,3,5,2],
question:'how much is 1+1',
correctAnswer:2
},
{
answers:[1,8,5,6],
question:'how much is 4+4',
correctAnswer:8
},
{
answers:[1,8,10,6],
question:'how much is 4+6',
correctAnswer:10
}
]
}
var options = document.querySelectorAll('#options input');
var question = document.querySelector('#question');
var backBtn = document.querySelector('#back');
var submitBtn = document.querySelector('#submit');
function nextQuestion () {
if (data.currentQuestion < data.questions.length - 1) {
data.currentQuestion += 1;
displayQuestion();
} else {
data.currentQuestion = data.questions.length;
submitBtn.removeEventListener('click', evaluate);
backBtn.removeEventListener('click', prevQuestion);
question.textContent = "Done!"
}
}
function prevQuestion () {
if (data.currentQuestion > 0) {
data.currentQuestion -= 1;
displayQuestion();
}
}
function displayQuestion () {
question.textContent = data.questions[data.currentQuestion].question;
options.forEach(function (option, index) {
let answer = data.questions[data.currentQuestion].answers[index];
// set the value of the radio button
option.value = answer;
// set the text of the label next to it
option.nextElementSibling.textContent = answer;
// reset the selected value
option.checked = false
});
}
function evaluate () {
let correctAnswer = data.questions[data.currentQuestion].correctAnswer;
// get the value of the currently selected answer
let selectedAnswer = document.querySelector('#options :checked');
if(selectedAnswer && selectedAnswer.value == correctAnswer){
nextQuestion();
}
}
submitBtn.addEventListener('click', evaluate);
backBtn.addEventListener('click', prevQuestion);
displayQuestion();
fieldset {
border: none;
}
/* highlight the label immediately after the selected radio button */
#options input:checked + label {
border: 3px red solid;
}
/* hide the actual radio button
that the labels are controling. */
#options input {
display: none;
}
/* make the label look like a button */
#options label {
padding: .5em;
background: #eee;
border: outset 1px #eee;
}
<div class="app">
<h2 id="question"></h2>
<fieldset id="options">
<input id="answer1" type="radio" name="answers"> <label for="answer1"></label>
<input id="answer2" type="radio" name="answers"> <label for="answer2"></label>
<input id="answer3" type="radio" name="answers"> <label for="answer3"></label>
<input id="answer4" type="radio" name="answers"> <label for="answer4"></label>
</fieldset>
<button id="submit" type="button" name="button">Submit</button>
<button id="back" type="button" name="button">Back</button>
</div>

How can I get form data with JavaScript/jQuery?

Is there a simple, one-line way to get the data of a form as it would be if it was to be submitted in the classic HTML-only way?
For example:
<form>
<input type="radio" name="foo" value="1" checked="checked" />
<input type="radio" name="foo" value="0" />
<input name="bar" value="xxx" />
<select name="this">
<option value="hi" selected="selected">Hi</option>
<option value="ho">Ho</option>
</form>
Output:
{
"foo": "1",
"bar": "xxx",
"this": "hi"
}
Something like this is too simple, since it does not (correctly) include textareas, selects, radio buttons and checkboxes:
$("#form input").each(function () {
data[theFieldName] = theFieldValue;
});
Use $('form').serializeArray(), which returns an array:
[
{"name":"foo","value":"1"},
{"name":"bar","value":"xxx"},
{"name":"this","value":"hi"}
]
Other option is $('form').serialize(), which returns a string:
"foo=1&bar=xxx&this=hi"
Take a look at this jsfiddle demo
$('form').serialize() //this produces: "foo=1&bar=xxx&this=hi"
demo
Updated answer for 2014: HTML5 FormData does this
var formData = new FormData(document.querySelector('form'))
You can then post formData exactly as it is - it contains all names and values used in the form.
Based on jQuery.serializeArray, returns key-value pairs.
var data = $('#form').serializeArray().reduce(function(obj, item) {
obj[item.name] = item.value;
return obj;
}, {});
document.querySelector('form').addEventListener('submit', (e) => {
const formData = new FormData(e.target);
// Now you can use formData.get('foo'), for example.
// Don't forget e.preventDefault() if you want to stop normal form .submission
});
This is a nitpicky answer, but let me explain why this is a better solution:
We're properly handling a form submit rather than a button press. Some people like to push enter on fields. Some people use alternative input devices such as speech input or other accessibility devices. Handle the form submit and you correctly solve it for everyone.
We're digging into the form data for the actual form that was submitted. If you change your form selector later, you don't have to change the selectors for all the fields. Furthermore, you might have several forms with the same input names. No need to disambiguate with excessive IDs and what not, just track the inputs based on the form that was submitted. This also enables you to use a single event handler for multiple forms if that is appropriate for your situation.
The FormData interface is fairly new, but is well supported by browsers. It's a great way to build that data collection to get the real values of what's in the form. Without it, you're going to have to loop through all the elements (such as with form.elements) and figure out what's checked, what isn't, what the values are, etc. Totally possible if you need old browser support, but the FormData interface is simpler.
I'm using ES6 here... not a requirement by any means, so change it back to be ES5 compatible if you need old browser support.
It is 2019 and there's a better way to do this:
const form = document.querySelector('form');
const data = new URLSearchParams(new FormData(form).entries());
or if you want a plain Object instead
const form = document.querySelector('form');
const data = Object.fromEntries(new FormData(form).entries());
although note that this won't work with duplicate keys like you get from multi-select and duplicate checkboxes with the same name.
Simplest way, 2022.
document.querySelector('form').addEventListener('submit', (e) => {
const data = Object.fromEntries(new FormData(e.target).entries());
console.log(data)
});
Output
{ name: 'Stackoverflow' }
use .serializeArray() to get the data in array format and then convert it into an object:
function getFormObj(formId) {
var formObj = {};
var inputs = $('#'+formId).serializeArray();
$.each(inputs, function (i, input) {
formObj[input.name] = input.value;
});
return formObj;
}
Here's a really simple and short soluton that even doesn't require Jquery.
var formElements=document.getElementById("myForm").elements;
var postData={};
for (var i=0; i<formElements.length; i++)
if (formElements[i].type!="submit")//we dont want to include the submit-buttom
postData[formElements[i].name]=formElements[i].value;
I use this:
jQuery Plugin
(function($){
$.fn.getFormData = function(){
var data = {};
var dataArray = $(this).serializeArray();
for(var i=0;i<dataArray.length;i++){
data[dataArray[i].name] = dataArray[i].value;
}
return data;
}
})(jQuery);
HTML Form
<form id='myform'>
<input name='myVar1' />
<input name='myVar2' />
</form>
Get the Data
var myData = $("#myForm").getFormData();
$("#form input, #form select, #form textarea").each(function() {
data[theFieldName] = theFieldValue;
});
other than that, you might want to look at serialize();
Here is a working JavaScript only implementation which correctly handles checkboxes, radio buttons, and sliders (probably other input types as well, but I've only tested these).
function setOrPush(target, val) {
var result = val;
if (target) {
result = [target];
result.push(val);
}
return result;
}
function getFormResults(formElement) {
var formElements = formElement.elements;
var formParams = {};
var i = 0;
var elem = null;
for (i = 0; i < formElements.length; i += 1) {
elem = formElements[i];
switch (elem.type) {
case 'submit':
break;
case 'radio':
if (elem.checked) {
formParams[elem.name] = elem.value;
}
break;
case 'checkbox':
if (elem.checked) {
formParams[elem.name] = setOrPush(formParams[elem.name], elem.value);
}
break;
default:
formParams[elem.name] = setOrPush(formParams[elem.name], elem.value);
}
}
return formParams;
}
Working example:
function setOrPush(target, val) {
var result = val;
if (target) {
result = [target];
result.push(val);
}
return result;
}
function getFormResults(formElement) {
var formElements = formElement.elements;
var formParams = {};
var i = 0;
var elem = null;
for (i = 0; i < formElements.length; i += 1) {
elem = formElements[i];
switch (elem.type) {
case 'submit':
break;
case 'radio':
if (elem.checked) {
formParams[elem.name] = elem.value;
}
break;
case 'checkbox':
if (elem.checked) {
formParams[elem.name] = setOrPush(formParams[elem.name], elem.value);
}
break;
default:
formParams[elem.name] = setOrPush(formParams[elem.name], elem.value);
}
}
return formParams;
}
//
// Boilerplate for running the snippet/form
//
function ok() {
var params = getFormResults(document.getElementById('main_form'));
document.getElementById('results_wrapper').innerHTML = JSON.stringify(params, null, ' ');
}
(function() {
var main_form = document.getElementById('main_form');
main_form.addEventListener('submit', function(event) {
event.preventDefault();
ok();
}, false);
})();
<form id="main_form">
<div id="questions_wrapper">
<p>what is a?</p>
<div>
<input type="radio" required="" name="q_0" value="a" id="a_0">
<label for="a_0">a</label>
<input type="radio" required="" name="q_0" value="b" id="a_1">
<label for="a_1">b</label>
<input type="radio" required="" name="q_0" value="c" id="a_2">
<label for="a_2">c</label>
<input type="radio" required="" name="q_0" value="d" id="a_3">
<label for="a_3">d</label>
</div>
<div class="question range">
<label for="a_13">A?</label>
<input type="range" required="" name="q_3" id="a_13" min="0" max="10" step="1" list="q_3_dl">
<datalist id="q_3_dl">
<option value="0"></option>
<option value="1"></option>
<option value="2"></option>
<option value="3"></option>
<option value="4"></option>
<option value="5"></option>
<option value="6"></option>
<option value="7"></option>
<option value="8"></option>
<option value="9"></option>
<option value="10"></option>
</datalist>
</div>
<p>A and/or B?</p>
<div>
<input type="checkbox" name="q_4" value="A" id="a_14">
<label for="a_14">A</label>
<input type="checkbox" name="q_4" value="B" id="a_15">
<label for="a_15">B</label>
</div>
</div>
<button id="btn" type="submit">OK</button>
</form>
<div id="results_wrapper"></div>
edit:
If you're looking for a more complete implementation, then take a look at this section of the project I made this for. I'll update this question eventually with the complete solution I came up with, but maybe this will be helpful to someone.
I have included the answer to also give back the object required.
function getFormData(form) {
var rawJson = form.serializeArray();
var model = {};
$.map(rawJson, function (n, i) {
model[n['name']] = n['value'];
});
return model;
}
Based on neuront's response I created a simple JQuery method that gets the form data in key-value pairs but it works for multi-selects and for array inputs with name='example[]'.
This is how it is used:
var form_data = $("#form").getFormObject();
You can find an example below of its definition and how it works.
// Function start
$.fn.getFormObject = function() {
var object = $(this).serializeArray().reduce(function(obj, item) {
var name = item.name.replace("[]", "");
if ( typeof obj[name] !== "undefined" ) {
if ( !Array.isArray(obj[name]) ) {
obj[name] = [ obj[name], item.value ];
} else {
obj[name].push(item.value);
}
} else {
obj[name] = item.value;
}
return obj;
}, {});
return object;
}
// Function ends
// This is how it's used
$("#getObject").click( function() {
var form_data = $("#form").getFormObject();
console.log(form_data);
});
/* Only to make view better ;) */
#getObject {
padding: 10px;
cursor:pointer;
background:#0098EE;
color:white;
display:inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<form id="form">
<input type="text" name="text" value="Hola amigo" />
<input type="text" name="text_array[]" value="Array 1" />
<input type="text" name="text_array[]" value="Array 2" />
<input type="text" name="text_array[]" value="Array 3" />
<select name="multiselect" multiple>
<option name="option1" selected> option 1 </option>
<option name="option2" selected> option 2 </option>
</select>
<input type="checkbox" name="checkbox" value="checkbox1" checked/>
<input type="checkbox" name="checkbox" value="checkbox2" checked/>
<input type="radio" name="radio" value="radio1" checked/>
<input type="radio" name="radio" value="radio2"/>
</form>
<div id="getObject"> Get object (check the console!) </div>
If you are using jQuery, here is a little function that will do what you are looking for.
First, add an ID to your form (unless it is the only form on the page, then you can just use 'form' as the dom query)
<form id="some-form">
<input type="radio" name="foo" value="1" checked="checked" />
<input type="radio" name="foo" value="0" />
<input name="bar" value="xxx" />
<select name="this">
<option value="hi" selected="selected">Hi</option>
<option value="ho">Ho</option>
</form>
<script>
//read in a form's data and convert it to a key:value object
function getFormData(dom_query){
var out = {};
var s_data = $(dom_query).serializeArray();
//transform into simple data/value object
for(var i = 0; i<s_data.length; i++){
var record = s_data[i];
out[record.name] = record.value;
}
return out;
}
console.log(getFormData('#some-form'));
</script>
The output would look like:
{
"foo": "1",
"bar": "xxx",
"this": "hi"
}
You can also use the FormData Objects; The FormData object lets you compile a set of key/value pairs to send using XMLHttpRequest. Its primarily intended for use in sending form data, but can be used independently from forms in order to transmit keyed data.
var formElement = document.getElementById("myform_id");
var formData = new FormData(formElement);
console.log(formData);
This will append all form fields to the JavaScript object "res":
var res = {};
$("#form input, #form select, #form textarea").each(function(i, obj) {
res[obj.name] = $(obj).val();
})
var formData = new FormData($('#form-id'));
params = $('#form-id').serializeArray();
$.each(params, function(i, val) {
formData.append(val.name, val.value);
});
For those of you who would prefer an Object as opposed to a serialized string (like the one returned by $(form).serialize(), and a slight improvement on $(form).serializeArray()), feel free to use the code below:
var Form = {
_form: null,
_validate: function(){
if(!this._form || this._form.tagName.toLowerCase() !== "form") return false;
if(!this._form.elements.length) return false;
return true;
}, _loopFields: function(callback){
var elements = this._form.elements;
for(var i = 0; i < elements.length; i++){
var element = form.elements[i];
if(name !== ""){
callback(this._valueOfField(element));
}
}
}, _valueOfField: function(element){
var type = element.type;
var name = element.name.trim();
var nodeName = element.nodeName.toLowerCase();
switch(nodeName){
case "input":
if(type === "radio" || type === "checkbox"){
if(element.checked){
return element.value;
}
}
return element.value;
break;
case "select":
if(type === "select-multiple"){
for(var i = 0; i < element.options.length; i++){
if(options[i].selected){
return element.value;
}
}
}
return element.value;
break;
case "button":
switch(type){
case "reset":
case "submit":
case "button":
return element.value;
break;
}
break;
}
}, serialize: function(form){
var data = {};
this._form = form;
if(this._validate()){
this._loopFields(function(value){
if(value !== null) data[name] = value;
});
}
return data;
}
};
To execute it, just use Form.serialize(form) and the function will return an Object similar to this:
<!-- { username: "username", password: "password" } !-->
<input type="text" value="username">
<input type="password" value="password">
As a bonus, it means you don't have to install the entire bundle of jQuery just for one serialize function.
I'm kind of supprised because no one mentioned below solution.
Get form data via document.forms.namedItem function
var form = document.forms.namedItem("fileinfo");
form.addEventListener('submit', function(ev) {
var oData = new FormData(form);
}
The HT
<form name="fileinfo">
<label>Your email address:</label>
<input type="email" autocomplete="on" autofocus name="userid" placeholder="email" required size="32" maxlength="64" /><br />
<label>Custom file label:</label>
<input type="text" name="filelabel" size="12" maxlength="32" /><br />
<label>File to stash:</label>
<input type="file" name="file" required />
<input type="submit" value="Stash the file!" />
</form>
<div></div>
I wrote a library to solve this very problem: JSONForms. It takes a form, goes through each input and builds a JSON object you can easily read.
Say you have the following form:
<form enctype='application/json'>
<input name='places[0][city]' value='New York City'>
<input type='number' name='places[0][population]' value='8175133'>
<input name='places[1][city]' value='Los Angeles'>
<input type='number' name='places[1][population]' value='3792621'>
<input name='places[2][city]' value='Chicago'>
<input type='number' name='places[2][population]' value='2695598'>
</form>
Passing the form to JSONForms' encode method returns you the following object:
{
"places": [
{
"city": "New York City",
"population": 8175133
},
{
"city": "Los Angeles",
"population": 3792621
},
{
"city": "Chicago",
"population": 2695598
}
]
}
Here's demo with your form.
function getFormData($form){
var unindexed_array = $form.serializeArray();
var indexed_array = {};
$.map(unindexed_array, function(n, i){
if(indexed_array[n['name']] == undefined){
indexed_array[n['name']] = [n['value']];
}else{
indexed_array[n['name']].push(n['value']);
}
});
return indexed_array;
}
you can use this function for have an object or a JSON from form.
for use it:
var object = formService.getObjectFormFields("#idform");
function getObjectFormFields(formSelector)
{
/// <summary>Função que retorna objeto com base nas propriedades name dos elementos do formulário.</summary>
/// <param name="formSelector" type="String">Seletor do formulário</param>
var form = $(formSelector);
var result = {};
var arrayAuxiliar = [];
form.find(":input:text").each(function (index, element)
{
var name = $(element).attr('name');
var value = $(element).val();
result[name] = value;
});
form.find(":input[type=hidden]").each(function (index, element)
{
var name = $(element).attr('name');
var value = $(element).val();
result[name] = value;
});
form.find(":input:checked").each(function (index, element)
{
var name;
var value;
if ($(this).attr("type") == "radio")
{
name = $(element).attr('name');
value = $(element).val();
result[name] = value;
}
else if ($(this).attr("type") == "checkbox")
{
name = $(element).attr('name');
value = $(element).val();
if (result[name])
{
if (Array.isArray(result[name]))
{
result[name].push(value);
} else
{
var aux = result[name];
result[name] = [];
result[name].push(aux);
result[name].push(value);
}
} else
{
result[name] = [];
result[name].push(value);
}
}
});
form.find("select option:selected").each(function (index, element)
{
var name = $(element).parent().attr('name');
var value = $(element).val();
result[name] = value;
});
arrayAuxiliar = [];
form.find("checkbox:checked").each(function (index, element)
{
var name = $(element).attr('name');
var value = $(element).val();
result[name] = arrayAuxiliar.push(value);
});
form.find("textarea").each(function (index, element)
{
var name = $(element).attr('name');
var value = $(element).val();
result[name] = value;
});
return result;
}
$( "form" ).bind( "submit", function(e) {
e.preventDefault();
console.log( $(this).serializeObject() );
//console.log( $(this).serialize() );
//console.log( $(this).serializeArray() );
});
$.fn.serializeObject = function() {
var o = {};
var a = this.serializeArray();
$.each( a, function() {
if ( o[this.name] !== undefined)
{
if ( ! o[this.name].push )
{
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
}
else
{
o[this.name] = this.value || '';
}
});
return o;
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<form>
<input type="radio" name="foo" value="1" checked="checked" />
<input type="radio" name="foo" value="0" />
<input name="bar" value="xxx" />
<select name="this">
<option value="hi" selected="selected">Hi</option>
<option value="ho">Ho</option>
</select>
<input type="submit" value="Submit" />
</form>
Codepen
I wrote a function that takes care of multiple checkboxes and multiple selects. In those cases it returns an array.
function getFormData(formId) {
return $('#' + formId).serializeArray().reduce(function (obj, item) {
var name = item.name,
value = item.value;
if (obj.hasOwnProperty(name)) {
if (typeof obj[name] == "string") {
obj[name] = [obj[name]];
obj[name].push(value);
} else {
obj[name].push(value);
}
} else {
obj[name] = value;
}
return obj;
}, {});
}
Here is a nice vanilla JS function I wrote to extract form data as an object. It also has options for inserting additions into the object, and for clearing the form input fields.
const extractFormData = ({ form, clear, add }) => {
return [].slice.call(form.children).filter(node => node.nodeName === 'INPUT')
.reduce((formData, input) => {
const value = input.value
if (clear) { input.value = '' }
return {
...formData,
[input.name]: value
}
}, add)
}
Here is an example of its use with a post request:
submitGrudge(e) {
e.preventDefault()
const form = e.target
const add = { id: Date.now(), forgiven: false }
const grudge = extractFormData({ form, add, clear: true })
// grudge = {
// "name": "Example name",
// "offense": "Example string",
// "date": "2017-02-16",
// "id": 1487877281983,
// "forgiven": false
// }
fetch('http://localhost:3001/api/grudge', {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(grudge)
})
.then(response => response.json())
.then(grudges => this.setState({ grudges }))
.catch(err => console.log('error: ', err))
}
showing form input element fields and input file to submit your form without page refresh and grab all values with file include in it here it is
<form id="imageUploadForm" action="" method="post" enctype="multipart/form-data">
<input type="text" class="form-control" id="fname" name='fname' placeholder="First Name" >
<input type="text" class="form-control" name='lname' id="lname" placeholder="Last Name">
<input type="number" name='phoneno' class="form-control" id="phoneno" placeholder="Phone Number">
<textarea class="form-control" name='address' id="address" rows="5" cols="5" placeholder="Your Address"></textarea>
<input type="file" name="file" id="file" >
<input type="submit" id="sub" value="Registration">
</form>
on Submit button page will send ajax request to your php file.
$('#imageUploadForm').on('submit',(function(e)
{
fname = $('#fname').val();
lname = $('#lname').val();
address = $('#address').val();
phoneno = $('#phoneno').val();
file = $('#file').val();
e.preventDefault();
var formData = new FormData(this);
formData.append('file', $('#file')[0]);
formData.append('fname',$('#fname').val());
formData.append('lname',$('#lname').val());
formData.append('phoneno',$('#phoneno').val());
formData.append('address',$('#address').val());
$.ajax({
type:'POST',
url: "test.php",
//url: '<?php echo base_url().'edit_profile/edit_profile2';?>',
data:formData,
cache:false,
contentType: false,
processData: false,
success:function(data)
{
alert('Data with file are submitted !');
}
});
}))
$(form).serializeArray().reduce(function (obj, item) {
if (obj[item.name]) {
if ($.isArray(obj[item.name])) {
obj[item.name].push(item.value);
} else {
var previousValue = obj[item.name];
obj[item.name] = [previousValue, item.value];
}
} else {
obj[item.name] = item.value;
}
return obj;
}, {});
It will fix issue:couldn't work with multiselects.
Here's my version in vanilla JS (tested on Chrome)
works with:
name="input"
name="form[name]" (creates an object)
name="checkbox[]" (creates an object with an array)
name="form[checkbox][]" (creates an array)
name="form[select][name]" (creates an object with an object containing only the selected value)
/**
* Get the values from a form
* #param formId ( ID without the # )
* #returns {object}
*/
function getFormValues( formId )
{
let postData = {};
let form = document.forms[formId];
let formData = new FormData( form );
for ( const value of formData.entries() )
{
let container = postData;
let key = value[0];
let arrayKeys = key.match( /\[[\w\-]*\]/g ); // Check for any arrays
if ( arrayKeys !== null )
{
arrayKeys.unshift( key.substr( 0, key.search( /\[/ ) ) ); // prepend the first key to the list
for ( let i = 0, count = arrayKeys.length, lastRun = count - 1; i < count; i++ )
{
let _key = arrayKeys[i];
_key = _key.replace( "[", '' ).replace( "]", '' ); // Remove the brackets []
if ( _key === '' )
{
if ( ! Array.isArray( container ) )
{
container = [];
}
_key = container.length;
}
if ( ! (_key in container) ) // Create an object for the key if it doesn't exist
{
if ( i !== lastRun && arrayKeys[i + 1] === '[]' )
{
container[_key] = [];
}
else
{
container[_key] = {};
}
}
if ( i !== lastRun ) // Until we're the last item, swap container with it's child
{
container = container[_key];
}
key = _key;
}
}
container[key] = value[1]; // finally assign the value
}
return postData;
}
You are all not fully correct. You cannot write:
formObj[input.name] = input.value;
Because this way if you have multiselect list - its values will be overwritten with the last one, since it's transmitted as: "param1" : "value1", "param1" : "value2".
So, correct approach is:
if (formData[input.name] === undefined) {
formData[input.name] = input.value;
}
else {
var inputFieldArray = $.merge([], $.isArray(formData[input.name]) ? formData[input.name] : [formData[input.name]]);
$.merge(inputFieldArray, [input.value]);
formData[input.name] = $.merge([], inputFieldArray);
}

Categories