Cannot read attribute of null using array - javascript

I am trying to change the the classes of element that already exist but are hidden . This problem is whenever the code is executed a null exception is thrown. Tried everything but can't seem to get what's wrong
function changeMenu(code) {
var i, id = ["'demo'", "'drill'", "'alert'"];
for (i = 0; i <= 2; i += 1) {
if (document.getElementById(id[i]) !== null) {
if (i !== code) {
document.getElementById(id[i]).setAttribute('class', 'row hidden');
} else {
document.getElementById(id[i]).setAttribute('class', 'row');
}
} else {window.alert("error");}
}
}
The script is already loaded in the end of document and the id also exists.

The problem here is that you are storing the ids with the ' in your strings, so when you will call document.getElementById("'demo'") it won't find the element and return null.
Change your id array declaration to:
var i, id = ["demo", "drill", "alert"];
The script is already loaded in the end of document and the id also exists.
Yes the id exists but they don't have ' on it, just remove it from your array.

There are several issues with quotes and tests
I believe you actually meant to do this:
var ids = ["demo", "drill", "alert"];
function changeMenu(code) {
for (var i = 0; i < ids.length; i++) {
var elm = document.getElementById(id[i]);
if (elm) { // element exists
elm.classList.toggle("hidden",ids[i] !== code);
}
else {
window.alert("error");
}
}
}

Related

querySelector returns 'Failed to execute div is not a valid selector' when it exists in the DOM

I'm running into an issue where the 'div ID' isn't valid when I run it from the code.
However, when I document.querySelector('#div-gpt-ad\\/1594654184865-0>div') it returns the correct div ID.
Screenshot of error: https://gyazo.com/a7f1898f246bd84f28e85c2052ac60eb
The div id exists on page before executing the code, the console.log in the renderDiv function returns
Here is the code I'm trying to execute:
var slots = window.top.googletag.pubads().getSlots();
init(slots);
function init(slots) {
for (let i = 0; i < slots.length; i++) {
renderDiv(slots[i]);
}
}
function getSelectors(slot) {
var escapeCheck = slot.getSlotElementId();
if(escapeCheck.includes('/')){
let placeHold = escapeCheck.replace(/\//g, '\\\\/');
return "#" + placeHold + ">div" ;
} else{
return "#" + escapeCheck + ">div";
}
}
function renderDiv(slot) {
let selector = getSelectors(slot);
console.log("Selector:" + selector)
document.querySelector(selector)
}
Fixed!
Changed let placeHold = escapeCheck.replace(/\//g, '\\\\/'); to
let placeHold =escapeCheck.replace(/\//g, "\\/").replace(/\./g, "\\.");
Something I quite don't understand yet is why the first one did not work as the string was correct that was passing through

Is this the correct way to express no value in this function?

I have a accordion which has been set up to open to a specific panel by appending this string to the URL?panel=0
farfegnugen = jQuery.noConflict();
farfegnugen(document).ready(function($) {
var defaultPanel = parseInt(getParam('panel'));
farfegnugen("#st-accordion").accordion({
open: defaultPanel
});
function getParam(name) {
var query = location.search.substring(1);
if (query.length) {
var parts = query.split('&');
for (var i = 0; i < parts.length; i++) {
var pos = parts[i].indexOf('=');
if (parts[i].substring(0, pos) == name) {
return parts[i].substring(pos + 1);
}
}
} else if(query.length === undefined){
window.location.href = "#topOfPage";
}
return false;
}
});
However, I have noticed it scrolls slightly to the body of the accordion on load despite not having selected a specific panel. To fix this behavior I figured adding this else if block would check to see if the parameter was empty, if so we would force the window to scroll where that anchor is situated.
<a id="topOfPage" href='#'></a>
So wouldn't else if(query.length === undefined) work because you're saying nothing, is there?
P.S. I tried null too, and that didn't work!
Try running location.search.substring(1).length in your console and you will see what the issue is. It is equal to 0 which is not undefined. In your case, you don't need an else if, you can simply use an else

Variable scope or return issue (not sure which)

Using the script below I'm attempting to create an object called temptagarray which gets populated with all the tags on a Tumblr weblog and their frequency. So it should end up looking like this:
{'performance': 10, 'installation': 5}
I know the object is being created and it looks correct (I can print it out in each loop) but I can't figure out how to use it after/outside the function i.e. at the bottom of the script where I attempt to document.write() it out. Is this a global/local variable issue, a return issue or do I need to address it in some way?
<script type="text/javascript">
var temptagarray = {};
var tags;
var tag;
function loadPosts () {
var key = "api_key=9I4rZAYQCbU1o5TSMZuyrlvXiQsNxKBicCJxNK5OKZ6G9pgdim";
var api = "https://api.tumblr.com/v2/blog/garrettlynch.tumblr.com/";
var retrieve_more = function (offset) {
$.getJSON(api + "posts?callback=?&filter=image&limit=20&offset=" + offset + "&" + key,function(data) {
//for each item (post) in the response
$.each(data.response.posts, function(i, item) {
//pull out the posts tags
tags = item['tags'];
//loop through the tags
for (i = 0; i < tags.length; i++)
{
tag = tags[i];
//if the tag already exists in the tag array
if (temptagarray[tag])
{
temptagarray[tag] = temptagarray[tag] + 1;
}
else
{
temptagarray[tag] = 1;
}
}
});
if (data.response.posts.length == 20) {
retrieve_more(offset + 20);
}
});
};
retrieve_more(0);
}
loadPosts();
document.write(JSON.stringify(temptagarray));
</script>
Thanks in advance
Garrett
Replace this:
if (data.response.posts.length == 20) {
retrieve_more(offset + 20);
}
...with this:
if (data.response.posts.length == 20) {
retrieve_more(offset + 20);
} else {
document.write(JSON.stringify(temptagarray));
}
The problem you're having is that, despite your document.write(...) command being located below the ajax call in your code, the ajax call is asynchronous and thus the callback will be invoked asynchronously as well. Basically, document.write(...) is being invoked long before you've had a chance to interact with the temptagarray variable in the ajax callback.
First things first - AJAX is Async Asynchronous.
So the code block does not wait for the previous instruction to be completed before it executes the next line.
So your document.writeline would have already been executed by the time the response comes back.
Try printing that info in the success call back after the if block and you would indeed see the response.
thanks for the replies. Below is what I have now as a workable solution as the result is going to call another function anyway. Reading a little bit more I'm wondering if I should be using a callback - is it better?
<script type="text/javascript">
//load posts from a Tumblr weblog
function loadPosts () {
//api key and weblog address
var key = "api_key=9I4rZAYQCbU1o5TSMZuyrlvXiQsNxKBicCJxNK5OKZ6G9pgdim";
var api = "https://api.tumblr.com/v2/blog/garrettlynch.tumblr.com/";
//tags object
var temptagarray = {};
//all tags and each tag
var tags;
var tag;
//looping function to keep retrieving posts until all are retrieved
var retrieve_more = function (offset) {
$.getJSON(api + "posts?callback=?&filter=image&limit=20&offset=" + offset + "&" + key,function(data) {
//for each item (post) in the response
$.each(data.response.posts, function(i, item) {
//pull out the posts tags
tags = item['tags'];
//loop through the tags
for (i = 0; i < tags.length; i++)
{
//pull out each tag
tag = tags[i];
//if the tag already exists in the tag array
if (temptagarray[tag])
{
//add 1 to its count
temptagarray[tag] = temptagarray[tag] + 1;
}
else
{
//set its count to 1
temptagarray[tag] = 1;
}
}
//to test object as it gets added to
//$("#Posts ul").append('<li>' + JSON.stringify(item, ['tags']) + '</li>')
});
//if the number of posts is more than 20
if (data.response.posts.length == 20)
{
//retrieve the next 20
retrieve_more(offset + 20);
}
else
{
//call the show result function
showresult(temptagarray);
}
});
};
//stop retrieving posts
retrieve_more(0);
}
loadPosts();
function showresult(tagarray)
{
$("#Posts ul").append('<li>' + JSON.stringify(tagarray) + '</li>');
//document.write(JSON.stringify(tagarray));
}
</script>

how to loop through rows in an HTML table to find a specific value?

I have the following sample html: https://jsfiddle.net/pgd8e46b/1/
What I'm trying to do is let the users click on a row from the "available_widgets" table and if it's not already in the "existing_widgets" table, add it and do other things.
I have the logic to grab the appropriate table and start the loop...
var check_for_duplicates = function (row_id) {
var table = document.getElementById('existing_widgets');
var rows = table.getElementsByTagName('td');
for (var i = 0, len = rows.length; i < len; i++) {
console.log(i);
}
console.log(rows);
return true;
}
but I don't know how to compare the ID field with the id that's passed in.
I still have to write the logic that strips out the "row_" prefix from the id that's passed to the check_for_duplicates() method.
Any suggestions would be appreciated.
I think what you are looking for is the id property in the returned elements.
rows[i].id should give you what you are looking for in your loop.
to strip off the prefix: rows[i].id.replace('row_', '') That will create a new string for you to compare against.
You can simplify check_for_duplicates function if you use jQuery to find widget row by id:
$('#available_widgets').on('click', 'tr', function () {
if (check_for_duplicates(this.id)) {
return false;
} else {
alert('otherwise, do other stuff');
}
});
var check_for_duplicates = function (row_id) {
return !!$('#existing_widgets').find('#' + row_id.replace('row_', '')).length;
}
This is a working function in JS. I would also suggest using some other prefix on the existing_widgets table, perhaps existing_row_#, and then you'd just need to modify the replace() component.
function check_for_duplicates(row_id) {
var table = document.getElementById('existing_widgets');
if (document.getElementById(row_id.replace('row_', ''))) {
console.log(row_id + ' is a duplicate');
return true;
} else {
console.log(row_id + ' is not a duplicate');
return false;
}
}

Arguments in Parse.com query.find success callback

Thanks for the help in advance.
I'm working on an practice assigment using Phonegap and Javascript. Long story short: I need to use Parse.com to store information about some Lego minifigures. The problem I'm having right now is due mostly to my inexperience in Javascript.
I'm working on letting the user add tags to the figures. The user enters them, separated by comma, and I then split the string. That's working OK.
Now, I need to add the tags that don't exist yet to my database. For this, I search for any tags with that description (using query.find) and then, if it exists, I don't create it, I just modify the relationship. If it doesn't exist, I create it and then modify the relationship.
My problem is: I can't seem to be able to access the tag description (the string) from within the success callback of query.find. I'm pretty sure it's because of the scope. Is there any proper way to access variables from withing a success callback, besides the results array?
My current code is as follows:
var Figure = Parse.Object.extend("Figure");
var Tag = Parse.Object.extend("Tag");
var nombre = $('#nombre').val();
var serie = $('#serie').val();
var figure = new Figure({"Name":nombre,"Series":serie});
var tags = $('#tags').val();
res = tags.split(","); //split the
figure.save().then(function() {
for (var i = 0; i < res.length; i++) { //for each tag
var query = new Parse.Query(Tag); //create the query.
query.equalTo("Description", res[i]);
query.find( {//execute query
success: function(results, res[i]) {
if (results.length > 0){ //if there are results.
var tag = results[0]; //get the tag
var relation_tag = tag.relation("figures"); //get the relation
relation_tag.add(figure); //add figure to relation
tag.save();
}
else { //if there are no results, the tag does not exist.
new_tag = new Tag({"Description":res[i]});
//ABOVE THIS LINE: res[i] is always undefined.
var relation_tag = new_tag.relation("figures"); //get the relation
relation_tag.add(figure); //add the figure
new_tag.save();
}
},
//error with query
error: function() {
alert("ERROR");
}
});
}
}, function(error) {
alert("No se pudo guardar la figura");
});
In the success callback, res[i] always is undefined, I assume that it's because of the scope.
This is a very common problem in async Javascript programming. You are doing something like this:
for (var i = 0; i < array.length; i++) {
anAsyncFunction(function(result) { // inner function
doSomethingWith(array[i]);
}
}
The problem is that in Javascript functions store outer variables by reference and not by value, which means that a function looks up the value of a variable from an outer scope, when it is executed and not when it is defined. Since the code is async the the inner function is called after the for loop completed and at this point we have i === array.length, so array[i] === array[array.length] === undefined.
To avoid this you can use an immediately invoked function expression (IIFE, pronounced "iffy"):
for (var i = 0; i < array.length; i++) {
anAsyncFunction((function(j) { // IIFE
return function innerFunction(result) { // inner function
doSomethingWith(array[j]); // j instead of i
}
})(i); // passing "value of i"
}
Because the IIFE is invoked immediately, the current value is of i is passed and stored into j and when the inner function executes it uses the correct value.
So in your case this should work:
success: (function(j) { // IIFE
return function(results) {
if (results.length > 0) {
var tag = results[0];
var relation_tag = tag.relation("figures");
relation_tag.add(figure);
tag.save();
}
else { //if there are no results, the tag does not exist.
new_tag = new Tag({"Description":res[j]}); // j instead of i
var relation_tag = new_tag.relation("figures");
relation_tag.add(figure);
new_tag.save();
}
}
})(i) // pass "value of i"
If you prefer, you can also pass the description itself instead of just the index to the IIFE (I think I would do it that way):
success: (function(description) { // IIFE
return function(results) {
if (results.length > 0) {
var tag = results[0];
var relation_tag = tag.relation("figures");
relation_tag.add(figure);
tag.save();
}
else { //if there are no results, the tag does not exist.
new_tag = new Tag({"Description":description}); // description
var relation_tag = new_tag.relation("figures");
relation_tag.add(figure);
new_tag.save();
}
}
})(res[i]) // pass description
var Tag = Parse.Object.extend("Tag");
var query = new Parse.Query(Tag);

Categories

Resources