My code isnt working and I am getting an "Uncaught TypeError: Cannot read property 'getElementByTagName' of null at HTMLButtonElement.countInParagraphs at line 76.
can you help me figure out my problem.
Im thinking new eyes can figure out the problem better than I can.
Thank you
<!DOCTYPE html>
<html>
<head>
<title>Word Count Version 2</title>
<style>
button{
margin: 1em;
}
#leftArea{
width: 35%;
float: left;
height: 500px;
background-color: #C200AF;
padding: 10px;
}
#rightArea{
width: 30%;
float: left;
height: 500px;
background-color: #C2AF00;
padding: 10px;
}
#midArea{
width: 30%;
float: left;
height: 500px;
background-color: #00C2AF;
padding: 10px;
}
h3{
margin-left: 10px;
}
#paragraphs > p{
background-color: #66ffff;
}
</style>
<script>
"use strict;"
function buildParagraphs()
{
var sentenceArea = document.getElementById("sentence");
var words = sentenceArea.value;
if(words.length == 0)
{
return;
}
var wordsArray = words.split(" ");
//Get the div element with id = paragraphs and assign it to a variable called midDiv
var midDiv = document.getElementById("paragraphs");
for(var i = 0; i < wordsArray.length; i++)
{
//Build the p elements
//create a new p element and store it in a variable called newPara
var newPara = document.createElement("p");
//create a new text node from the current item in the wordsArray and store it in a variable called textNode
var textNode = document.createTextNode(wordsArray[i]);
//Append the text node to the newPara you just created
newPara.appendChild(textNode);
//Append the newPara element to the midDiv element
midDiv.appendChild(newPara);
}
sentenceArea.value = "";
}
function countInParagraphs()
{
var textArea = document.getElementById("searchWord");
var searchWord = textArea.value;
//Get the div element with id = paragraphs and assign it to a variable called midDiv
var midDiv = document.getElementById("wordList");
//Declare a variable called paraElements and assign all of the paragraph (p) tags from the midDiv element to it.
---> LINE 76 var paraElements = midDiv.getElementsByTagName("p");
var count = 0;
//Start a for-loop that sets a variable i to 0; checks it against the length of the paraElements array; and increments i by 1
for (var i =0; i <paraElements.length; i++)
{
//Write an if-statement that checks the innerHTML of the current paragraph element
if (searchWord == paraElements[i].innerHTML)
{
count++
}
//against the searchWord variable to see if they are the same. If they are, add 1 to the count.
}//end for
//Get the element with id = word and assign it to a variable called wordArea
var wordArea = document.getElementById("word");
//Assign the searchWord to the innerHTML of wordArea
wordArea.innerHTML = searchWord;
//Get the element with id = wordCount and assign it to a variable called countArea
var countArea = document.getElementById("wordCount");
//Assign the count to the innerHTML of countArea
count.innerHTML = countArea;
textArea.value = "";
}
window.onload = function()
{
var buildBtn = document.getElementById("buildBtn");
buildBtn.onclick = buildParagraphs;
var countBtn = document.getElementById("countBtn");
countBtn.onclick = countInParagraphs;
}
</script>
</head>
<body>
<div id = "leftArea">
<div>
Enter a sentence:
<textarea id = "sentence" rows = "10" cols = "30"></textarea>
</div>
<div>
<button type="button" id = "buildBtn">Build paragraphs</button>
</div>
<div>
Enter a word to find in the paragraphs:
<input type="text" id = "searchWord">
</div>
<div>
<button type="button" id = "countBtn">Count in paragraphs</button>
</div>
</div>
<div id = "midArea">
<h3>Word list</h3>
<div id = "paragraphs">
</div>
</div>
<div id = "rightArea">
The number of times the word '<span id = "word"></span>' turns up in the sentence is: <span id = "wordCount"></span>
</div>
</body>
Try moving your script to the end of your HTML document, right before </body> so that it looks like so: <script>...</script></body>.
I believe null means the element does not exist yet in the DOM, as opposed to undefined which means it's in the DOM, so it can be found, but is undefined.
If your script executes before the rest of your HTML document loads, you'll usually end up getting null and perhaps sometimes undefined errors.
As there is no element with id 'wordList', this throws an error. But there is an element with id 'word'. So you can replace id to var midDiv = document.getElementById("word"); in line number 74.
First of all you don't have the element with id == wordList at the page. But try to change string 76 to the code:
var paraElements = [];
if (midDiv) {
paraElements = midDiv.getElementsByTagName("p");
}
Related
I have many comma separated strings, each of which consists of a list of tags, and I want to style each tag inside a box (see here).
I converted each comma separated string ("p") into an array, then wrapped <span> tags around each value in the array, so I could style it with CSS, which worked great.
But whitespace strings are also getting wrapped in span tags which I do not want, I want to ignore those (or hide them).
How do I ignore those occurrences of "p" which contain only whitespace? The answers here and here but didn't work for me.
HTML:
<p>Skill 1, Skill 2, Skill 3</p>
<p>Skill 1</p>
<p> </p>
Javascript:
$("p").each(function() {
var words = $(this).text().split(", ");
var total = words.length;
$(this).empty();
for (index = 0; index < total; index++) {
$(this).append($("<span class = 'tag' > ").text(words[index]));
}
})
CSS:
.tag {
background-color: lightgray;
padding: 3px;
margin: 3px;
border-radius: 3px;
}
JS Fiddle
Just check to see if the trimmed text is truthy first. Also make sure not to implicitly create global variables, always declare variables with const (or let or var) before using them, otherwise errors will be thrown in strict mode:
if (words[index].trim()) {
$(this).append($("<span class = 'tag' > ").text(words[index]));
}
// Converts comma separated string into tags
function convertToTags(s) {
$(s).each(function() {
var words = $(this).text().split(", ");
var total = words.length;
$(this).empty();
for (let index = 0; index < total; index++) {
if (words[index].trim()) {
$(this).append($("<span class = 'tag' > ").text(words[index]));
}
}
})
}
// Calls the function on document ready
$(document).ready(function() {
convertToTags("p");
});
.tag {
background-color: lightgray;
padding: 3px;
margin: 3px;
border-radius: 3px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>This, is, a, test</p>
<p>This</p>
<p> </p>
You need to apply your function only to the relevant elements.
In the following example I've used this condition:
$(this).text().trim().length > 0
$("p")
.each(function() {
const text = $(this).text().trim();
// This condition will make sure that "empty" p elements won't be affected
if (text.length > 0) {
var words = $(this).text().split(", ");
var total = words.length;
$(this).empty();
for (index = 0; index < total; index++) {
$(this).append($("<span class = 'tag' > ").text(words[index]));
}
}
})
.tag {
background-color: lightgray;
padding: 3px;
margin: 3px;
border-radius: 3px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Skill 1, Skill 2, Skill 3 </p>
<p>Skill 1</p>
<p> </p>
function convertToTags(s) {
$("p").each(function() {
var text = $(this).text();
if(!text.replace(/ /g, '')){
$(this).remove();
return;
}
var words = text.split(", ");
var total = words.length;
$(this).empty();
for (index = 0; index < total; index++) {
$(this).append($("<span class = 'tag' > ").text(words[index]));
}
})
}
Magic lies in the first 2 statements within .each function. Before doing the split, we will check if there is anything else in this paragraph other than whitespace(s).
If not, remove this paragraph and start the next iteration.
What I am trying to accomplish is have the user click button one and a text field is created, this button is pushed 3 times and 3 text fields appear. When each text field appears it should the user should then enter text in each text field. Once all text fields are filled by the user, there is a second button that when clicked; should display and sort the manually entered input fields text in a bonafide node list by alphabetical order.
(NOT AN ARRAY) it must be a true nodelist. Keep in mind, each input field is being created upon the push of button #1. Then the user entered information is being displayed and sorted when pushing button #2. A for-loop should be used to retrieve value of each element of the nodelistand store each value into an element of the new listItemValues array.
Appreciate any help.
javascript:
var $ = function (id) {
return document.getElementById(id)
}
var adding = function() {
var newInput = document.createElement("input");
var newBreak = document.createElement("br");
var myparent = $("todolist");
newInput.setAttribute("title", "text");
newInput.setAttribute("class", "listitem");
myparent.appendChild(newInput);
myparent.appendChild(newBreak);
};
var sorting = function() {
var display = "";
var listItemValues = document.getElementsByTagName("input");
for (i = 1; i <= listItemValues.length; i++)
var myItem = $("additem") + i;
var myItemName = (myItem).value;
display += myItemName;
}
window.onload = function() {
$("additem").onclick = adding;
$("sortitems").onclick = sorting;
}
I have made some changes to your code to make it a completely a javascriptsolution.
To reduce the use of the repetitive syntax of document.getElementById and document.createElement. I have 2 Function Declarations:
function id(id) {
return document.getElementById(id);
}
function ce(el) {
return document.createElement(el);
}
Other change is in the Function Expression adding() where I've added: newInput.type = "text"; to setting the input type when you click in the Add Item button.
In the Function Expression sorting() I've declared:
nodeList = document.querySelectorAll("input[type=text]");
The document.querySelectorAll() method returns a list of the
elements within the document (using depth-first pre-order traversal of
the document's nodes) that match the specified group of selectors. The
object returned is a NodeList.
Finally I've made a Function Expression printSortedValues() to print the sorted values in <p id="displayitems"></p>. In this function use the Array.prototype.sort() to sort its values ascending.
var printSortedValues = function(listItemValues) {
listItemValues.sort(); // Sorting the values.
var html = "", i, len = listItemValues.length;
for (i = 0; i < len; i++) {
html += "<span>";
html += listItemValues[i];
html += "</span>";
}
return html; // Return the html content with the sorted values.
};
Something like this:
function id(id) {
return document.getElementById(id);
}
function ce(el) {
return document.createElement(el);
}
var adding = function() {
var newInput = ce("input"), newBreak = ce("br"), myparent = id("todolist");
newInput.setAttribute("title", "Some title...");
newInput.setAttribute("class", "listitem");
newInput.type = "text";
myparent.appendChild(newInput);
myparent.appendChild(newBreak);
};
var sorting = function() {
var listItemValues = [], nodeList = document.querySelectorAll("input[type=text]"), i, len = nodeList.length, node;
for (i = 0; i < len; i++) {
node = nodeList[i];
listItemValues.push(node.value); // Store its values.
}
id("displayitems").innerHTML = printSortedValues(listItemValues);
};
var printSortedValues = function(listItemValues) {
listItemValues.sort(); // Sorting the values.
var html = "", i, len = listItemValues.length;
for (i = 0; i < len; i++) {
html += "<span>";
html += listItemValues[i];
html += "</span>";
}
return html; // Return the html content with the sorted values.
};
window.onload = function() {
var additem = id("additem"), sortitems = id("sortitems");
additem.onclick = adding;
sortitems.onclick = sorting;
};
#displayitems span {
border: solid 1px #ccc;
border-radius: 5px;
display: block;
margin: 2px 0;
padding: 4px;
}
<body>
<h1>ToDo List - Date: <span id="today"> </span></h1>
<div id="todolist">
<p>
<input type="button" id="additem" value="Add Item">
</p>
</div>
<hr>
<div>
<p>
<input type="button" id="sortitems" value="Sort and Display Items">
</p>
<p id="displayitems"></p>
</div>
</body>
Hope this helps.
Fairly new to Javascript and trying a more complex script (to me, anyway).
The end-game for this script is the following:
Element tagged with onclick=(runscript) will prompt script to load a text file of site names/links, separated by commas.
Script loads text file into an array.
First loop iterates through array, separating the main array into two new arrays based on even/odd array position. (ex. Site name is 0, link is 1, name is 2, link is 3, etc.)
Second loop iterates through the site name array, creating a new div element for each value in the array.
Second loop also creates new anchor element, appending it to the div element.
Second loop sets div's class, anchor's href, and appends the new div to a container div.
I feel like I'm making a few noob mistakes and my lack of exposure to Javascript is keeping me from seeing them. I cut out the first two steps to test steps 3-6 instead.
This is what I've managed to come up with so far.. any nudge in the right direction would be awesome.
Thanks!
var main = ["Google", "http://google.com", 'Gmail', 'http://gmail.com', 'Hotmail', 'http://hotmail.com', 'Battle.net', 'http://battle.net', 'Steam', 'http://steampowered.com'];
function getSites() {
var site = new Array();
var link = new Array();
for (var i = 0; i <= main.length; i++) {
if (i % 2 == 0) {
link.push(main[i]);
} else {
site.push(main[i]);
}
}
for ($i = 1; i <= site.length; i++) {
var divElement = document.createElement("div");
var anchorElement = document.createElement("a");
divElement.appendChild(anchorElement);
divElement.className = "boxin";
anchorElement.href = link[i];
divElement.innerHTML = (site[i]);
linkContainer.appendChild(divElement);
}
}
getSites();
boxin {
height: 20px;
background-color: green;
}
#linkContainer div
{
border:solid 1px black;
margin:5px;
}
<div id="linkContainer"></div>
You have a bug in the second iteration, you are initializing a new variable $i, but the condition is checking for i<=site.length which will be false since the value of i is updated to main.length at the end of the first loop
But you really don't need to use 2 loop to solve the problem, you can use a single loop as below. Also you need to set the label text as the content of anchor element not of the div
var main = ["Google", "http://google.com", 'Gmail', 'http://gmail.com', 'Hotmail', 'http://hotmail.com', 'Battle.net', 'http://battle.net', 'Steam', 'http://steampowered.com'];
function getSites() {
for (var i = 0; i < main.length; i += 2) {
var divElement = document.createElement("div");
var anchorElement = document.createElement("a");
divElement.appendChild(anchorElement);
divElement.className = "boxin";
anchorElement.href = main[i + 1];
anchorElement.innerHTML = (main[i]);
linkContainer.appendChild(divElement);
}
}
getSites();
boxin {
height: 20px;
background-color: green;
}
#linkContainer div {
border: solid 1px black;
margin: 5px;
}
<div id="linkContainer"></div>
Do you really need them to be in an array? I think an object would work much better.
var sites = {
"Google": "http://google.co.uk",
"Gmail": "http://gmail.com"
};
Then you could loop through the object and achieve what you wanted.
Try it : i'm change your code little bit... below code 100% workable...
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="../js/jquery-1.7.1.min.js"></script>
<style type="text/css">
boxin {
height: 20px;
background-color: green;
}
#linkContainer div {
border: solid 1px black;
margin: 5px;
}
</style>
</head>
<body>
<div>
<div id="linkContainer"></div>
</div>
<script type="text/javascript">
var main = ["Google", "http://google.com", 'Gmail', 'http://gmail.com', 'Hotmail', 'http://hotmail.com', 'Battle.net', 'http://battle.net', 'Steam', 'http://steampowered.com'];
function getSites() {
var site = new Array();
var link = new Array();
for (var i = 0; i <= main.length; i++) {
if (i % 2 == 0) {
link.push(main[i]);
} else {
site.push(main[i]);
}
}
for (var i = 0; i < site.length; i++) {
var divElement = document.createElement("div");
var anchorElement = document.createElement("a");
divElement.appendChild(anchorElement);
divElement.className = "boxin";
anchorElement.href = link[i];
divElement.innerHTML = (site[i]);
document.getElementById("linkContainer").appendChild(divElement);
}
}
getSites();
</script>
</body>
</html>
I am trying to use JavaScript to update the CSS layout as the webpage loads. My code looks like so:
var container = 0; // Add Total UI
var containerTitle = 0; // Container Title
var article = 0;
var articleTitle = 0;
var divName = 0; // temp variable for article id names
var divNameT = 0; // temp variable for title id names
function setLayout(id) {
container = document.getElementById(id);
for(var x = 0; x < 18; ++x) {
// CREATE CONTAINER FOR ALL PANELS
divName = "articleCon"+ x;
article = document.createElement('div');
article.id = divName;
// SETUP CSS STYLE
article.style.cssText = 'height: 205px; width: 300px; background: red; margin-right: 20px; margin-bottom: 20px; display: block; float: left;';
setNewsTitle(count,divName); // Function Call to set Title Panel
container.appendChild(article);
}
}
function setNewsTitle(count,id) {
containerTitle = document.getElementById(id);
// CREATE CONTAINER FOR TITLE
divNameT = "articleTitle"+ count;
articleTitle = document.createElement('div');
articleTitle.id = divNameT;
// SETUP CSS STYLE
articleTitle.style.cssText = 'position: absolute; height: 45px; width: 100px; background: yellow; display: inline;';
containerTitle.appendChild(articleTitle);
}
When I compile my code without making the call to function setNewsTitle(count,id) all the CSS elements are working fine.
The issue I am facing here is whenever the function call is made, my page appears blank. Nothing displays on the screen.
I tried adding screenshots for better understanding, but i don't have the reputation yet.
Try ...
container.appendChild(article);
setNewsTitle(x,divName); // Function Call to set Title Panel
The article needs to be in place before setNewsTitle is run, since you are looking for the element by id. Also, you do not have count, you have x ...
jsFiddle: http://jsfiddle.net/rfornal/o1wyae74/
Try this, append child in DOM before call funtion setNewsTitle, replace count with x :
var container = 0; // Add Total UI
var containerTitle = 0; // Container Title
var article = 0;
var articleTitle = 0;
var divName = 0; // temp variable for article id names
var divNameT = 0; // temp variable for title id names
function setLayout(id) {
container = document.getElementById(id);
for(var x = 0; x < 18; ++x) {
// CREATE CONTAINER FOR ALL PANELS
divName = "articleCon"+ x;
article = document.createElement('div');
article.id = divName;
// SETUP CSS STYLE
article.style.cssText = 'height: 205px; width: 300px; background: red; margin-right: 20px; margin-bottom: 20px; display: block; float: left;';
container.appendChild(article);
setNewsTitle(x,divName); // Function Call to set Title Panel
}
}
function setNewsTitle(count,id) {
containerTitle = document.getElementById(id);
// CREATE CONTAINER FOR TITLE
divNameT = "articleTitle"+ count;
articleTitle = document.createElement('div');
articleTitle.id = divNameT;
// SETUP CSS STYLE
articleTitle.style.cssText = 'position: absolute; height: 45px; width: 100px; background: yellow; display: inline;';
containerTitle.appendChild(articleTitle);
}
You have 2 issues in your code:
You have not actually added the element to the DOM yet, so when you attempt document.getElementById in your function setNewsTitle() - it won't find anything.
You have an error in the method call to setNewsTitle(count,id). You are passing "count", but count doesn't exist. You need to call it as setNewsTitle(x, divName) but only AFTER you have made the call to container.appendChild(article).
The setLayout function would end up something like this:
function setLayout(id) {
container = document.getElementById(id);
for(var x = 0; x < 18; ++x) {
// CREATE CONTAINER FOR ALL PANELS
divName = "articleCon"+ x;
article = document.createElement('div');
article.id = divName;
// SETUP CSS STYLE
article.style.cssText = 'height: 205px; width: 300px; background: red; margin-right: 20px; margin-bottom: 20px; display: block; float: left;';
// Add it to the DOM first
container.appendChild(article);
// need to pass "X", not count
setNewsTitle(x,divName); // Function Call to set Title Panel
}
}
I am making a tic-tac-toe game in which I have a series of 9 buttons. I change the background image to an X or an O based on the boolean value of variable isX.
I have a div with an id stage which holds all the buttons of class square. I added a listener on the stage and passed it an event parameter. However, the function clickHandler is not recognized. Chrome says:
Uncaught ReferenceError: clickHandler is not defined 2Players.html:38
(anonymous function)
HTML:
<body>
<div id="stage">
</div>
</body>
CSS:
#stage{
width: 400px;
height: 400px;
padding: 0px;
position: relative;
}
.square{
width: 64px;
height: 64px;
background-color: gray;
position: absolute;
}
JavaScript:
const ROWS = 3;
const COLS = 3;
const GAP = 10;
const SIZE = 64;
var stage = document.querySelector("#stage");
var lotOfButtons = [];
var winningPatterns = ["123","159","147",
"258","357","369",
"456","789"];
var isX = true;
var player1Pattern = "";
var player2Pattern = "";
stage.addEventHandler("click",clickHandler,false);
prepareBoard();
function prepareBoard(){
for(var row = 0;row<ROWS;row++){
for(var col = 0;col < COLS;col++){
var square = document.createElement("button");
square.setAttribute("class","square");
stage.appendChild(square);
lotOfButtons.push(square);
square.style.left = col * (SIZE+GAP) + "px";
square.style.top = row * (SIZE+GAP) + "px";
}
}
function clickHandler(event){
if(isX===true){
event.target.style.backgroundImage = "url(../img/X.PNG)";
isX = false;
}else{
event.target.style.backgroundImage = "url(../img/O.PNG)";
isX = true;
}
}
}
You have some syntax and semantic errors in the code -- possibly more than I caught, but it is in a working state.
You only closed one of the braces in the nested for loops in prepareBoard. This leads to clickHandler being defined inside prepareBoard rather than on window.
It's addEventListener, not addEventHandler.
http://jsfiddle.net/4pgQ8/