I am programming a text game that generates many variables randomly, like the character's name and the name of their ship, from arrays. Once the functions to generate those names is run, I use something like this in the body of the html:
<pre> The sea welcomes you back. It is finally time to achieve your destiny. Become who you were born to be.
You are <ins class="cap"></ins>, Captain of <ins id="ship"></ins>, <a id="story"></a>, and there is nothing standing between you, and fame, and fortune.
Your fearsome crew has been assembled. <ins id="crew1"></ins> is your first mate, <ins id="crew2"></ins> and <ins id="crew3"></ins> man the deck, and <ins id="crew4"></ins> is up in the crow's nest. They are a raggedy bunch but they'll see you through if the gold is right.
<a id="crew1"></a> glances back at the helm, ready to draw the anchor and set sail. You give the command and <ins id="ship"></ins> sets off into a bright, cloudless morning...</pre>
where there are these functions in the javascript to fill those in:
var captainName = function () {
var e = firstName[Math.floor(Math.random() * firstName.length)] + " " + lastName[Math.floor(Math.random() * lastName.length)];
document.getElementById("cap").innerHTML = e;
e = captainName;
};
var ship = function () {
var e = shipName[Math.floor(Math.random() * shipName.length)];
document.getElementById("ship").innerHTML = e;
e = shipName;
};
captainName();
ship();
and it will display like this:
You are Django de Bois, Captain of The Black Beauty.
However, when I want to display the name of the character again, and I use another tag in the html, it remains empty. I think it doesn't want the repeated tag to have the same id but I can't be sure. I am very new to javascript and programming in general and learning on my own, so feel free to point out something that may seen obvious.
You can't use the same ID twice. Only the first one is taken into account in JS because it's not expecting extras, and in HTML it's against the standards and is actually invalid HTML.
You need to either use different IDs for each element, and pass the ID you want as an argument to the function:
var captainName = function (id) {
var e = firstName[Math.floor(Math.random() * firstName.length)] + " " + lastName[Math.floor(Math.random() * lastName.length)];
document.getElementById(id).innerHTML = e;
e = captainName; // <-- also, what is this for?
};
Or use classes instead, and target them all at once:
var captainName = function () {
var e = firstName[Math.floor(Math.random() * firstName.length)] + " " + lastName[Math.floor(Math.random() * lastName.length)];
// you can use forEach to iterate through them
document.getElementsByClassName("cap").forEach(function(el)
el.innerHTML = e;
});
e = captainName;
};
Related
I just wanted to clear something up in my own head.
I have a Class with methods generating html. I also have a method generating an eventlistener.
I find the event listener only works once (when the button is pushed in this instance). I checked the developer tools and the event listener is removed.
it only stays and works more than once when I make the eventlistener callback recursive so the eventlistener is reapplied every callback.... like below
calculate(){ //class method
let button_html = document.getElementById("calculateBut");// probably quite leaky finding this twice
button_html.addEventListener("click", this.calculate.bind(this));// recursive binding of event listener and callback. need this because i think whats happening is the callback is being removed by garbage collection after the render_content() method is finished
}
//called in another part of the class
let button_html = document.getElementById("calculateBut"); //getting the calculate button after its creation
button_html.addEventListener("click", this.calculate.bind(this)); // binding call back function to scope of the class. not the callback function internal scope
I am fairly sure it either because of the global execution context being deleted off the call stack or something around garbage collection but please if im missing something please let me know
also if you think be should be doing thing differently, please let me know.
thanks is advance!
edit:
Full classes & main below:
Main
let toolPage = new TablePage(toolBut, homeUrl ,tool_dataUrl, tools_title);
let materialsPage = new TablePage(materialsBut,homeUrl, material_dataUrl ,materials_title);
let feedsSpeedsPg = new CalcPage(feedSpeedsBut, homeUrl, [tool_dataUrl, material_dataUrl], FS_title);
//event listeners - have url hash operations for use later
homeBut.addEventListener("click", function(){window.location = "/";});//eventlistener for clicking te home button
feedSpeedsBut.addEventListener("click", function(){ //eventlistener for clicking te F&S button
window.location.hash = feedSpeedsBut.innerHTML
feedsSpeedsPg.render_content();
});
materialsBut.addEventListener("click", () => { //eventlistener for clicking te materials button
materialsPage.render_content();
});
toolBut.addEventListener("click", () => { //eventlistener for clicking te tools button
toolPage.render_content();
});
CalcPage
generate_results(){ // create a container for results
this.content_html.innerHTML += `<div id = "results"></div>`;
}
populate_results(fr,sd){ //populate the results container
let results_html = document.getElementById("results");
results_html.innerHTML = "";
let feed_rate = `<div id = "feed_rate">${"Feed Rate = " + fr + "mm/min"}</div>`;
let stepdown = `<div id = "stepdown">${"Step Down = " + sd + "mm"}</div>`;
results_html.innerHTML = feed_rate + "\n" + stepdown;
}
generate_calculate_button(){ //generate button
let button = `<button type="button" id="calculateBut"> Calculate </button>`
return button;
}
calculate(){
let button_html = document.getElementById("calculateBut");// probably quite leaky finding this twice
button_html.addEventListener("click", this.calculate.bind(this));// recursive binding of event listener and callback. need this because i think whats happening is the callback is being removed by garbage collection after the render_content() method is finished
}
async generatePage(populate_dropdown, generate_calculate_button){
//wiping previous page
this.table_html.innerHTML = "";
this.title_html.innerHTML = this.title;
//injecting dd into html
this.content_html.innerHTML = material_dropdown + "\n" + tool_diameter_dropdown + "\n" + tool_flute_number + "\n" + RPM_dropdown + "\n" + generate_calculate_button();
this.generate_results();// generate results container after page load could follow convention of insertion as the line above but good to know eithe way works
let button_html = document.getElementById("calculateBut"); //getting the calculate button after its creation
button_html.addEventListener("click", this.calculate.bind(this)); // binding call back function to scope of the class. not the callback function internal scope
}
render_content(){
this.generatePage(this.populate_dropdown,this.generate_calculate_button);
}
}
I'm trying to write basic calculator in js (I'm learning) and so far i wrote something like this:
function Wprowadzanie(nacisnieto){
var temp = document.getElementById('kalkulator_linia_2').textContent;
temp = temp + nacisnieto;
document.getElementById('kalkulator_linia_2').innerHTML = temp;
}
function Dzialanie(nacisnieto){
var temp = document.getElementById('kalkulator_linia_2').textContent;
if(temp!="") document.getElementById('kalkulator_linia_1').innerHTML = document.getElementById('kalkulator_linia_1').textContent + ' ' + temp + ' ' + nacisnieto;
document.getElementById('kalkulator_linia_2').textContent = "";
}
function Rowna_Sie(){
var dzialanie = document.getElementById('kalkulator_linia_1').textContent + ' ' + document.getElementById('kalkulator_linia_2').textContent;
document.getElementById('kalkulator_linia_1').innerHTML = dzialanie + ' =';
var wynik = 0;
document.getElementById('kalkulator_linia_2').innerHTML = wynik;
}
Function Wprowadzanie is activated when a button (div) with number is pressed and gets the content of the button (0,1,2,3,etc..).
Example:
<div class="klawiatura_przycisk" onclick="Wprowadzanie(1)">1</div>
Same with function Dzialanie, it gets activated when button with +,-,* or / is pressed and gets content of that button (for example '+').
Example:
<div class="klawiatura_przycisk" onclick="Dzialanie('+')">+</div>
Function Rowna_Sie is activated when button with "=" is pressed.
<div class="klawiatura_przycisk" onclick="Rowna_Sie()";>=</div>
I tried to make function "Rowna_Sie()" calculate the content of var "dzialanie" and save it to var "wynik", but everything I tried didn't want to work. Could you please show me how to correctly finish that function?
You could use the eval function, which treats its argument as javascript code and tries to execute it. There are huge security concerns when you do this, but because the string is being built by buttons like that, and because this looks like it's just a project you're doing for fun, it should be fine. The code you need is this:
var wynik = eval(dzialanie);
I don't speak the language you named things in so it's a little hard to follow, and I may have made a small mistake in the snippet. The argument should be the string containing the equation the user has entered. So if they wanted to calculate 1+1, you need to do eval("1 + 1") to get the answer.
I'm working on a midterm project for my Intro to Javascript class, and I've got the required part down (an image gallery where you click a thumbnail to change the central image), but I would also like to change the text in a paragraph below the central image (not a requirement of the project, and not something we've been taught in class, but I would like to know how to do it because it would enhance the project) when the thumbnail is clicked.
I've looked at several answers related to this, and they all seem to rely on the manual population of an array, but my paragraph text is going to be rather long so I'd like to automatically populate the array with members of a class (.text), if that's possible. (Or find some other workaround.) We've learned a bit of JQuery but we're not allowed to use it for this project, which is a shame since I'm sure it would be much easier with it. Here's the relevant part of my project:
HTML:
<ul id="gallery">
<li>
<a href="images/charactername.gif" title="Character Name" id="first_link">
<img src="images/thumbs/charactername.png"></a>
<p class="text">Character Information</p>
</li>
<li>
<a href="images/charactername.gif" title="Character Name">
<img src="images/thumbs/charactername.png"/></a>
<p class="text">Character Information</p>
</li>
(several more similar blocks for different characters)
</ul>
<p><img src="images/character.gif" id="image"></p>
<h2 id="caption">Character Name</h2>
<p id="blurb">Large amount of text summarizing character's backstory/traits/etc.</p>
Javascript:
window.onload = function () {
var listNode = $("gallery");
var captionNode = $("caption");
var imageNode = $("image");
var imageLinks = listNode.getElementsByTagName("a");
var i, linkNode, image;
for ( i = 0; i < imageLinks.length; i++ ) {
linkNode = imageLinks[i];
linkNode.onclick = function (evt) {
var link = this; // link is the linkNode
imageNode.src = link.getAttribute("href");
captionNode.firstChild.nodeValue = link.getAttribute("title");
};
};
I can set the Character Name easily using the title, but changing the #blurb text below to match each character's info paragraph onclick is evading me. I feel like if I can find a way to automatically populate the array with the paragraph #text, I should be able to set the text of #blurb to array[i] or something similar, but I'm not sure. I'd vastly appreciate some help. Thank you.
I don't know if this is how you would do it in your class, but I thought I would add an example of how you might do it.
var listNode = document.getElementById('list-node');
var imageLinks = listNode.getElementsByTagName("a");
var i;
var characterText;
for (i = 0; i < imageLinks.length; i += 1) {
// Add a click event listener
imageLinks[i].addEventListener("click", function (evt) {
// Prevent the default functioning
evt.preventDefault();
// Get the character text
characterText = getCharacterText(this.parentNode);
// Update the blurb
document.getElementById('blurb').innerText = characterText;
});
}
/**
* Returns the character text of an item in list-node.
*
* #param parentNode, the list item that contains the character
* description node.
* #return characterText, the text of the character description
*/
function getCharacterText(parentNode) {
return parentNode.getElementsByClassName('text')[0].innerText;
}
JS fiddle, for what it's worth: http://jsfiddle.net/9ekcgeyq/
Sorry in advance for the extremely banal question, I'm positive I am missing something extremely simple. Without further adieu;
I'm getting a type error when I try to pull a block of text, and a button, from a div in my HTML. The div has an id that I am accurately referencing. For oversight, I am attempting to retrieve the text, apply coloring to each word (cycling through red, blue, and green), and replace the original text with my colored text. It works fine in JSFiddle, but I cannot get the data to retrieve in espresso - transcribeText is null.
var transcribeText = document.getElementById("divideTranscript");
transcribeText.onclick = function() {
var words = document.getElementById("transcriptText");
var textArray = words.innerHTML.split(" ");
var count = 0;
var colorArr = ["red", "blue", "green"];
var newWords = document.createElement("div");
for(var i = 0; i < textArray.length; i++){
var item = textArray[i];
var newSpan = document.createElement("span");
var newText = document.createTextNode(item);
var dotNode = document.createTextNode(" ");
newSpan.className = colorArr[count % 3];
newSpan.id = "word"+i;
newSpan.appendChild(newText);
newSpan.appendChild(dotNode);
count++;
};
words.parentNode.replaceChild(newWords, words);
}
<div id="transcriptText"> It’s that time of year when you clean out your
closets, dust off shelves, and spruce up your floors. Once you’ve taken
care of the dust and dirt, what about some digital cleaning? Going
through all your files and computers may seem like a daunting task, but
we found ways to make the process fairly painless.</div>
<br>
<div id="divideTranscript" class="button"> Transform the
Transcript! </div>
Your problem is that the javascript runs before the HTML exists, therefore, you cannot get any ids. There are several ways around this. First is my favorite:
window.onload = function name() {
//code to be excuted
}
The function will be called after HTML has loaded. body.onload = function name() also works.
The second method (no pun intended haha) is to put the script tag at the end of the body tag.
<body>
<script></script>
</body>
Personally, I use the first one more commonly because I have a template I religiously use, and I don't like moving tags around. That's just me, whatever works for you! Hope this Helps!
I can set up an event listener to tell me when a mouse click occurred at some place in an HTML document. But if the click occurred on some text, I need to know which character in the text the click occurred over. Is there a way to do this?
I can think of some really obnoxious solutions. For instance, for every single character in the document I could wrap it in a separate element with its own event. Or, since I can tell which textnode the click occurred in, I could perform some kind of calculation (basically almost like simulating rendering of the text) perhaps using clientWidth, to determine which character the click occurred in.
Surely there must be something easier?
Once the mouse event is captured, split the text in the element into two separate spans. Look at the offset of each span to determine which the event occurred in. Now split that span in two and compare again. Repeat until you have a span that has a single character whose coordinates contain the coordinates of the mouse click. Since this is essentially a binary search this whole process will be fairly quick, and the total number of span low compared to the alternative. Once the character has been found, the spans can be dissolved and all the text returned to the original element.
You do, unfortunately, have to wrap every character in an element, but you do not have to attach an event listener to each one. When the click event is fired on the element, it is bubbled up to its parents. You can then retrieve the element that was actually clicked by using the target property of the event.
Say we've got some text in an element named textElement. It contains a span for each character. If we wanted to be able to click on characters to delete them, we could use this code:
textElement.addEventListener('click', function(e) {
textElement.removeChild(e.target);
}, false);
Try it out.
This is my effort to implement what Michael wrote in his answer:
function hitCharBinSearch(mClientX, inmostHitEl) {
const originalInmost = inmostHitEl
const bareText = inmostHitEl.firstChild.textContent
var textNode = inmostHitEl.firstChild
var textLenghtBeforeHit = 0
do {
let textNodeR = textNode.splitText(textNode.length / 2)
let textNodeL = textNode
let spanL = document.createElement('span')
spanL.appendChild(textNodeL)
let spanR = document.createElement('span')
spanR.appendChild(textNodeR)
inmostHitEl.appendChild(spanL)
inmostHitEl.appendChild(spanR)
if (mClientX >= spanR.getBoundingClientRect().left) {
textNode = textNodeR
inmostHitEl = spanR
textLenghtBeforeHit += textNodeL.length
}
else {
textNode = textNodeL
inmostHitEl = spanL
}
} while (textNode.length > 1)
/* This is for proper caret simulation. Can be omitted */
var rect = inmostHitEl.getBoundingClientRect()
if (mClientX >= (rect.left + rect.width / 2))
textLenghtBeforeHit++
/*******************************************************/
originalInmost.innerHTML = bareText
return textLenghtBeforeHit
}
Placing each character in a document model object is not as obnoxious as it sounds. HTML parsing, DOM representation, and event handling is quite efficient in terms of memory and processing in modern browsers. A similar mechanism is used at a low level to render the characters too. To simulate what the browser does at that level takes much work.
Most documents are constructed with variable width characters
Wrapping can be justified or aligned in a number of ways
There is not a one to one mapping between characters and bytes
To be a truly internationalized and robust solution, surrogate pairs must be supported too 1
This example is lightweight, loads quickly, and is portable across common browsers. Its elegance is not immediately apparent, much reliability is gained by establishing a one to one correspondence between international characters and event listeners.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Character Click Demo</title>
<script type='text/javascript'>
var pre = "<div onclick='charClick(this, ";
var inf = ")'>";
var suf = "</div>";
function charClick(el, i) {
var p = el.parentNode.id;
var s = "para '" + p + "' idx " + i + " click";
ele = document.getElementById('result');
ele.innerHTML = s; }
function initCharClick(ids) {
var el; var from; var length; var to; var cc;
var idArray = ids.split(" ");
var idQty = idArray.length;
for (var j = 0; j < idQty; ++ j) {
el = document.getElementById(idArray[j]);
from = unescape(el.innerHTML);
length = from.length;
to = "";
for (var i = 0; i < length; ++ i) {
cc = from.charAt(i);
to = to + pre + i + inf + cc + suf; }
el.innerHTML = to; } }
</script>
<style>
.characters div {
padding: 0;
margin: 0;
display: inline }
</style>
</head>
<body class='characters' onload='initCharClick("h1 p0 p2")'>
<h1 id='h1'>Character Click Demo</h1>
<p id='p0'>æ€ – ࿗Ø —</p>
<p id='p1'>Next 𐐷 😀E para.</p>
<p id='p2'>© 2017</p>
<hr>
<p id='result'> </p>
</body>
</html>
[1] This simple example does not have handling for surrogate pairs, but such could be added in the body of the i loop.