How to create an empty JQuery element and populate it? - javascript

I need to create an empty JQuery element to be the outer scope list of a loop, for example :
const forms_data = ['Name', 'Email'];
const form = $();
for (const input_name of forms_data){
form.insertAfter(`<input placeholder="${input_name}" / >`)
form.append(`<input placeholder="${input_name}" / >`)
form.after(`<input placeholder="${input_name}" / >`)
}
$('form').append(form);
But none of the ways of inserting new elements into it worked for me, according to other answers here, $() seems to be the correct way.
I don't want an outer div, I want all the inserted element to be siblings.
What am I doing wrong?
jsFiddle : https://jsfiddle.net/Lusfektu/16/

You should create form element like this $('<form>) and then you can use append method.
const forms_data = ['Name', 'Email'];
const form = $('<form>');
forms_data.forEach(name => {
form.append($('<input>', {placeholder: name}))
})
$('body').append(form);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Update: You could use reduce to make array of inputs or jquery objects and then append that array to form element at once.
const forms_data = ['Name', 'Email'];
const inputs = forms_data.reduce((r, name) => {
r.push($('<input>', {placeholder: name}))
return r;
}, [])
$('form').append(inputs)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form action=""></form>

If all you are trying to do is create the inputs and append them to the form, you can use a simple array, or map the elements. $.map can be used to loop over an array, create new elements, and return them as a list at the end. Using this we can create the array, and then append them all at once to the form.
const forms_data = ['Name', 'Email'];
$('form').append($.map(forms_data, function(element){
return `<input placeholder="${element}" / >`;
}));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form></form>

You're right to use $(), but those APIs won't work. Instead use .add():
form = form.add($(`<input placeholder="${input_name}" / >`));
As the other answer notes, if you initialize the element to a <form> element then you can use the other APIs, but if you've already got a <form> on the page then that will be problematic. You could in that case initialize the container to something harmless like a <div> or a <span>.

This should do it:
const form = $('<form>');
Then simply use append on it like you did on the second line of your sample.

Related

Selecting and adding values found with getElementsByClassName to dict or list

I am very new to JavaScript and I am trying to use it to select values from HTML using document.getElementsByClassName by putting index [0] from HTMLCollection. There is either one instance of the class being present or two or more.
const pizzatype = document.getElementsByClassName("pizzapizza")[0].innerHTML;
const pizzacheese = document.getElementsByClassName("cheesecheese")[0].innerHTML;
const pizzasauce = document.getElementsByClassName("saucesauce")[0].innerHTML;
const ordertotal = document.getElementsByClassName("fiyat")[0].innerHTML;
const order_dict = {
pizzatype,
pizzacheese,
pizzasauce,
ordertotal
}
const s = JSON.stringify(order_dict);
console.log(s); // returns {"pizzatype":"value1","pizzacheese":"value2","pizzasauce":"value3","ordertotal":"value4"}
The class is set like this:
<div class="cheesecheese card-text">${pizza.cheese}</div>
I tried experimenting with for loop, index(), .length, and others but I never got it to work. What would be the way to go to get return:
{
"pizzatype": "valuex1",
"pizzacheese": "valuex2",
"pizzasauce": "valuex3",
"ordertotal": "valuex4",
"pizzatype": "valuey1",
"pizzacheese": "valuey2",
"pizzasauce": "valuey3",
"ordertotal": "valuey4"
}
It should work even when there are more than 2 instances of those classes.
There is no way to store same key multiple times in Javascript object. You can use Entries syntax instead to get something similar.
Example of entries
[
[“pizzatype”, firstval],
[“pizzatype”, secondval],
]
Or you can use array of values inside your object.
To get result like so
{
pizzatype: [firstval,secondval],
…
}
You can get it with this way
{
pizzatype: Array.from(document.getElementsByClassName(“pizzapizza”)).map(elem => elem.innerHTML)
}

Create HTML Element using jQuery dynamically

I am trying to create an HTML element in JS using jQuery from an Array of Objects have information of element tag, class, and other attributes or style.
Why Use jQuery instead HTML?
You can think of sort of dynamically append small components like buttons, inputs at certain place in html. This helps user to interact with webpage better.
Code
let array = [{el:'div',class:'card',attr:{id:'abc'},css:{display:'block'}}];
let array2 = [{el:'div'}]
const element = (array) => {
let el = [];
Object.values(array).forEach((val)=>{
el.push($(`<${val.el}>`).addClass(val.class).attr(val.attr).css(val.css))
})
return el;
}
element(array) // Works fine;
element(array2) //Throws error because we are not feeding any value to .addClass ...
it works fine when all keys are available in the array but it doesn't work when any of them missing.
Error >
Uncaught TypeError: Cannot read property 'nodeType' of undefined
how do I fix this? thanks
All you need is to decouple the tagName el from the other Object data and return that jQuery Element instance Object $:
const $element = ({el, ...rest}) => $(`<${el}/>`, rest);
const el1 = {el:'div', class:'card', id:'abc', text:"Hello", css:{color:'red'}, appendTo: "body"};
const el2 = {el:'span', class:'test', text:" World!", css:{color:'blue'}, appendTo: "body"};
const $el1 = $element(el1);
const $el2 = $element(el2);
$el1.css({fontSize: "2em"});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
From the DOCS
As of jQuery 1.8, any jQuery instance method (a method of jQuery.fn) can be used as a property of the object passed to the second parameter:
$("<tagName/>", {properties})
For an Array of data, here's how to return an Array of jQuery instances by simply passing the function $element as the Array.prototype.map() argument:
const $element = ({el, ...rest}) => $(`<${el}/>`, rest);
const els = [
{el:'div', class:'card', id:'abc', text:"Hello", css:{color:'red'}, appendTo: "body"},
{el:'span', class:'test', text:" World!", css:{color:'blue'}, appendTo: "body"}
];
const $els = els.map($element);
console.log($els);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
You have written the wrong syntax to access object item values. try below-mentioned code to access object values directly
let stringfyObject=JSON.parse(JSON.stringify(array));
el.push((`<${stringfyObject.el}>`).addClass(stringfyObject.class).attr(stringfyObject.attr).css(stringfyObject.css));

How to use a call multiple varibles in a sinlge if statement?

I have multiple variables like so
var a0001 = document.getElementById("a0001");
var a0002 = document.getElementById("a0002");
var a0003 = document.getElementById("a0003");
that I use for
a0001.blur= function() {
localStorage.setItem("a0001", a0001.innerHTML);
};
a0002.blur = function() {
localStorage.setItem("a0002", a0002.innerHTML);
};
a0003.blur = function() {
localStorage.setItem("a0003", a0003.innerHTML);
};
My question is I will be working with 100's of variables and I cant figure out how to place a "loop place holder" to process the variables like such (*** represent the place holder that will cycle throw the variables and parse them out)
***.blur= function() {
localStorage.setItem("***", ***.innerHTML);
};
I am obviously new and this is a single page localstorage project and not sure if I just am not using the right keywords to find the documentation I am looking for or ? so any help will be appreciated.
edit; markup added below
<table>
<thead>
<tr>
<th id="a0001" contenteditable='true' spellcheck="false">ID
</th><th id="a0002" contenteditable='true' spellcheck="false">First Name
</th><th id="a0003" contenteditable='true' spellcheck="false">Last Name
</th></tr></thead>
</table>
The used markup is not entirely clear, but I'm assuming you have a form with some input elements (because blur fires only on form controls and window objets by default).
Despite of the real HTML, you're making a lot of unnecessary work. You've a ton of elements, which you need to handle as a group of elements, not one by one. Using id to identify a group is error prone and also very hard to maintain, and on the top of that, a global variable of each id is created, which floods yet the strongly crowded object with unneeded variables. Also, hundreds of event listeners are not needed, you can use a single listener, which can handle all the elements. Here's a simple code showing how to handle a group of elements:
const form = document.querySelector('#list'),
storingList = new Map(),
inputs = form.querySelectorAll('.to-storage');
inputs.forEach((input, idx) => {
const itemName = 'a' + (idx.toString()).padStart(4, '0');
storingList.set(input, itemName);
});
form.addEventListener('focusout', e => {
if (e.target.className !== 'to-storage') {return;} // Quit, not a target element blurred
const itemName = storingList.get(e.target),
value = e.target.value;
console.log(itemName, value);
// localStorage.setItem(itemName, value); // Commented out to not populate localStorage of SO visitors
});
<form id="list">
Item 1 <input class="to-storage"><br>
Item 2 <input class="to-storage"><br>
Item 3 <input class="to-storage"><br>
Item 4 <input class="to-storage"><br>
Item 5 <input class="to-storage"><br>
Item 6 <input class="to-storage"><br>
Item 7 <input>
</form>
In the snippet, all the inputs which of values are stored in localStorage, are grouped with a class named to-storage. The grouped inputs and the identifiers are collected to a Map object, hence they're easy to identify in an event handler (Map object can take an element as a key). Notice the dynamic item name creation, which takes care of the running index automatically.
The focusout event listener is delegated (blur doesn't bubble and can't be delegated in a simple way) to the form, which contains all the input elements. This way you can edit and update the HTML of the page, no matter how many inputs needs to be removed or added, you don't have to make any changes to the JavaScript which stores the values.
If your markup is different and you can't apply the snippet, please add an example of your markup to the question.
You can have your variables in an array like const arr = [] then loop through each of them. The entire thing would look something like:
const arr = []
// add variables to array
arr.forEach(a => {
a.blur = function() {
localStorage.setItem(a.key, a.innerHTML);
}
})
You may want to define a unique identifier for the variable, like key or something
Do not need to create multiple variables!
Native JS
var elements = document.querySelectorAll('[id^=a000]');
Jquery
$('[id^="a000"]')
Selects elements which IDs start with a000
I will suggest two methods to solve this.
Select element by different ids and store in a array
Ex:
// Add more id of elements on this array
let selectableElementsId = ['a0001', 'a0002', 'a0003'];
selectableElementsId.forEach(elementId => {
let element = document.querySelector(`#${elementId}`);
if(!element) return;
element.blur = function(){
localStorage.setItem(element.id, element.innerHTML);
}
});
add same class to all three or more elements.
// Replace yourClassName with class name of elements
let selectedElements = Array.from(document.querySelectorAll(".yourClassName"));
selectedElements.forEach(element => {
element.blur = function(){
localStorage.setItem(element.id || selectedElements.indexOf(element), element.innerHTML);
}
});

Loop through each element and add values to a new object. Add these objects to array & access their values

I have an element called lineNumber that contains some form inputs. All the values of the inputs are stored in variables.
<div class="lineNumber">
<div class="input-container">
<label>Example Line 1 :</label>
<input type="text" name="lineText" value="John's Electrical" class="lineText">
</div>
<!-- more inputs... -->
</div>
On page load there is one instance of this element.I add new instances of lineNumber dynamically with a button.
At this stage I have retrieved all the values of the first instance and placed in variables that update onchange. The challenge is iterating for each new instance.
I would like to loop through each instance of lineNumber and store the values of each variable in an object. I would also like the object values to be updated when the variables are as well.
I have eventListeners that detect a change to the input and update the value of the variable.
//wrap this in lineNumber.length loop?
let lineText = lineNumber[i].querySelector('.lineText');
let lineTextValue = lineNumber[i].querySelector('input[name="lineText"]').value;
//end of loop?
//add event listener and update lineTextValue
lineText.addEventListener("keyup", liveTextFunction);
//store value in variable lineTextValue;
function liveTextFunction(){
lineTextValue = lineNumber[i].querySelector('input[name="lineText"]').value;
}
//createLineObject() runs when new lineNumber element added
//store values *** //
let lineObject;
function createLineObject(){
lineObject = { };
lineObject[lineNumber] = lineNumberValue;
lineObject[lineText] = lineTextValue;
};
Hoping to end up with multiple lineObjects that I can access the values from eg
lineObject[1].lineNumberValue or Object.entries(lineObject[1]);
Not sure if the objects would then go in an array? eg
objectArray[ lineObject[1], lineObject[2] ];
Hoping for some guidance on how to achieve this or constructive criticism on my approach. I'm just not sure if I'm approaching this the right way and could use some feedback.
Is every <input/> you're trying to query going to have the name="linetext"? If so, you could iterate through your "lineNumber" divs and iterate through the "linetext" inputs below them with something like this:
lineObjects = Array.from(document.querySelectorAll('div.lineNumber')).map(div => {
return Array.from(div.querySelectorAll('input[name="linetext"]')).map(input => {
// do whatever you need to do with each div and input
const lineObject = {
div: div,
input: input,
value: null, // set automatically by listener
removeListener: null // call when lineObject no longer needed
};
const listener = event => {
lineObject.value = event.target.value;
}
input.addEventListener('keyup', listener);
lineObject.removeListener = () => input.removeEventListener('keyup', listener);
return lineObject;
});
});
lineObjects = lineObjects.flat(); // convert array of arrays to flat array
As an aside, this is already pretty unwieldy and it is for a single piece of functionality. You'd also have to run this again on adding new lineNumbers after cleaning up the listeners on the existing lineObjects. I'd suggest using a framework like React if possible.

Javascript filter and match all elements in array

I have an array that looks like:
var testArr = ["40", "A1", "B9", "58"]
I want to loop over all div elements of a certain class and return only the elements where the data attribute matches ANY of the items in that array.
If I do something like this:
$("div.prodCodes").filter(function(e) {
var x1 = $(this);
var x2 = $(this).data("prodCode");
testArr.forEach(function(e) { if (e == x2) { console.log("MATCH"); } });
});
That console outputs the correct number of matches, but I cannot return those elements from the filter function.
What on earth am I missing here? I've tried creating a new array and pushing each item onto it and returning that, but it's always empty. I'm sure I'm missing something obvious here. I've also tried rewriting this using .grep() and getting nowhere. Help is appreciated.
You need to return a truthy value in filter() to have an item included.
Try :
$("div.prodCodes").filter(function(e) {
return testArr.indexOf($(this).attr('data-prodCode')) >-1;
}).doSomething();
Without a return all items will be excluded
I would use a Set for constant-time lookup.
Be aware that jQuery reads the attribute value "58" as a number when using the data method, so it won't match unless you make sure the data type is the same:
// Use a set
var testSet = new Set(["40", "A1", "B9", "58"]);
var texts = $("div.prodCodes").filter(function() {
var x = $(this).data("prodCode").toString(); // data type must match
// Return a boolean to indicate whether the div element should be kept
return testSet.has(x); // Set#has() is fast
}).map(function(){
// For demo only: get the text content of the matching div elements
return $(this).text();
}).get(); // convert that to a plain array
console.log(texts);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="prodCodes" data-prod-code="A1">Hello</div>
<div class="prodCodes" data-prod-code="XX">Not this one</div>
<div class="prodCodes" data-prod-code="58">There</div>

Categories