I did a bunch of buttons on a navbar. These buttons can trigger different articles or lesson in two languages from side to side.
Later, I found that this is troublesome when I try to search for a specific article. So I decided that I'll add an input text bar to take my input and trigger a javascript function that I want.
I simplified the numbers in this post, but this is the core of the script. I named the functions with 4 digits numbers MMDD so I wish that I can type in a box 0312 and the function 0312 get executed.
However, I cannot seem to get it to work. Is that even possible in javascrip? If not what approach do you recommend?
function fr01() {
document.getElementById("fr01").innerHTML = "text here will appear in french"
}
function en01() {
document.getElementById("en01").innerHTML = "text here will apear in english"
}
function myFunction01() {
fr01();
en01();
}
function myFunction() {
var x = document.getElementById("myInput").value;
function 'x'
}
<input type="text" id="myInput" oninput="myFunction()">
<button onclick="myFunction01()">CHAPTER01</button>
the top part works perfectly fine and in realtime, when I press the corresponding button it shows both sides, when I try to type in number I get this error message
myFunction is not defined at HTMLInputElement.oninput
Didn't I already define it in the top portion?
Thank you for taking the time to read this.
Do call functions dynamically based on for example a text value, then you could create a map which connects a string to a function. An object is very similar to this. It has properties, which can be strings, and a value, which can be functions.
const actions = {
'foo': function() {
// Do something.
}
};
With this pattern you can create a list of valid options and the connect the corresponding functions that come with the list.
Now let's say you have a string. With that string you can select a function from the object with the bracket notation.
const string = 'foo';
const action = actions[action];
If a value is found, then action will now be a function. Otherwise it will be undefined.
The snippet below demonstrates this principle.
function omg() {
console.log('omg');
}
function bark() {
console.log('bark');
}
const actions = {
'0312': function() { // Regular function.
console.log('Hello')
},
'0420': () => { // Arrow function.
console.log('Wow')
},
'1360': () => console.log('Vroom'), // Arrow function variation.
'1337': () => { // Arrow function that calls two other functions.
omg();
bark();
},
'7331': [omg, bark] // Array with function references.
};
const form = document.querySelector('#form');
const actionField = document.querySelector('#action');
form.addEventListener('submit', event => {
const selector = actionField.value;
const action = actions[selector];
if (typeof action === 'function') {
action();
}
if (Array.isArray(action)) {
action.forEach(subAction => {
subAction()
});
}
event.preventDefault();
});
<form id="form">
<label for="action">Try me (0312, 0420, 1360 and 1337 will work)</label>
<input type="text" id="action" />
<button type="submit">Execute</button>
</form>
Related
I have tried to make an function with a onclick that when you click it, it will change the value from 'suspended' in true (this is about suspending a website user account)
suspended = false;
type user = User['suspended'];
function blokkeerFunctie() {
// get user & user element
document.getElementById('userInfo') && document.getElementById('blokkeren');
// blocks user when clicked
if (document.getElementById('blokkeer')?.addEventListener('click', blokkeerFunctie)) {
type user = !User['suspended'];
} else {
// deblocks user when clicked
document.getElementById('deblokkeer')?.addEventListener('click', blokkeerFunctie);
type user = User['suspended'];
}
console.log('blokkeerFunctie');
}
blokkeerFunctie();
I thought with !User i will reverse the boolean value from false in true, but that code isn't even read. ▼
'user' is declared but never used.ts(6196)
You shouldn't put event listeners in your conditional if/else in this way. Here's how I would approach what you're trying to accomplish. You will need to add types to these variables, but you'll get the basic logic here.
let User = {
suspended: true
};
let button = document.querySelector('#suspender');
function setSuspendButton() {
button.innerText = User['suspended'] ? 'Unsuspend' : 'Suspend';
}
window.addEventListener('load', () => {
button.addEventListener('click', blokkeerFunctie)
setSuspendButton();
})
function blokkeerFunctie() {
User['suspended'] = !User['suspended'];
setSuspendButton();
}
<button id='suspender'></button>
type user = creates a new type, not a value. It's unused in each branch of the if because you just create a new type, named user which shadows the type of the same name from the parent scope, which is never used by anything.
Furthermore, this line does nothing:
document.getElementById('userInfo') && document.getElementById('blokkeren');
This line gets up to two references to DOM elements, but doesn't save them anywhere, or perform any logic on them.
I think you want something more like this?
const user = {
name: 'Joe',
suspended: false
}
function blokkeerFunctie() {
// block/deblocks user when clicked
if (document.getElementById('blokkeer')?.addEventListener('click', blokkeerFunctie)) {
user.suspended = !user.suspended // toggle `suspended` back and forth
}
console.log('blokkeerFunctie');
}
blokkeerFunctie();
Working example
I'm new in javascript. I'm trying to create button like this.
<button class="btn btn-info fa fa-check"></button>
Here is my javascript function and it works
function createButton(buttonTypeClass, eventListener) {
const button = document.createElement("button");
button.classList.add(buttonTypeClass, "btn", "btn-info", "fa");
button.addEventListener("click", function(event) {
eventListener(event);
});
return button;
}
......
function createCheckButton() {
return createButton("fa-check", function(event) {
addBookToCompleted(event.target.parentElement);
});
}
But what I want defining class of btn-info (from createButton function) inside createCheckButton function coz I wanna make another style button (e.g. createTrashButton with class btn-danger).
function createCheckButton() {
return createButton("btn-info", "fa-check", function(event) {
addBookToCompleted(event.target.parentElement);
});
}
And it doesn't work well. It's only accept the first class (btn-info). How to fix this?
I would pass in an array of classes you want included on the button. This would be functionally correct.
function createButton(buttonTypeClasses, eventListener) {
const button = document.createElement("button");
buttonTypeClasses.forEach((class) => button.classList.add(class));
button.classList.add("btn", "fa");
button.addEventListener("click", function(event) {
eventListener(event);
});
return button;
}
function createCheckButton() {
return createButton(["btn-info", "fa-check"], function(event) {
addBookToCompleted(event.target.parentElement);
});
}
If you want to use the old function syntax, the forEach line would look like this.
buttonTypeClasses.forEach(function(class) { button.classList.add(class); });
It's probably because the function createButton() only accepts two params but instead you gave it three params. Try using two params for the class names and one more for the event like
createButton(btnClass, btnClass2, eventListener) {
const button = document.createElement("button");
// do your thing here
return button;
}
Another solution would be using the arguments array to pass a variable number of params
createButton() {
// arguments array contains all of the params you want to pass and its variable
// do your thing here
return button;
}
Here's an example I put together to explains arguments usage
function test() {
console.log(arguments) // prints all of the values you passed to it
}
test(1, 2, 3, 4)
I have this load-more listener on a button that calls the functions and it works fine.
let moviesPage = 1;
let seriesPage = 1;
document.getElementById('load-more').addEventListener('click', () => {
if (document.querySelector('#movies.active-link')) {
moviesPage++;
getMovies(moviesPage);
//getMovies(genreId, moviesPage);
} else if (document.querySelector('#series.active-link')) {
seriesPage++;
getSeries(seriesPage);
}
});
Now I have another listener on a list of links that calls the following code. It takes the genreId from the event parameter to sent as an argument to the api call. Also works fine so far.
document.querySelector('.dropdown-menu').addEventListener('click',
getByGenre);
function getByGenre (e) {
const genreId = e.target.dataset.genre;
movie.movieGenre(genreId)
.then(movieGenreRes => {
ui.printMovieByGenre(movieGenreRes);
})
.catch(err => console.log(err));
};
What I want to do is to call getByGenre from the load-more listener while passing also the moviesPage argument as you can see on the commented code so it can also be passed to the api call.
What would be the best way to do that? I've looked into .call() and .bind() but I'm not sure if it's the right direction to look at or even how to implement it in this situation.
Short Answer
Kludge: Global State
The simplest, though not the most elegant, way for you to solve this problem right now is by using some global state.
Take a global selection object that holds the selected genreId. Make sure you declare the object literal before using it anywhere.
So, your code might look something like so:
var selection = { };
document.querySelector('.dropdown-menu').addEventListener('click',
getByGenre);
function getByGenre (e) {
const genreId = e.target.dataset.genre;
selection.genreId = genreId;
movie.movieGenre(...);
};
...
let moviesPage = 1;
let seriesPage = 1;
document.getElementById('load-more').addEventListener('click', () => {
if (document.querySelector('#movies.active-link')) {
...
if (selection.genreId !== undefined) {
getMovies(selection.genreId, moviesPage);
}
} else if (...)) {
...
}
});
Closure
A more elegant way for you to accomplish this is by using a closure, but for that I have to know your code structure a bit more. For now, global state like the above will work for you.
Longer Answer
Your concerns have not been separated. You are mixing up more than one concern in your objects.
For e.g. to load more movies, in your load-more listener, you call a function named getMovies. However, from within the .dropdown-menu listener, you call into a movie object's method via the getByGenre method.
Ideally, you want to keep your UI concerns (such as selecting elements by using a query selector or reading data from elements) separate from your actual business objects. So, a more extensible model would have been like below:
var movies = {
get: function(howMany) {
if (howMany === undefined) {
howMany = defaultNumberOfMoviesToGetPerCall;
}
if (movies.genreId !== undefined) {
// get only those movies of the selected genre
} else {
// get all kinds of movies
}
},
genreId : undefined,
defaultNumberOfMoviesToGetPerCall: 25
};
document.get...('.load-more').addEventListener('whatever', (e) => {
var moviesArray = movies.get();
// do UI things with the moviesArray
});
document.get...('.dropdown-menu').addEventListener('whatever', (e) => {
movies.genreId = e.target.dataset.genreId;
var moviesArray = movies.get();
// do UI things with the moviesArray
});
I have form (in a ColdFusion program -- not that it matters) with two submit buttons (they look quite different from each other) which submits to different programs depending on which button is clicked. The form looks like this:
<form name = "pers3aa"
action = ""
method = "post"
onkeypress = "return event.keyCode != 13;"
onsubmit = "return subpers3aa('#coltop#');">
Each button calls a Javascript function to set the action -- they look like this:
<input type = "Submit"
name = "subpers4"
class = "submitbut"
value = "Submit"
onclick = "persaasubmit('person4aa.cfm')" >
and
<input type = "Submit"
id = "delbut"
class = "onoffbut"
value = "Delete this Value"
onclick = "persaasubmit('persdel.cfm')">
The javascript function persaasubmit is:
function persaasubmit(target) {
document.pers3aa.action = target;
}//end function persaasubmit
So far, so good -- this works. However, in another program I have a different form "xrefform", which I submit with a similar function:
function aasubmit(target) {
document.xrefform.action = target;
}//end function aasubmit
This situation occurs all throughout my system, and the various javascript submit functions are proliferating. So I tried to combine them by sending the form name as an argument:
function generalsubmit(formname, target) {
document.formname.action = target;
}//end function generalsubmit
This does not work. I infer that Javascript cannot find a form called "formname" so it doesn't submit anything. I'm thinking there probably is syntax for getting Javascript to submit the formname that was passed to it, but I don't know what that might be. Can anyone help?
In object document there is a 'forms' array. You can access a specific form :
document.forms['myform']
You're on the right track; however, that's not how dot notation works in JavaScript.
Consider this:
var someVar = "Foo Bar",
someObject = { someVar: someVar };
// someObject = {"someVar": "Foo Bar"}
someObject[someVar] == undefined
// Resolves to the 'Foo Bar' property in someObject
someObject.someVar == 'Foo Bar'
// Resolves to the 'someVar' property in someObject
someObject['someVar'] = 'Foo Bar'
// Resolves to t 'someVar' property in someObject
This should work as a drop-in replacement for the function in your question:
function generalsubmit(form, target) {
form = document.forms[form];
if (form) {
form.target = target;
}
}
I seem to have an issue when creating copies of a template and tying the .click() method to them properly. Take the following javascript for example:
function TestMethod() {
var test = Array();
test[0] = 0;
test[1] = 1;
test[2] = 2;
// Insert link into the page
$("#test_div").html("<br>");
var list;
for (x = 0; x < test.length; x++) {
var temp = $("#test_div").clone();
temp.find('a').html("Item #" + test[x]);
temp.click(function () { alert(x); });
if (list == undefined)
list = temp;
else
list = list.append(temp.contents());
}
$("#test_div2").append(list);
}
The problem I am seeing with this is that no matter which item the user clicks on, it always runs alert(2), even when you click on the first few items.
How can I get this to work?
Edit: I have made a very simple example that should show the problem much clearer. No matter what item you click on, it always shows an alert box with the number 2 on it.
Correct me if I'm wrong, .valueOf() in JS returns the primitive value of a Boolean object.....
this would not happen ShowObject(5,'T');... ShowObject(objectVal.valueOf(), 'T');
why not use objects[x].Value directly? ShowObject(objects[x].Value, 'T');
WOOOOOSSSHHHH!
after searching deeply... I found a solution...
because it's a closure, it won't really work that way...
here's a solution,
temp.find('a').bind('click', {testVal: x},function (e) {
alert(e.data.testVal);
return false;
});
for best explanation, please read this... in the middle part of the page where it says Passing Event Data a quick demo of above code
I think your issue arises from a misunderstanding of scopes in JavaScript. (My apologies if I'm wrong.)
function () {
for (...) {
var foo = ...;
$('<div>').click(function () { alert(foo); }).appendTo(...);
}
}
In JavaScript, only functions create a new scope (commonly referred to as a closure).
So, every round of the for loop will know the same foo, since its scope is the function, not the for. This also applies to the events being defined. By the end of looping, every click will know the same foo and know it to be the last value it was assigned.
To get around this, either create an inner closure with an immediately-executing, anonymous function:
function () {
for (...) {
(function (foo) {
$('<div>').click(function () { alert(foo); }).appendTo(...);
})(...);
}
}
Or, using a callback-based function, such as jQuery.each:
function () {
$.each(..., function (i, foo) {
$('<div>').click(function () { alert(foo); }).appendTo(...);
});
}
For your issue, I'd go with the latter (note the changes of objects[x] to just object):
var list;
jQuery.each(data.objects, function (x, object) {
// Clone the object list item template
var item = $("#object_item_list_template").clone();
// Setup the click action and inner text for the link tag in the template
var objectVal = object.Value;
item.find('a').click(function () { ShowObject(objectVal.valueOf(), 'T'); }).html(object.Text);
// add the html to the list
if (list == undefined)
list = item;
else
list.append(item.contents());
});