I am writing a JavaScript utility which allows a user to detect if a particular object / function is available at runtime. Here is the source code, this works but it needs editing every time you want to test for another object:
<!DOCTYPE html>
<html>
<head>
<title>HTML5 Template</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#TestObject').click(function() {
alert(typeof(HTMLCollection));
});
});
</script>
</head>
<body>
<input id="ObjectType" type="text" />
<input id="TestObject" type="button" value="Test" />
</body>
</html>
When the button is clicked, it displays an alert indicating "object" or "function" if the item exists, and "undefined" if it does not.
What I want is to have a textbox <input id="ObjectType" type="text" /> where I can type in the object to test, and then click the button to test it, which will eliminate the need to keep editing the document. Is this possible? Is there anything similar to reflection that I can use?
This is possible due to JavaScript object properties actually being associative key-value pairs, meaning that obj.property is equivalent to obj['property'].
Applying this to your problem, the following code would work:
alert(typeof(window[$('#ObjectType').val()]));
This works because all "global" variables, are actually properties of the window object.
You can get the object from a string-ed class name by doing window[className]. We'll use that by getting the string value of a class name from the input text box, getting the object from the class name, and calling typeof against it:
Keeping your code the same just replace alert(typeof(HTMLCollection)); with alert(typeof(window[$("#ObjectType").val()]));
Related
As you can see below, I have a function,createCharacter(), that calls another function, getUserInput(). This function is intended to grab the value of a text input element and return that value to be stored in the "name" variable within the createCharacter. However, if you run this code. It completely runs through both functions, never giving the opportunity for the user to input a value. Perhaps a more specific question is, how can I make this function wait for the variable to be defined before returning it to createCharacter? I've tried to wrap the code in a while loop that will run for as long as value is undefined. Didn't work, created an infinite loop and crashed. ANY solution to this problem will be greatly appreciated. I feel like the solution is so simple, but I just can't figure it out for the life of me. Thanks.
var messageDisplay = document.querySelector(".message-display");
var menuInput = document.querySelector(".menu-input");
var playerInput = document.querySelector(".player-text")
function createCharacter() {
messageDisplay.textContent = "Welcome! What is your name?";
var name = getUserInput();
messageDisplay.textContent = "Hello " + name + "!";
}
function getUserInput() {
var textValue = playerInput.value;
return textValue;
}
createCharacter();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="message-display"></div>
<div class="menu-input">
<form class="menu-input-content">
<input class="player-text" type="text">
<input class="submit-button" type="submit">
</form>
</div>
<script type="text/javascript" src="app.js"></script>
</body>
</html>
I think you have a misunderstanding of how the DOM and the user interact. The DOM is event based. You can start by add an change event listener to your input element (or on the submit button):
menuInput.onchange = createCharacter;
And then remove the call to createCharacter, the last line in the code you posted.
This will then call the createCharacter() method when you change the text in the input at all, which is probably not what you want. You could also try:
var menuSubmit = document.querySelector(".submit-button");
menuSubmit.onclick = createCharacter;
And that is probably more on the right track.
However, given your misunderstanding in the first place, perhaps you need to reconsider how you approach your design?
The reason it runs through the code immediately is because of the last line. The browser loads the JS and executes everything in the global scope. Your query selectors are run and stored in those variables, the functions defined, and then on the last line you call one of the defined functions.
To fix this you need to redesign your app to be event based. Keep defining needed variables and functions in the global scope, as you are doing here, but then change your execution to be in response to events.
I think you are looking for something like this. You should be using the events to get what you wanted. You are executing createCharacter() before even the user clicked the Submit button. Hence you see "Hello !" as there is no user input initially.
function submitClicked(event) {
var messageDisplay = document.querySelector(".message-display");
var playerInput = document.querySelector(".player-text");
messageDisplay.innerHTML = "Hello " + playerInput.value;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="message-display"></div>
<div class="menu-input">
<input class="player-text" type="text">
<input class="submit-button" onclick="submitClicked()" type="submit">
</div>
<script type="text/javascript" src="app.js"></script>
</body>
</html>
This question already has answers here:
JS function named `animate` doesn't work in Chrome, but works in IE
(3 answers)
Closed 6 years ago.
In my HTML I define the lang function in the script tag and add the "Test Fire!" button which has to call lang on click:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Testing Functions</title>
<script type="text/javascript">
function lang() {
alert("Hello, World! It's JavaScript this time");
}
</script>
</head>
<body>
<form action="">
<input type="button" value="Test Fire!" onclick="lang();">
</form>
</body>
</html>
However, if I click the button I get this error:
Uncaught TypeError: lang is not a function
But if I change the function name from lang to anything else this code works fine.
Consider this code:
<input type="button" value="Debugger Test" onclick="debugger;" />
<input type="button" value="Prototype Test" onclick="console.log(__proto__);" />
When you click on “Debugger Test” and open your debugger, you’ll see that there seems to be an implicit with scope wrapped around the onclick, making all the <input>’s properties accessible without needing to refer to the button.
Clicking on “Prototype Test” logs the prototype of the current scope. You’ll see that it’s the HTMLInputElement’s prototype, making all the scopable properties of this entire prototype chain available to the scope.
Interestingly, the scopable part of the prototype chain of the current HTMLDocument is included as well.
All this means that all global attributes (lang is one of them) and several others specific to buttons are overridden. E.g. value, type also wouldn’t work. Similarly, variables like createElement (from document) also wouldn’t work, but the unscopable append (from ParentNode.prototype) would.
All this is also explained in this answer to a related question about global variables clashing with window properties.
Your best bet is to use the standard way of adding event listeners: addEventListener.
<input type="button" value="Test" />
<script>
function lang() {
alert("Hello, World! It’s not an HTML event handler attribute this time");
}
document.querySelector("input").addEventListener("click", lang);
</script>
There is no reason to complicate (I really do not know why it does not work), but you can use:
Add the alert directly in the input.
Result: https://jsfiddle.net/cmedina/h4m1qcoq/6/
or
Add listener to input
function lang() {
alert("Hello, World! It's JavaScript this time");
}
document.getElementById('test').onclick = lang
Result: https://jsfiddle.net/cmedina/h4m1qcoq/7/
This question already has answers here:
JS function named `animate` doesn't work in Chrome, but works in IE
(3 answers)
Closed 6 years ago.
In my HTML I define the lang function in the script tag and add the "Test Fire!" button which has to call lang on click:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Testing Functions</title>
<script type="text/javascript">
function lang() {
alert("Hello, World! It's JavaScript this time");
}
</script>
</head>
<body>
<form action="">
<input type="button" value="Test Fire!" onclick="lang();">
</form>
</body>
</html>
However, if I click the button I get this error:
Uncaught TypeError: lang is not a function
But if I change the function name from lang to anything else this code works fine.
Consider this code:
<input type="button" value="Debugger Test" onclick="debugger;" />
<input type="button" value="Prototype Test" onclick="console.log(__proto__);" />
When you click on “Debugger Test” and open your debugger, you’ll see that there seems to be an implicit with scope wrapped around the onclick, making all the <input>’s properties accessible without needing to refer to the button.
Clicking on “Prototype Test” logs the prototype of the current scope. You’ll see that it’s the HTMLInputElement’s prototype, making all the scopable properties of this entire prototype chain available to the scope.
Interestingly, the scopable part of the prototype chain of the current HTMLDocument is included as well.
All this means that all global attributes (lang is one of them) and several others specific to buttons are overridden. E.g. value, type also wouldn’t work. Similarly, variables like createElement (from document) also wouldn’t work, but the unscopable append (from ParentNode.prototype) would.
All this is also explained in this answer to a related question about global variables clashing with window properties.
Your best bet is to use the standard way of adding event listeners: addEventListener.
<input type="button" value="Test" />
<script>
function lang() {
alert("Hello, World! It’s not an HTML event handler attribute this time");
}
document.querySelector("input").addEventListener("click", lang);
</script>
There is no reason to complicate (I really do not know why it does not work), but you can use:
Add the alert directly in the input.
Result: https://jsfiddle.net/cmedina/h4m1qcoq/6/
or
Add listener to input
function lang() {
alert("Hello, World! It's JavaScript this time");
}
document.getElementById('test').onclick = lang
Result: https://jsfiddle.net/cmedina/h4m1qcoq/7/
A call to numericInput(), like this:
numericInput("obs", "Observations:", 10, min = 1, max = 100)
constructs HTML code like this:
<div class="form-group shiny-input-container">
<label for="obs">Observations:</label>
<input id="obs" type="number" class="form-control" value="10" min="1" max="100"/>
</div>
Then, presumably, in the browser, JavaScript code provided by one of the scripts included in the HTML doc's header finds that <input> element and renders it with the interactive widget displayed below:
I'm having a hard time, though, figuring out where the code that finds that <input> element and then triggers production of the corresponding widget is stored. Is it in Shiny's own JavaScript, or in that borrowed from by Bootstrap or jQuery UI or one of the other plugins that ship with shiny?
My question(s):
Where is the JavaScript code that provides the widget pictured above and associates it with the HTML <input> element? And how, from the code that's involved, might I have learned that on my own?
More only possibly useful details
This section of the script "shiny.js" finds the <input> element of interest, and provides methods that can get and set the widget's value. It doesn't (as far as I can see) provide the widget itself.
var numberInputBinding = {};
$.extend(numberInputBinding, textInputBinding, {
find: function(scope) {
return $(scope).find('input[type="number"]');
},
getValue: function(el) {
var numberVal = $(el).val();
if (/^\s*$/.test(numberVal)) // Return null if all whitespace
return null;
else if (!isNaN(numberVal)) // If valid Javascript number string, coerce to number
return +numberVal;
else
return numberVal; // If other string like "1e6", send it unchanged
},
setValue: function(el, value) {
el.value = value;
[... snip ...]
}
});
inputBindings.register(numberInputBinding, 'shiny.numberInput');
And here is a copy of the <head> section of the the shiny-generated HTML file that results in the numericInput widget. The scripts it references can mostly be found here
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<script type="application/shiny-singletons"></script>
<script type="application/html-dependencies">json2[2014.02.04];jquery[1.11.0];shiny[0.12.2];bootstrap[3.3.1]</script>
<script src="shared/json2-min.js"></script>
<script src="shared/jquery.min.js"></script>
<link href="shared/shiny.css" rel="stylesheet" />
<script src="shared/shiny.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="shared/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
<script src="shared/bootstrap/js/bootstrap.min.js"></script>
<script src="shared/bootstrap/shim/html5shiv.min.js"></script>
<script src="shared/bootstrap/shim/respond.min.js"></script>
<title>Hello Shiny!</title>
</head>
Here's the incorrect assumption that made this so hard for me to figure out:
Then, presumably, in the browser, JavaScript code provided
by one of the scripts included in the HTML doc's header finds that
element and renders it with the interactive widget displayed
below:
In fact, as #epascarello points out, modern browsers themselves support <input type="number">.
(For further documentation of this fact, along with a long list of the features whose support was enabled by the incorporation of JavaScript in these modern web browsers, see Chapter 4 of "HTML for Web Designers".)
look at this code,
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script>
function change()
{
document.getElementById("myInput").type="text";
}
</script>
</head>
<body onload="setTimeout('change()',2000);">
<input type = "button" id="myInput">
</body>
</html>
now, my button becomes text box after 2 seconds.. Iam happy!
Why not the same happens when i try to do from the browser (i tested in IE6 & Chrome) using
javascript:document.getElementById("myInput").type="text"
different browsers behave in different way...
when i tried javascript:document.getElementById("myInput").value="myNewValue", IE6 changes the value, but chrome doesn't..
Why is this "javascript:" behaving like this ?
Use:
javascript:document.getElementById("myInput").setAttribute('type','text');
I'm not sure why it behaves like that, I think that it is because setting a value returns the value, and returning a string results in a page displaying that string, like:
javascript:"test";
Will open a page with 'test'., Using setAttribute() returns nothing.