JavaScript - Modify function to be reusable in later instances? - javascript

This is a followup to this question.
I have a function that I would like to make reusable so I don't have to make new, very similar functions over and over to achieve the same effect. Specifically, I would like to know how to change var message after the first function instance runs without losing the original message.
Here's my code:
var message = `This message is (hopefully) a successful implementation of JS video game scrolling!
//Pretty cool, huh? Well, believe it or not, this whole page is a test for a very basic interactive story using HTML/JavaScript!
// Let's see if we can add some fade-in buttons, shall we?
//(By the way--you can click anywhere in this window to instantly clear through subsequent text scrolls.)`;
var timer = setInterval(dialogue, 20);
function dialogue(add = 1){ // By default 1 character is made visible
var len = $("#pid").text().length + add; // Get desired length
$("#pid").text(message.substr(0, len)); // Make the change
if (len < message.length) return; // Nothing more to do
clearInterval(timer); // All is shown, so stop the animation
$("#button1").fadeIn(); // and fade in the button
};
// On click, pass the length of the message to the function
$(document).click(dialogue.bind(null, message.length));
// Hide the button on page load
$("#button1").hide();
<!DOCTYPE HTML>
<html>
<head>
<title>Sandbox</title>
<link rel="stylesheet" href="mainstyle.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
</head>
<body>
<p id="pid"></p>
<button id="button1">Ooh, here's one! Click to see what it does!</button>
</body>
</html>

edit: https://jsfiddle.net/n8Lczdk0/4/
I'm not sure what you mean, but if you wrap everything in a function that takes message as an argument, then it'll be in the dialogue function's closure and you'll be able to update var message after you call the wrapper function without dialogue() knowing about it. As they say, a few lines of code are worth hundreds of prose:
var message = `This message is (hopefully) a successful implementation of JS video game scrolling!
Pretty cool, huh? Well, believe it or not, this whole page is a test for a very basic interactive story using HTML/JavaScript!
Let's see if we can add some fade-in buttons, shall we?
(By the way--you can click anywhere in this window to instantly clear through subsequent text scrolls.)`;
const f = message => {
var timer = setInterval(dialogue, 20);
function dialogue(add = 1){ // By default 1 character is made visible
var len = $("#pid").text().length + add; // Get desired length
$("#pid").text(message.substr(0, len)); // Make the change
if (len < message.length) return; // Nothing more to do
clearInterval(timer); // All is shown, so stop the animation
$("#button1").fadeIn(); // and fade in the button
};
// On click, pass the length of the message to the function
$(document).click(dialogue.bind(null, message.length));
// Hide the button on function call
$("#button1").hide();
}
f(message)
message = "some new value"
So above, I'm essentially wrapping your whole js code in a function that takes message as an argument. Kinda like currying your dialogue function.
You could also pass your ids as arguments and make it fully reusable. Just pass a message and DOM ids to the function and the magic unrolls with associated buttons fading in as various texts show up.
Hope this helped, cheers,
thomas

You can try with below one way
You can pass one optional argument(message) to the function and check if it is passed then use that message otherwise use default original message.
var message = `This message is (hopefully) a successful implementation of JS video game scrolling!
Pretty cool, huh? Well, believe it or not, this whole page is a test for a very basic interactive story using HTML/JavaScript!
Let's see if we can add some fade-in buttons, shall we?
(By the way--you can click anywhere in this window to instantly clear through subsequent text scrolls.)`;
var timer = setInterval(dialogue, 20);
function dialogue(add = 1, custom_message){ // By default 1 character is made visible
var temp_message;
if(typeof custom_message === "undefined") {
temp_message = message;
}
else {
temp_message = custom_message;
}
var len = $("#pid").text().length + add; // Get desired length
$("#pid").text(temp_message.substr(0, len)); // Make the change
if (len < temp_message.length) return; // Nothing more to do
clearInterval(timer); // All is shown, so stop the animation
$("#button1").fadeIn(); // and fade in the button
};
// On click, pass the length of the message to the function
$(document).click(dialogue.bind(null, message.length));
// Hide the button on page load
$("#button1").hide();
<!DOCTYPE=HTML>
<html>
<head>
<title>Sandbox</title>
<link rel="stylesheet" href="mainstyle.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
</head>
<body>
<p id="pid"></p>
<button id="button1">Ooh, here's one! Click to see what it does!</button>
</body>
</html>

You can simply ask for another parameter in dialogue() for the new message. You can then create an array of messages, from which you can choose whatever message you'd like to pass. This will ensure that all your messages are saved.
var messages = ["Message one", "Message two", "Message three"];

Related

JavaScript - Make function properly run upon clicking a button

This a followup to this question, sorry for making them so close to each other.
I am trying to have a button disappear upon clicking and then running a subsequent function, but here, clicking on the button makes the first char of message2 appear, then has it fade back in again; continuing to click the button makes each subsequent character appear until none are left, and the second button never appears no matter how many times I click. It should work, so what is the problem here?
EDIT: Somehow slipped my mind to add timer2. Now the effect is closer to what I want, but it doesn't completely match the effect of the original dialogue function (which is what I was going for).
My code:
var message = `This message is (hopefully) a successful implementation of JS video game scrolling!
Pretty cool, huh? Well, believe it or not, this whole page is a test for a very basic interactive story using HTML/JavaScript!
Let's see if we can add some fade-in buttons, shall we?
(By the way--you can click anywhere in this window to instantly clear through subsequent text scrolls.)`;
var timer = setInterval(dialogue, 20);
function dialogue(add = 1) { // By default 1 character is made visible
var len = $("#pid").text().length + add; // Get desired length
$("#pid").text(message.substr(0, len)); // Make the change
if (len < message.length) return; // Nothing more to do
clearInterval(timer); // All is shown, so stop the animation
$("#button1").fadeIn(); // and fade in the button
};
// On click, pass the length of the message to the function
$(document).click(dialogue.bind(null, message.length));
var message2 = `Wonderful! Now let's try summoning another button.`
function dialogue2(add2 = 1) {
$("#button1").hide();
var timer2 = setInterval(dialogue2,20);
var len2 = $("#pid2").text().length + add2; // Get desired length
$("#pid2").text(message2.substr(0, len2)); // Make the change
if (len2 < message2.length) return; // Nothing more to do
clearInterval(timer2); // All is shown, so stop the animation
$("#button2").fadeIn(); // and fade in the button
}
// Hide the button on page load
$("#button1").hide();
$("#button2").hide();
$("#button3").hide();
<!DOCTYPE=HTML>
<html>
<head>
<title>Sandbox</title>
<link rel="stylesheet" href="mainstyle.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
</head>
<body>
<p id="pid"></p>
<button id="button1" onclick="dialogue2(add2 = 1);">Ooh, here's one! Click to see what it does!</button>
<p id="pid2"></p>
<button id="button2">Speak of the devil, and he shall come!</button>
<p id="pid3"></p>
<button id="button3">Last time, I promise!</button>
</body>
</html>

How to update the same browser page with new URLs with a specific delay using Javascript?

I want to open 10 webpages one after another, in the same browser window and with some specific delay.
e.g. I want
open "www.Anywebsite.com"
Delay 5 seconds
In the same page open a new "www.Anywebsite.com"
I am trying to do something like this
<!DOCTYPE html>
<html>
<body>
<script>
var myVar=setInterval(function(){myTimer()},1000);
var condOpen = 0;
function myTimer()
{
if (condOpen == 0)
{
window.open("http://www.tut.fi","_self");
condOpen = condOpen + 1;
}
else if(condOpen == 1)
{
window.open("www.w3schools.com","_self");
}
}
</script>
</body>
</html>
The problem is it opens only the first page, and as I read about "setInterval", it must execute the function specified after some delay. Please help me in this, I have no prior experience with JavaScript but this is needed for a particular task I am doing.
Your code is failing because when you call window.open() with the _self parameter, it's just like doing window.location = "http://www.example.com/";. This replaces your page with the new page you're loading. All JavaScript on your page is killed, including any timers.
You can do something like what you're trying to do here, but you would need to use a different target window name in the window.open() call. You can make up any arbitrary name here; just don't begin the name with _ to avoid the special names like _self.
Unfortunately, you will run afoul of popup blockers if you do this. In order to get past the popup blockers, what you need to do is have the first window.open() be triggered directly by a user action, e.g. by clicking a button. After that you can use the timer to change URLs in the window you've opened.
Also you will get tired of writing if statements when you want to add more URLs to your list. You can use an array of URLs to simplify the code.
And finally, it would be a really good idea to indent your JavaScript code to show its structure. Putting it all against the left margin makes it hard to follow.
Putting those together:
<!DOCTYPE html>
<html>
<head>
<title>Window Loop Test</title>
</head>
<body>
<button onclick="start()">Start</button>
<script>
var targets = [
'http://www.stackoverflow.com/',
'https://developer.mozilla.org/en-US/',
'http://www.w3fools.com/'
];
var iTarget;
function nextTarget(){
window.open( targets[iTarget], 'target' );
if( ++iTarget >= targets.length ) {
iTarget = 0;
}
}
function start() {
iTarget = 0;
nextTarget();
setInterval( nextTarget, 5000 );
}
</script>
</body>
</html>
And here is a fiddle to test.

Function to find string on page and click link next to it?

I'm wondering whether it is possible to devise a script which will search a webpage for a certain string of text, and then click the link in the element id directly to its right.
Is this possible. Maybe javascript, php?
Please help, and thanks to all that do. :)
#Four_lo
Thanks for your reply. I'm sorry, maybe it's because I'm pretty new to javascript, but I can't really understand anything on the page you suggested.
I put together some javascript which will search the page for an element id and click the link within there.
<html>
<head>
<script type="text/javascript">
function init(){
var linkPage = document.getElementById('linkid').href;
window.location.href = linkPage;
}
onload=init;
</script>
</head>
<body>
GO HERE
I WANT TO CLICK HERE!
</body>
</html>
So basically, I need to search the page for GO HERE. Then, once this is found, I need to click the link in id="thisone", if that makes sense.
The above code works, and clicks the link within the id specified. However, I'd like to find certain text within that id, then move onto the next id, and click the link within that id.
It is possible. It will probably take some finesse but here is where you should start to access String you need. I believe regular expressions will be a must as well.
http://dom.spec.whatwg.org/#processinginstruction
http://domparsing.spec.whatwg.org/
Slightly more complicated than it needs to be:
function performAfterLinkWithText(text, perform) {
// get all the links
var $links = document.getElementsByTagName('a');
// scan them for your text
for(var i in $links) {
if($links[i].innerHTML === text) {
var $next = $links[i] // ready for loop
, terminateAfter = 20 // don't repeat forever
;
// keep checking the adjacent element
// because newlines show up as #text
do {
$next = $next.nextSibling;
} while( !$next.href && terminateAfter-- > 0 );
// do your thing
perform($next.href, $next); // window.location.href = $next.href;
}
}
}
// test -- performAfterLinkWithText('GO HERE', function(url, link) { console.log(url, link); });
performAfterLinkWithText('GO HERE', function(url) { window.location.href = $next.href; });
Or with jQuery:
window.location.href = $('a:contains("GO HERE")').next().attr('href')

JavaScript behaving weirdly, opening an drop/drag item only 'alert' is put

I have Drag/Drop functionality in my page embedded using YAHOO.js which is initalized at load of the page. When 'alert' is put in the init function, the Drag/Drop is working otherwise
it is not. Using Firebug I had debugged the code and seen when init function is called but not looping through the function when no alert is put.
This function should work when ALT key is pressed. I am using velocity template engine over JavaScript.
Sample code:
<script type="text/javascript" language="JavaScript">
var myLogger;
var dd1, ddTrashCan; // draggable div objs
#if ($displayOptions.isDoDragDropJavaScript())
YAHOO.util.Event.addListener(window, "load", DD_TestInit);
#end
function display(data) {
var output = "<div>" + data.text + "</div>";
element.innerHTML=output;
}
function DD_TestInit() {
#if ($showLoggerDiv)
initLogger();
#end
//display("date");
initDragObjects();
}
function logMsg(strMsg) {
if (myLogger)
myLogger.debug(strMsg);
}
function initDragObjects() {
//alert('---');
if (dd1) dd1.unreg();
if (ddTrashCan) ddTrashCan.unreg();
YAHOO.util.DDM.mode = YAHOO.util.DDM.POINT;
YAHOO.util.DDM.clickTimeThresh = 10;
## init constant drag objects, draggable div and droppable trash, resp.
dd1 = new lineSched_Draggable("dragDiv1");
ddTrashCan = new lineSched_Droppable("TrashCan");
}
What I had found is whenever I put an alert or call any window.open() this works fine.
Any clue whats happening here.
There is timer event which is delaying the process.My feeling is that this is a timing issue. The page is not fully in place when on load. The alert slows the process down, essentially the page is in place by the time the user clicks Ok on the alert. Clearly, we can’t deploy the app with an alert. But, we can look into different places to put the initialization. We can try to place it the same place I added the timing, when the page receives the last table. The page should be fully formed at this point and the function should work properly.

Javascript: How to temporarily disable all actions on the page?

On a page with Ajax event, I want to disable all actions until the Ajax call returns (to prevent issues with double-submit etc.)
I tried this by prepending return false; to the current onclick events when "locking" the page, and removing this later on when "unlocking" the page. However, the actions are not active any more after they are "unlocked" -- you just can't trigger them.
Why is this not working? See example page below. Any other idea to achieve my goal?
Example code:
both the link and the button are showing a JS alert; when pressing lock, then unlock the event handler is the same as it was before, but doesn't work...?!?
The code is meant to work with Trinidad in the end, but should work outside as well.
<html><head><title>Test</title>
<script type="text/javascript">
function lockPage()
{
document.body.style.cursor = 'wait';
lockElements(document.getElementsByTagName("a"));
lockElements(document.getElementsByTagName("input"));
if (typeof TrPage != "undefined")
{
TrPage.getInstance().getRequestQueue().addStateChangeListener(unlockPage);
}
}
function lockElements(el)
{
for (var i=0; i<el.length; i++)
{
el[i].style.cursor = 'wait';
if (el[i].onclick)
{
var newEvent = 'return false;' + el[i].onclick;
alert(el[i].onclick + "\n\nlock -->\n\n" + newEvent);
el[i].onclick = newEvent;
}
}
}
function unlockPage(state)
{
if (typeof TrRequestQueue == "undefined" || state == TrRequestQueue.STATE_READY)
{
//alert("unlocking for state: " + state);
document.body.style.cursor = 'auto';
unlockElements(document.getElementsByTagName("a"));
unlockElements(document.getElementsByTagName("input"));
}
}
function unlockElements(el)
{
for (var i=0; i<el.length; i++)
{
el[i].style.cursor = 'auto';
if (el[i].onclick && el[i].onclick.search(/^return false;/)==0)
{
var newEvent = el[i].onclick.substring(13);
alert(el[i].onclick + "\n\nunlock -->\n\n" + newEvent);
el[i].onclick = newEvent;
}
}
}
</script>
<style type="text/css">
</style>
</head>
<body>
<h1>Page lock/unlock test</h1>
<p>Use these actions to lock or unlock active elements on the page:
lock,
unlock.</p>
<p>And now some elements:</p>
<a onclick="alert('This is the action!');return false;" href="#">link action</a>
<input type="button" value="button action" onclick="alert('This is another action!')"/>
</body>
</html>
Thanks guys for your ideas and answers.
Now I see that I have mixed up Strings and functions, which obviously can't work ;(
I should have made clear that we use some Web FW and tag libraries (Trinidad) which create the event handling (and Ajax) code, hence I can't edit that directly or use synchronous Ajax etc.
Moreover, Ajax is only one scenario where this code should be executed. It's purpose is to prevent the user to double-submit a page/action, which is also relevant for non-Ajax pages where you could kind of doulbe-click on a button. I know that this is not really safe, and it's only meant to be a "convenience" thingy to avoid getting the navigation error page too often (we have server-side protection, of course).
So, will try the div overlay, probably.
Thanks again,
Christoph.
How about setting up a global var
actions_disabled = 0
increment when the AJAX call starts then decrement when it finishes. All your "action" handlers can then start with
if (actions_disabled) return false;
Much simpler than debugging self-modifying code!
Alternatively, to lock your controls you could set:
control.disabled="disabled"
which will have the bonus of greying them out, making it obvious to the user that they can't submit. To unlock, simply set:
control.disabled=""
NEW IDEA BASED ON COMMENTS (can't quote code in comments, it appears ...):
You can always just hang extra attributes off Javascript objects:
To lock, you could:
control.onclick_old = control.onclick
control.onclick = "return false;"
To unlock, you could:
control.onclick = control.onclick_old
I once achieved this goal by creating a DIV that covered the area I wanted disabled, setting its z-index higher than any of the other elements on the page, and then setting its opacity to 0. By default, this DIV was hidden by display: none, so that it wouldn't interfere with anything. However, when I wanted the area disabled, I just set its display to block.
Steve
AJAX. Asynchronous. Just make the HTTP request synchronous. Problem solved.
The problem with your code is a result of not coming to grips with types in javascript.
When you say:
var newEvent = 'return false;' + el[i].onclick
what this does is coerce el[i].onclick (which is a function) to a string, then concatenates it to the string 'return false;'. Then when you reassign it as so:
el[i].onclick = newEvent;
onclick which was previously a function is now a string.
Then you attempt to resurrect your old function from the string by taking a substring:
var newEvent = el[i].onclick.substring(13);
which is fine, except newEvent is still a string! So when you assign it back to onclick again, you are assigning the string representation of the original function, not the function itself.
You could use eval to evaluate the string and return the function, but please don't do that. There are a number of better ways to do this, as has been suggested by other commenters.
I would also question why you wish to use AJAX at all if you don't want to allow asynchronous requests.
Put lockPage() at top of activete() function, and unlockPage() at bottom of deactivate().
activate: function() {
function lockPage()
{
lockElements(document.getElementsByTagName("a"));
lockElements(document.getElementsByTagName("input"));
lockElements(document.getElementsByTagName("button"));
};
function lockElements(el)
{
for (var i=0; i<el.length; i++)
{
el[i].style.pointerEvents="none";
}
};
lockPage();
// ...
},
deactivate: function() {
// ...
function unlockPage() {
unlockElements(document.getElementsByTagName("a"));
unlockElements(document.getElementsByTagName("input"));
unlockElements(document.getElementsByTagName("button"));
};
function unlockElements(el)
{
for (var i=0; i<el.length; i++)
{
el[i].style.pointerEvents="auto";
}
};
unlockPage();
},
Using a div overlay does not prevent a user from tab-ing into your page. Usually that is OK, since most users do not tab through a page anyhow.
If you use any keyboard shortcuts on your page, they will still be available, so separate handling will be needed for those.
Alse, I assume that clicking an element that can have focus (eg. an <a> tag), then pressing enter, would still cause a double submit.

Categories