I am relatively new to the world of programming. I have a sturdy knowledge of HTML and CSS, and recently picked up JavaScript. I am working on a series of text generators as a school project; my goal is to be able to, on the website, click a button and have the computer spit out random text each time you click the button. However, while I have a good grasp on HTML and JavaScript, my knowledge on how to combine the two to give a webpage functionality is virtually nonexistent.
I created a "Shakespearean Insult Generator" using JavaScript, which is functional, and I figured out how to add a button to a page so that when you click the button, it prints a randomly generated insult to the page:
<script>
var adjective1 = ["artless", "bawdy", "beslubbering"...
var adjective2 = ["base-court", "bat-fowling", "beef-witted"...
var noun = ["apple-john", "baggage", "barnacle"...
var insult = "Thou art a " + adjective1[Math.floor(Math.random() * 60)] + ", " + adjective2[Math.floor(Math.random() * 60)] + ", " + noun[Math.floor(Math.random() * 60)] + "!";
var genInsult = function() {
x=document.getElementById("replace");
x.innerHTML=insult;
};
</script>
<p id= "replace">Your insult will appear here!</p>
<button type="button" onclick="genInsult()">Generate Insult</button>
However, once you press the button once, you cannot press it again to generate another insult unless you refresh the page.
So my question is: how can I make this button reusable by using JavaScript?
I've tried searching for an answer to my question, but the problem is that I'm so new to JavaScript that I often have trouble understanding other people's questions and the answers to them. Also, a lot of responses reference jQuery, a language I do not know. If anyone has a solution within the realm of JavaScript, I would be extremely grateful!
I might not know a heck of a lot now, but I am very eager to learn!
Move this:
var insult = "Thou art a " + adjective1[Math.floor(Math.random() * 60)] + ", " + adjective2[Math.floor(Math.random() * 60)] + ", " + noun[Math.floor(Math.random() * 60)] + "!";
within your genInsult() function and you should be good. Right now it's outside so it will only generate once.
As has already been stated, your main issue is just that the creation of the insult is outside of your function, so it is only created once, rather than each time the function is called. I really enjoy re-factoring code, I wanted to offer you this: (click here for live demo)
I have written your app using an object oriented pattern and cleaning up a lot of mess and inefficiency. I think it will do you a lot of good to study this and learn all you can!! By the way, don't ever use inline javascript (the click function in your html) unless you're using a framework that makes sense of it (like AngularJS).
<p id="insult">Your insult will appear here!</p>
<button type="button" id="generate">Generate Insult</button>
and the js:
var generator = { //only one global variable!!
init: function() { //this sets up your app for use
var elem = document.getElementById('insult');
var obj = this; //cache reference to the generator object (the "this" pointer will be changed within the click function
var button = document.getElementById('generate');
button.addEventListener('click', function() {
elem.innerText = obj.generateInsult();
});
},
generateInsult: (function() { //use an IIFE to give a nice closure to these variables
var generateInsult = function() {
return "Thou art a "+adjs1[r(adjs1)]+", "+adjs2[r(adjs2)]+", "+nouns[r(nouns)]+"!";
};
function r(arr) {
return Math.floor(Math.random() * arr.length);
}
var adjs1 = ["artless", "bawdy", "beslubbering"];
var adjs2 = ["base-court", "bat-fowling", "beef-witted"];
var nouns = ["apple-john", "baggage", "barnacle"];
return generateInsult;
}())
};
generator.init(); //I suggest running this on document ready to be sure the elements are loaded. If not, this will not work. Keep that in mind!
Related
I have written the following JavaScipt code within a Spotfire TextArea. I include the application and tag for completeness, but I don't believe my issue is Spotfire-specific. Essentially, I have a timer which runs every 5 minutes, and clicks on a link (clickLink('Foo');) to trigger execution of some Python code elsewhere in the application. If the application also contains a timestamp of the last full update, which occurs every 30 minutes in the same manner (clickLink('Foo');):
function reportTimestamp() {
var timeNow = new Date();
var hoursNow = timeNow.getHours();
var minutesNow = timeNow.getMinutes();
var secondsNow = timeNow.getSeconds();
return hoursNow + ":" + minutesNow + ":" + secondsNow;
};
function timeBasedReaction(timestampAge){
if (timestampAge >= 1800) {
clickLink('Foo');
clickLink('Bar');
} else if (timestampAge >= 300) {
clickLink('Foo');
};
};
/*
function timeBasedReaction_B(timestampAge){
if (timestampAge >= 300) {
clickLink('Foo');
if (timestampAge >= 1800) {
clickLink('Bar');
};
};
};
*/
function clickLink(linkName) {
var clickTarget = document.getElementById(linkName).children[0];
clickTarget.click();
};
function checkTimestampAge() {
console.log(reportTimestamp());
var myTimeStamp = document.getElementById('Timestamp').children[0]
var timeStampMS = new Date(myTimeStamp.textContent).getTime();
var currentDateMS = new Date().getTime();
var timestampAgeSeconds = (currentDateMS - timeStampMS)/1000;
timeBasedReaction(timestampAgeSeconds);
};
function pageInitialization() {
checkTimestampAge();
var myTimer = null;
var timerInterval = 300000;
myTimer = setInterval(function(){checkTimestampAge()},timerInterval);
}
pageInitialization();
For reasons unclear to me, running this code in the application or in a web browser starts off fine, but eventually leads to very large memory allocation.
I've tried to read
4 Types of Memory Leaks in JavaScript and How to Get Rid Of Them,
JS setInterval/setTimeout Tutorial, and
An interesting kind of JavaScript memory leak, and it's a start, but I don't know enough to really understand what I'm doing wrong and how to correct it.
Thanks, and sorry for the huge block of text.
This causes a memory leak because of how Spotfire handles Javascript which has been associated with/loaded into a TextArea.
Both in the desktop client, as well as in the Webplayer instance, when the page is loaded, all the portions of that page are loaded, include the TextArea and including the Javascript associated therein. My previous understanding in the comments above:
"the code is intended to run when the page loads, and it was my understanding that it would stop/be cleared if the page was re-loaded or someone navigated away from it"
was incorrect. One of the script's actions was to update/redraw the HTML location in the TextArea. This, in turn, reloads the TextArea but does not clear the existing Javascript code. However, it's not really accessible anymore, either, since var myTimer = null actually creates a new myTimer rather than nulling-out the existing one. In this way, instances of myTimer increase geometrically as instances of function timeBasedReaction run and continually update the underlying TextArea and load in more of the same Javascript.
To anyone who ha a similar issue and come here, it's been over 3 months and I haven't figured out how to solve this once and for all. If I do, I'll try to come back with another update.
I'm experimenting with indexedDB. Now everything is like asynchronous and that hurts my brain a lot.
I created an object like this:
var application = {};
application.indexedDB = {};
application.indexedDB.db = null;
application.indexedDB.open = function() {
var dbName = "application";
var dbVersion = 1;
var openRequest = indexedDB.open(dbName, dbVersion);
openRequest.onupgradeneeded = function(e) {
console.log("Upgrading your DB (" + dbName + ", v" + dbVersion + ")...");
var thisDB = e.target.result;
if (!thisDB.objectStoreNames.contains("journal")) {
thisDB.createObjectStore(
"journal",
{keyPath: "id"}
);
}
}
openRequest.onsuccess = function(e) {
console.log("Opened DB (" + dbName + ", v" + dbVersion + ")");
application.indexedDB.db = e.target.result;
}
openRequest.onerror = function(e) {
console.log("Error");
console.dir(e);
}
};
Now I am able to open the dbconnection with application.indexedDB.open(). Now I added another function to my Object:
application.indexedDB.addItemToTable = function(item, table) {
var transaction = application.indexedDB.db.transaction([table], "readwrite");
var store = transaction.objectStore(table);
//Perform the add
var request = store.add(item);
request.onerror = function(e) {
console.log("Error", e.target.error.name);
//some type of error handler
}
request.onsuccess = function(e) {
console.log("Woot! Did it");
}
};
My instruction-sequence extended like this:
application.indexedDB.open()
application.indexedDB.addItemToTable(item, "journal")
But this doesn't work. Because the open-Instruction is asynchronous the application.indexedDB.dbis not yet available when i call it in the addItemToTable-Function.
How does a Javascript-Developer solve this?
I was following this tutorial here: http://code.tutsplus.com/tutorials/working-with-indexeddb--net-34673 and now I have some problems with those examples.
For example he creates the HTML-Output directly in the "onsuccess"-Part (in the "Read More Data" Section) . In my eyes this is bad coding because the view has nothing to do with the db-reading part.. isn't it? but then comes my question. how the heck can you return something in the "onsuccess"-Part?
Adding callbackfunctions is somewhat complicated. Especially when i want to read some data and with that resultset get some more data. It's very complicated to describe what i mean.
I made a little fiddle - maybe it clarifies things.. -- http://jsfiddle.net/kb8nuby6/
Thank you
You don't need to use someone else's extra program. You will need to learn about asynchronous javascript (AJAX) before using indexedDB. There is no way to avoid it. You can learn about AJAX without learning about indexedDB. For example, look at how XMLHttpRequest works. Learn about setTimeout and setInterval. Maybe learn about requestAnimationFrame. If you know nodejs stuff, review process.nextTick. Learn about how functions are first class. Learn about the idea of using a callback function. Learn about continuation passing style.
You will probably not get the answer you are looking for to this question. If anything, this is a duplicate of the several thousand other questions on stack overflow about asynchronous programming in javascript. It is not even that related to indexedDB. Take a look at some of the numerous other questions about asynchronous js.
Maybe this gets you started:
var a;
setTimeout(function() { a = 1; }, 10);
console.log('The value of a is %s', a);
Figure out why that did not work. If you do, you will be much closer to finding the answer to this question.
The pattern I commonly adopted is wait all database operations until connected. It is similar concept to $.ready in jQuery.
You will find that as the app get age, you have many schema versions and need to upgrade data as well. A lot of logic in database connection itself.
You can use callback queue, if you need to use database before ready. Here is snippet from Google analytics on commend queue:
// Creates an initial ga() function. The queued commands will be executed once analytics.js loads.
i[r] = i[r] || function() {
(i[r].q = i[r].q || []).push(arguments)
},
Basically, you will execute these callbacks once database is connected.
I highly recommend to check out my own library, ydn-db. It has all these concepts.
Basically, what I'm trying to do is to pass a parameter through the URL to the php code, however, it seems that in the function body of the on message event, I can't change the source. Here's the code below:
var source = new EventSource("get_message.php?latest_chat_ID=0");
var i = 0;
$(source).on("message", function (event) {
var data = event.originalEvent.data;
++i;
source = new EventSource("get_message.php?latest_chat_ID=" + i);
// $.post("get_message.php", {latest_chat_ID: 0}, function (data, status) {});
$("#messages").html($("#messages").html() + data);
});
I was wondering -
How do I rectify this problem?
Are there other ways to send data to a PHP page? (I contemplated using the $.post{} jQuery function, but that will execute the script twice - once from firing the EventSource event and once from the .post{} request?)
I also understand that alternative technologies exist, such as WebSockets and libraries such as node.js, that are better suited for bidirectional communication. However, most of my base code is written with an SSE implementation in mind, and I'd like to stick to that.
If you want to continue using SSE, I think you'll need to rewrite what you have similar to what is below. The reason what you have right now doesn't work is because you are only listening to the first EventSource, and just changing the variable. The variable is not reused by jQuery when you change it. Plus, I probably would skip using jQuery for that since it's going to try and cache things you don't want cached.
var listenToServer = function(i) {
source = new EventSource("get_message.php?latest_chat_ID=" + i);
source.onmessage = function(event) {
var data = event.originalEvent.data;
$messages.html($messages.html() + data);
listenToServer(i + 1);
}
},
$messages = $('#messages'),
source;
listenToServer(0);
I also went ahead and cached $('#messages') so you're not creating new objects over and over. Left source outside of the function so that you don't have to worry as much about garbage collection with the various EventSources.
I have written a very simple script to test one page for possible discounts options.
But I have faced the problem that once I do 'button.click();' the page loading is blocked until I complete my function, so following actions like button.click(); do not make sense.
Is that possible to make page to load while I am inside of the function? This should be ran in Developer toolbar in safari (I believe I cannot set onload event for the page on my side, so need to do that without using even handlers).
var card = document.getElementById('discount_card');
var button = $('.buttonTotal');
var disc_val = document.getElementById('cart_discount').firstChild;
for(var i=init;i<=finish;i++){
card.value = i;
button.click();
disc = disc_val.data;
if(disc > my_discount) console.log(disc + " : " + i);
}
Does order of buttons clicking matters? If no, try to replace button.click(); with (function (button) {setTimeout(function () {button.click();}, 1)})(button);
If order matters, you still can use setTimeout, but you'll be needed to change your script logic (replace loop with recursion).
I am writing a webOS app and I want the following code to present a "Good Morning!" message. What code do I need to put in my Main-scene.html file and what else do I need in the Main-assistant.js file to make this work?
Many thanks in advance.
MainAssistant.prototype.welcomeMessage = function(){
datetoday = new Date();
timenow = datetoday.getTime();
datetoday.setTime(timenow);
thehour = datetoday.getHours();
if (thehour > 18)
display = "Evening";
else
if (thehour > 12)
display = "Afternoon";
else
display = "Morning";
var greeting = ("Good " + display + "!");
document.write(greeting);
};
All the best
David
I don't know when the welcomeMessage method will be called, but if it's after the document has finished loading then it is closed. Calling document.write then will call document.open, which completely clears the document. Likely you have an element that the message should be written into, much better to use the load or DOMReady events (or whatever is available on WebOS).
Consider a much simpler form of the function and don't forget to declare variables to limit their scope to only what is necessary:
var tod = ['morning','morning','afternoon','evening'];
var now = new Date();
var greeting = 'Good ' + (tod[now.getHours()/6|0]) + '!';
If you want a library, try myLibrary. No, it's not my Library, it's your library when you use it. You can create a customised core and just the DOM ready part that is quite small and the code quality is excellent.