Cannot append comparison value to DOM using javascript - javascript

The following function checks a form submission value against a data set. Currently, I am able to return a match using console.log. My question is, given that the function is working correctly in terms of comparison, how can I append the result to the body of my page? I attempted it with the following but could not get it to work:
function lookForMatches(){
const slugName = `${slugData.slug()}`;
for (var i = 0; i < globalArticles.length; i++) {
if(slugName === globalArticles[i]["slug"]){
const showMatches = document.createElement('div')
showMatches.innerHTML(`<p>${globalArticles[i]["slug"]}<p>`);
document.getElementById("slugResults").appendChild(showMatches);
}
else {
console.log("No Matches")
}
}
}
Any help would be much appreciated!

As others have mentioned, innerHTML is a property, not a function:
The Element property innerHTML gets or sets the HTML or XML markup contained within the element.
const content = element.innerHTML;
element.innerHTML = htmlString;
In your case, your code should look like this:
function lookForMatches() {
const slugName = `${slugData.slug()}`;
for (var i = 0; i < globalArticles.length; i++) {
if (slugName === globalArticles[i]["slug"]) {
const showMatches = document.createElement("div");
// Set's the inner HTML
showMatches.innerHTML = `<p>${globalArticles[i]["slug"]}</p>`;
document.getElementById("slugResults").appendChild(showMatches);
} else {
console.log("No Matches");
}
}
}

Related

Iterating over a nodelist, getting the length of a value of a node item

I am trying to dynamically grab ZIPcodes and validate them when the length is 5.
I used querySelectorAll to grab the Zipcode fields on the page, as well as a few other fields I will use after validating.
I iterate over the nodelist and pass it to another function, where the eventlistener kicks off if the value is the correct length.
function GetZipCodeDetails() {
var zipId = document.querySelectorAll("[id*='ZipCode']");
var countyId = document.querySelectorAll("[id*='CountyId']");
var stateId = document.querySelectorAll("[id*='StateId']");
var phoneId = document.querySelectorAll("[id*='PhoneNumber']");
for (var i = 0; i < zipId.length; i++) {
if (zipId[i].length = 5)
AssortedZipCodeFunctions(zipId[i], countyId[i], stateId[i], phoneId[i]);
}
}
function AssortedZipCodeFunctions(zipId, countyId, stateId, phoneId) {
//Runs auto-county/state function only when zipcode field is completed
document.addEventListener("keyup", (e) => {
if (zipId.value.length == 5) {
GetCountyAndStateFromIds(zipId, countyId, stateId, phoneId);
} });
}
The code works perfectly for me as it is listed above; I am just trying to move the second function into the first function, but I can't figure out how. I am just stuck on how come I can't do the following:
function GetZipCodeDetails() {
var zipId = document.querySelectorAll("[id*='ZipCode']");
var countyId = document.querySelectorAll("[id*='CountyId']");
var stateId = document.querySelectorAll("[id*='StateId']");
var phoneId = document.querySelectorAll("[id*='PhoneNumber']");
for (var i = 0; i < zipId.length; i++) {
document.addEventListener("keyup", (e) => {
if (zipId[i].value.length == 5) {
GetCountyAndStateFromIds(zipId[i], countyId[i], stateId[i], phoneId[i]);
}
});
}
}
The above gives: "TypeError: Cannot read property 'value' of undefined
at HTMLDocument."
I have figured out that the for loop is calling the second function, instead of waiting until the Zipcode value is 5... so all that happened is I passed it to another function? Or maybe I am stuck on how to get the length of the value of a node item? Please help.
In your event listener you are adding it to the document instead of each element separately
for (var i = 0; i < zipId.length; i++) {
zipId[I].addEventListener("keyup", (e) => {
if (zipId[i].value.length == 5) {
GetCountyAndStateFromIds(zipId[i], countyId[i], stateId[i], phoneId[i]);
}
});
}

getElementById check multiple conditions

I am trying to build a function that checks if all fields are populated, if populated then show div if not hide.
I can get this to work on one fields however i have then tried two ways of checking multiple.
first
if first condition met I then ran other condition checking second field nested inside the first... this done not work.
second
I passed in an array of ID's rather than a single... this did not work either..
I am left with a working function that only works if first filed is populated can anyone think of a solution to this or maybe i passed in my array incorrectly.
My code
var myVar = setInterval(myTimer, 10);
function myTimer() {
if(!document.getElementById('Email').value) { // I need this to pass if multiple id's
var divsToHide = document.getElementsByClassName("somediv"); //divsToHide is an array
for(var i = 0; i < divsToHide.length; i++){
divsToHide[i].style.visibility = "hidden"; // or
divsToHide[i].style.display = "none"; // depending on what you're doing
}
}
else {
var divsToHide = document.getElementsByClassName("somediv"); //divsToHide is an array
for(var i = 0; i < divsToHide.length; i++){
divsToHide[i].style.visibility = "visible"; // or
divsToHide[i].style.display = "block"; // depending on what you're doing
}
}
}
Make it so your function takes an argument of the element ID and the class Name you need to check for.
Also, never use .getElementsByClassName() (read here for why). Instead, use .querySelectorAll().
And, you can use the modern .forEach() API of arrays and node lists (not in IE though), which is simpler than managing traditional for loops with indexes.
Lastly, use pre-made CSS classes instead of inline styling.
// You just need to pass the ID and Class to the following line
var myVar = setInterval(function(){ myTimer([id here],[class here]) }, 10);
function myTimer(id, class) {
// Set up these references just once and the rest of the code
// will be easier to read
var elem = document.getElementById(id);
var divsToHide = document.querySelectorAll("." + class);
// Instead of negative logic, reverse the if branches
if(elem.value) {
divsToHide.forEach(function(){
this.classList.remove("hidden"); // Much simpler than inline styling
});
} else {
divsToHide.forEach(function(){
this.classList.add("hidden");
});
}
/* Use pre-made CSS classes instead of inline styling */
.hidden { display:none; }
If you have an array of the IDs such as
let idArray = ['foo', 'bar', 'baz']
You can iterate through an array using a for loop
for (i = 0; i > idArray.length; i++) {
if (!document.getElementById(idArray[i]).value) {
// your hide logic
} else {
// your show logic
}
}
You can create a const of all elements that need to validate. For example,
const elementIdsToBeValidated = ['name', 'email'];
You can also create validator functions that returns true and false based on input,
const nameValidator = (val) => !!val;
const emailValidator = (email) => !!email;
const validators = [nameValidator, emailValidator];
Then you can run your timer function,
var myVar = setInterval(myTimer(['name', 'email']), 10);
function myTimer(ids) {
ids.forEach(id => {
const el = document.getElementById(id);
const val = el.value;
const divEl = document.getElementById('error');
const valid = validators.reduce((acc, validator) => validator(val), false);
if(valid) {
divEl.style.display = 'none';
} else {
divEl.style.display = 'block';
}
});
}
You can look at this stackBlitz example,
https://stackblitz.com/edit/js-ie7ljf

Pull specific field from nth index of Knockout observableArray of objects

I have a page that displays a list of file templates built using the following method.
var loadCustomTemplate = function () {
loadBaseTemplate();
var res = 0;
for (i = 0; i < self.GetSeam().length; i++) {
var a = self.count() + 1;
self.count(a);
res = self.GetSeam()[i].FileFormat.split("_");
if (res.length == 4) {
var ap = res[3].split('.');
self.append(ap[0]);
} else {
self.append("");
}
var obj = {
Code: ko.observable(self.code()),
Number: ko.observable(self.number()),
SeamReportPath: ko.observable(self.reportPath()),
FileFormat: ko.observable(self.append()),
SequenceNumber: ko.observable(a)
}
self.CustomTemplate.push(obj);
}
self.count(0);
};
The user is then allowed to edit the fields as needed. They can also add records or remove them as needed. The method to add a record is as follows.
self.addTemplate = function () {
var count = self.CustomTemplate().length + 1;
var obj = {
Code: ko.observable(self.code()),
Number: ko.observable(self.number()),
SeamReportPath: ko.observable(self.reportPath()),
FileFormat: ko.observable(""),
SequenceNumber: ko.observable(count)
}
self.CustomTemplate.push(obj)
};
Once those updates are made they can save the updated CustomTemplate. This uses ajax that is not important to this question. The save method calls a validation method that is supposed to check to make sure there are no duplicate FileFormat fields in the object array. This is what I have, but it is failing.
var validateTemplates = function() {
for (i = 0; i < self.CustomTemplate().length; i++) {
var checkVal = self.CustomTemplate()[i].FileFormat;
var checkSeq = self.CustomTemplate()[i].SequenceNumber;
for (j = 0; j < self.CustomTemplate().length; j++) {
if (checkSeq !== self.CustomTemplate()[j].SequenceNumber ){
if (checkVal+"" === self.CustomTemplate()[j].FileFormat) {
if (checkSeq == self.CustomTemplate()[j].SequenceNumber ){
return false;
}
}
}
}
return true;
};
The problem is that when checking self.CustomTemplate()[i].FileFormat and self.CustomTemplate()[i].SequenceNumber it isn't reflecting the data displaying on the page or the data being sent to the controller (MVC 4). If I put either of those in an alert it is showing a function. How do I access the data in those specific fields for comparison?
Thanks in advance.
If I put either of those in an alert it is showing a function.
That's because you're doing this kind of thing:
var checkVal = self.CustomTemplate()[i].FileFormat;
FileFormat is the result of ko.observable(...), which returns a function, so checkVal in fact does contain a function.
The solution is for all those cases to do this:
var checkVal = self.CustomTemplate()[i].FileFormat(); // Parentheses at the end!
The parentheses execute the observable function, and if you do so without parameters you "get" the value of that observable. (If you would pass in a value, it would "set" the observable to that value.)

How do I loop a check for string in a document in Javascript?

I'm trying to make a code that will search for a specific text, and if it is found it will click a button. It needs to check for the string continuously, however I am struggling to find a way for that to happen. I'm a complete newb to coding, so any help is appreciated! :)
var findMe = [
//Test
'Hello!',
];
function findText() {
var text = document.querySelector('div[id=BtnText]');
for (var i = 0; i < findMe.length; i++) {
if (BtnText.match(findMe[i])) {
var btnDo = document.querySelector('input[type="submit"][value="Click!"]');
if (btnDo) {
btnDo.click();
}
}
}
}
Just editing your code a little bit.
I am assuming you have HTML like this?
<div id="BtnText">Hello!</div><input type="submit" value="Click!">
You will to change your code to this
var findMe = [
//Test
'Hello!',
];
function findText() {
var div = document.querySelector('div[id=BtnText]');
for (var i = 0; i < findMe.length; i++) {
if (div.innerText.indexOf(findMe[i]) !== -1) {
var btnDo = document.querySelector('input[type="submit"][value="Click!"]');
if (btnDo) {
if (typeof btnDo.onclick == "function") {
btnDo.onclick.apply(elem);
}
}
return true;
}
}
return false;
}
If you want to check continuously. I recommend using setInterval.
var interval = setInterval(function() {
var textFound = findText();
if(textFound) {
clearInterval(interval);
}
},50);
Regular expression:
(new RegExp('word')).test(str)
(new RegExp(/word/)).test(str)
indexOf:
str.indexOf('word') !== -1
search()
searches a string for a specified value, or regular expression, and returns the position of the match.
var n=str.search("word");
or
var n-str.search(/word/);
if(n>0)
{}
with window.find()
if (window.find("word", true)){}
//code
while(window.find("word",true){
//code
}
Why do you need to perform the check continously?
You should get another approach... Or your script will be blocked by Chrome, for example, if it makes the page non responsible. You can go for a timeout, as Taylor Hakes suggested... Or just call your findText function attached to the onChange event on the div.

JavaScript closures and variable scope

I am having trouble with JS closures:
// arg: an array of strings. each string is a mentioned user.
// fills in the list of mentioned users. Click on a mentioned user's name causes the page to load that user's info.
function fillInMentioned(mentions) {
var mentionList = document.getElementById("mention-list");
mentionList.innerHTML = "";
for (var i = 0; i < mentions.length; i++) {
var newAnchor = document.createElement("a");
// cause the page to load info for this screen name
newAnchor.onclick = function () { loadUsernameInfo(mentions[i]) };
// give this anchor the necessary content
newAnchor.innerHTML = mentions[i];
var newListItem = document.createElement("li");
newListItem.appendChild(newAnchor);
mentionList.appendChild(newListItem);
}
document.getElementById("mentions").setAttribute("class", ""); // unhide. hacky hack hack.
}
Unfortunately, clicking on one of these anchor tags results in a call like this:
loadUserNameInfo(undefined);
Why is this? My goal is an anchor like this:
<a onclick="loadUserNameInfo(someguy)">someguy</a>
How can I produce this?
Update This works:
newAnchor.onclick = function () { loadUsernameInfo(this.innerHTML) };
newAnchor.innerHTML = mentions[i];
The "i" reference inside the closure for the onclick handlers is trapping a live reference to "i". It gets updated for every loop, which affects all the closures created so far as well. When your while loop ends, "i" is just past the end of the mentions array, so mentions[i] == undefined for all of them.
Do this:
newAnchor.onclick = (function(idx) {
return function () { loadUsernameInfo(mentions[idx]) };
})(i);
to force the "i" to lock into a value idx inside the closure.
Your iterator i is stored as a reference, not as a value and so, as it is changed outside the closure, all the references to it are changing.
try this
function fillInMentioned(mentions) {
var mentionList = document.getElementById("mention-list");
mentionList.innerHTML = "";
for (var i = 0; i < mentions.length; i++) {
var newAnchor = document.createElement("a");
// Set the index as a property of the object
newAnchor.idx = i;
newAnchor.onclick = function () {
// Now use the property of the current object
loadUsernameInfo(mentions[this.idx])
};
// give this anchor the necessary content
newAnchor.innerHTML = mentions[i];
var newListItem = document.createElement("li");
newListItem.appendChild(newAnchor);
mentionList.appendChild(newListItem);
}
document.getElementById("mentions").setAttribute("class", "");
}

Categories