I need help to rewrite some arrow functions to regular functions but right now my brain is totally stucked, I have some example code here from a rock paper scissors game I got from kyle, how would it look if I was to ditch the arrow function please somebody?
selectionButtons.forEach(selectionButton => {
selectionButton.addEventListener('click', e => {
const selectionName = selectionButton.dataset.selection;
const selection = SELECTIONS.find(selection => selection.name === selectionName);
makeSelection(selection);
would it be as easy as?:
selectionButtons.forEach(selectionButton = function() {
or have I completely lost it?
The list of arguments goes inside the parenthesis.
You need an explicit return statement
function (selection) {
return selection.name === selectionName
}
This particular example doesn't use this but if it did it wouldn't be so simple to exchange a function expression for an arrow function (see this question).
// either:
selectionButtons.forEach(function (selectionButton) {
selectionButton.addEventListener('click', function (evt) {
// - what happens here should be prevented.
// - one really should avoid subscribing to each element
// its very own ad-hoc created handler function.
// - a shared reference of a single handler function
// is better in terms of resources and also of removing
// a listener again.
const selectionName = selectionButton.dataset.selection;
const selection = SELECTIONS.find(function (item) {
return item.name === selectionName;
});
makeSelection(selection);
});
});
// or:
function handleSelectionClick(evt) {
// access currently clicked selection button from event object.
const selectionButton = evt.currentTarget;
const selectionName = selectionButton.dataset.selection;
const selection = SELECTIONS.find(item =>
// an arrow function here is much more convenient
// than an anonymous function expression.
item.name === selectionName
);
makeSelection(selection);
}
function subscribeSelectionClickHandling(selectionButton) {
// shared reference of a one time implemented handler function.
selectionButton.addEventListener('click', handleSelectionClick);
}
selectionButtons.forEach(subscribeSelectionClickHandling);
Related
I have the following problem, I have a function that detects when a message has arrived, and when the message contains the prefix "#" for example "Hello I mean #LOL" it only gets the "LOL". And it works very well indeed, but now I want it to be able to detect not only the "#" prefix but also the "$" prefix for example $ MONEY. The first thing that occurred to me was to literally copy the entire function and declare for example prefixD = "$". The problem is that I have a lot of repeated / duplicated code, and I think it is not good to do it like that, what would be the correct way to do it? I leave my code here (Which works, but it has a lot of duplicate code)
client.on("message", function consigue(msg) {
const prefix = "#";
if (!msg.content.includes(prefix)) return;
const pattern = new RegExp(prefix + "([a-z]+)", "i");
const getMatch = (str) => str.match(pattern)?.[1];
TRADE_OUT = getMatch(msg.content);
if (TRADE_OUT != "") {
// some here
}
});
client.on("message", function consigueD(msg) {
const prefixD = "$";
if (!msg.content.includes(prefixD)) return;
function escapeRegex(string) {
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}
const pattern = new RegExp(escapeRegex(prefixD) + '([a-z]+)', 'i');
const getMatch = (str) => str.match(pattern)?.[1];
TRADE_OUT = getMatch(msg.content);
if (TRADE_OUT != "") {
// The same some here
}
});
For example I would like to replace that
if (TRADE_OUT != "") {
}
which is repeated in the two functions and is exactly the same code , and everything it has inside as it is done with the functions in the modules, which are declared only as name (); and they already execute all the code they have inside, and you can use it as many times as you want.
What would be the best way to do everything in the same function? I have tried with || , and in many other ways but the truth is I'm not very good with this topic
You could make a factory.
let handlerFactory = flag => {
return msg => {
const prefix = flag
...
}
}
Then you would do
client.on("message", handlerFactory("X"))
where X is either "$" or "#".
EDIT:
If you meant "just the part within the TRADE_OUT-bit is a duplicate" then just take that, put it in a function declared before the handlers and call that from within the TRADE_OUT-bit.
The reason I say language agnostic is that I would like a small, self contained implementation that determines if the outermost scope in a string containing JavaScript is a function or not. I've looked at the MDN for guidance on the possible forms of declaring functions, but unfortunately wasn't able to find any comprehensive examples for all the ways functions can be defined in JS.
Here's a few different test cases the implementation should be able to handle:
// yes
function (){}
//yes
() => p
//yes
((g) => p)
//yes
(g => p)
//no
(()=>p)()).call()
//no
a = function (){
console.log()
{
//no
g=() => p
I've thought about trying to construct a regex to look for this, but I'm not sure I've covered every case, and I haven't found any sort of corpus of example JS functions, or even if a pure regex approach would be the best way to handle this.
You can use Acorn to parse the Javascript. If parsing is successful, and the body is composed only of a single item, and that item is a FunctionDeclaration or ArrowFunctionExpression, the test passes:
const test = (str) => {
console.log(str);
try {
const { body } = acorn.parse(str);
if (body.length > 1) throw new Error();
const [item] = body;
if (item.type === 'FunctionDeclaration' || (item.type === 'ExpressionStatement' && item.expression.type === 'ArrowFunctionExpression')) {
console.log('Pass');
} else {
console.log('invalid');
}
} catch(e) {
console.log('invalid');
}
};
// function (){}
test(`function foo() {}`);
test(`() => p`);
test(`((g) => p)`);
test(`(g => p)`);
test(`(()=>p)()).call()`);
test(`a = function (){
console.log()
{`);
test(`g=() => p`);
<script src="https://cdnjs.cloudflare.com/ajax/libs/acorn/7.1.1/acorn.min.js"></script>
I simply tried to addEventListener and removeEventListener to element, but it doesn't remove.
I suppose that the problem could be with parameters, but I used them to follow the DRY. So I could simply reuse it like nextSection.addEventListener('mouseover', showContent(event, nextSection)) and so on and so on so I do not need any if statements or stuff like that.
* EDIT *
I made some more examples of elements that I will be using. There’s a chance, that there will be event more. If I do not use parameter, there would be a lot more of functions. Also, there will be click instead of mouse events on mobile, so I need to remove them.
As I understand now, the problem is with return statement. If I use event instead of parameter and so event.target I get some weird bug.
const loginSection = document.querySelector('#js-login-section');
const searchSection = document.querySelector('#js-search-section');
const shoppingBagSection = document.querySelector('#js-shopping-bag-section');
const wishlistSection = document.querySelector('#js-wishlist-section');
function showContent(element) {
return () => {
const toggle = element.lastElementChild;
toggle.style.maxHeight = toggle.scrollHeight + 'px';
}
}
function hideContent(element) {
return () => {
const toggle = element.lastElementChild;
toggle.style.maxHeight = null;
}
}
/* Media queries - min width 992px */
loginSection.addEventListener('mouseover', showContent(loginSection));
loginSection.addEventListener('mouseout', hideContent(loginSection));
searchSection.addEventListener('mouseover', showContent(searchSection));
searchSection.addEventListener('mouseout', hideContent(searchSection));
shoppingBagSection.addEventListener('mouseover', showContent(shoppingBagSection));
shoppingBagSection.addEventListener('mouseout', hideContent(shoppingBagSection));
wishlistSection.addEventListener('mouseover', showContent(wishlistSection));
wishlistSection.addEventListener('mouseout', hideContent(wishlistSection));
/* Media queries - max width 992px */
loginSection.removeEventListener('mouseover', showContent(loginSection));
loginSection.removeEventListener('mouseout', hideContent(loginSection));
searchSection.removeEventListener('mouseover', showContent(searchSection));
searchSection.removeEventListener('mouseout', hideContent(searchSection));
shoppingBagSection.removeEventListener('mouseover', showContent(shoppingBagSection));
shoppingBagSection.removeEventListener('mouseout', hideContent(shoppingBagSection));
wishlistSection.removeEventListener('mouseover', showContent(wishlistSection));
wishlistSection.removeEventListener('mouseout', hideContent(wishlistSection));
Thank you in advance!
What is happening is that return () => {}; is returning a new function every time it's run. So every time you call one of your functions a new event handler is being created.
This means that the handler that is added is different to the one you're trying to remove.
To remedy this, I'd keep it simple:
const loginSection = document.querySelector('#js-login-section');
function showContent(e)
{
const toggle = e.currentTarget.lastElementChild;
toggle.style.maxHeight = toggle.scrollHeight + 'px';
}
function hideContent(e)
{
const toggle = e.currentTarget.lastElementChild;
toggle.style.maxHeight = null;
}
loginSection.addEventListener('mouseover', showContent);
loginSection.addEventListener('mouseout', hideContent);
loginSection.removeEventListener('mouseover', showContent);
loginSection.removeEventListener('mouseout', hideContent);
I'm not sure what you want to avoid repeating, so I can't advise on that, but I'm sure you'll figure it out.
const loginSection = document.querySelector('#js-login-section');
function showContent(event) {
var element = event.target;
return () => {
const toggle = element.lastElementChild;
toggle.style.maxHeight = toggle.scrollHeight + 'px';
}
}
function hideContent(event) {
var element = event.target;
return () => {
const toggle = element.lastElementChild;
toggle.style.maxHeight = null;
}
}
loginSection.addEventListener('mouseover', showContent);
loginSection.addEventListener('mouseout', hideContent);
loginSection.removeEventListener('mouseover', showContent);
loginSection.removeEventListener('mouseout', hideContent);
You must set in events method function without call. Element you can get from event event.target
In your code, I found the following errors,
param 'event' will be always undefined - the event should go as a parameter to inner function.
you don't need closure here - You can directly assign the function without creating an inner function and access the element with event.target or this
with your implementation, you should pass the same handler reference used in addEventListener to removeEventListener. So, you should store the handler in a variable and pass it to both addEventListener and removeEventListener
Solution:
if you don't know the handler name, you can use window.getEventListeners to do the magic,
window.getEventListeners returns a dictionary of events associated with the element.
function removeEventListener(el, eventName) {
if (!el) {
throw new Error('Invalid DOM reference passed');
}
const listeners = getEventListeners(el)[eventName] || [];
listeners.forEach(({
listener
}) => {
removeEventListener(eventName, listener);
});
}
function removeAllEventListener(el) {
if (!el) {
throw new Error('Invalid DOM reference passed');
}
const events = Object.entries(getEventListeners(el) || {});
events.forEach(([eventName, listeners]) => {
listeners.forEach(({
listener
}) => {
removeEventListener(eventName, listener);
});
});
}
// example
// remove mouseout event
removeEventListener(loginSection, 'mouseout');
// remove all event listeners
removeAllEventListener(loginSection);
Why do I always get the last value assigned to the variable
even though I already enclosed it in a function?
When the event mouse up is triggered and getGoogleFiles is called, the last value assigned to resourceId is called. I don't get it.
for ( var i in arrayObj) {
var resourceId = arrayObj[i].ResourceId;
entity_list.onmouseup = function(event) {
parent.changeButtonState(this, event);
(function(resourceId) {
getGoogleFiles(resourceId);
})(resourceId);
}
}
Note: This is different to other JavaScript questions because the onmouseup is not triggered
I followed the creating of another function mentioned here:
JavaScript closure inside loops – simple practical example
for ( var i in arrayObj) {
entity_list.onmouseup = function(event) {
parent.changeButtonState(this, event);
testing(arrayObj[i].ResourceId);
}
}
function testing(index){
return function() { getGoogleFiles(index); };
}
But when the element of "entity_list" is triggered, nothing happens.
I can't use let because the specific browser that I'm using returns a SyntaxError
SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
Thank you!
You need to use testing() to create the listener function, not something you call inside it.
for (var i in arrayObj) {
entity_list.onmouseup = testing(arrayObj[i].ResourceId, parent);
}
function testing(index, parent) {
return function(event) {
parent.changeButtonState(this, event);
getGoogleFiles(index);
};
}
But you wouldn't have to go through any of this if you use forEach() instead of a for loop, since it creates a new scope for obj in each iteration.
arrayObj.forEach(function(obj) {
entity_list.onmouseup = function(event) {
parent.changeButtonState(this, event);
testing(obj.ResourceId);
}
});
You can't use a var scoped variable here.
But you could assign the resourceId to a data attribute on the relative html element, so you can read it when the event fires.
var arrayObj = [{ResourceId: "test1"}, {ResourceId: "test2"}, {ResourceId: "test3"}];
var entity_list = document.getElementsByClassName("entity_list");
for ( var i in arrayObj) {
entity_list[i].dataset.resourceId = arrayObj[i].ResourceId;
entity_list[i].onmouseup = function(event) {
getGoogleFiles(this.dataset.resourceId);
}
}
function getGoogleFiles(resourceId) {
console.log(resourceId);
}
<span class="entity_list">entity list (item 1)</span>
<span class="entity_list">entity list (item 2)</span>
<span class="entity_list">entity list (item 3)</span>
I am tackling a challenge, I am very new to JS and quite frankly I do not completely understand the challenge.
The question I need to answer to move to the next stage is:
Step 2 Create the following 3 functions:
a displayBirthdate arrow function a displayPhone arrow function and a
displayAddress arrow function These functions will be called by event
listeners attached to buttons in the UI.
Step 3 Create a displayExtraUserInfo arrow function. It should take in
a single parameter. It should then :
Add a click listener to the BUTTON with id of btn-birthdate. The
listener should make a call to displayBirthdate and pass in the
parameter displayExtraUserInfo received
Add a click listener to the BUTTON with id of btn-phone. The listener
should make a call to displayPhone and pass in the parameter
displayExtraUserInfo received
Add a click listener to the BUTTON with id of btn-address. The
listener should make a call to displayAddress and pass in the
parameter displayExtraUserInfo received
For the next 3 steps, be sure you have covered Step 1 above, then
review
https://simonsmith.io/destructuring-objects-as-function-parameters-in-es6
for a primer on ES6 destructuring
Step 4 Locate the displayBirthdate function you initially defined,
which took no parameter. Modify it to use object de-structuring to get
just the dob property of the parameter object it will receive.
Step 5 Like in step 4, locate the displayAddress function and
de-structure its parameter to get just the location property
Step 6 Like in step 4, locate the displayPhone function and
de-structure its parameter to get just the phone and cell properties
Here's my code:
var displayBirthdate = ({dob = 'DOB'}) => {};
var displayPhone = ({location = 'location'}) => {};
var displayAddress = ({phone = 'phone', cell='cell'}) =>{};
var displayExtraUserInfo = (params) => {
document.getElementById("btn-birthdate").addEventListener("click", function(){ displayBirthdate(params)});
}
However, I am not getting it right, is there a problem with my logic? or code? i cannot tell what i am doing wrong.
What I get from you question is that you want to call displayBirthdate on click and want to pass the params passed in displayExtraUserInfo to displayBirthdate
var displayBirthdate = (params) => (console.log(params));
var displayPhone = ({location = 'location'}) => ({});
var displayAddress = ({phone = 'phone', cell='cell'}) =>({});
var displayExtraUserInfo = (params) => {
document.getElementById("btn-birthdate").addEventListener("click",
function(){
displayBirthdate(params)
});
}
displayExtraUserInfo('some data')
<button id="btn-birthdate">click me</button>
const displayExtraUserInfo = (params) => {
document.getElementById("btn-birthdate").addEventListener("click", ()=> {
displayBirthdate(params)
})
document.getElementById("btn-phone").addEventListener("click", () => {
displayPhone(params)
})
document.getElementById("btn-address").addEventListener("click", () => {
displayAddress(params)
})
}
This is what you are looking for (Future Andelean) #winks
This is what you are looking for (Future Andelean) #winks
const displayBirthdate = ({dob})=>{
//
}
const displayPhone = ({phone, cell})=>{
//
}
const displayAddress = ({location})=>{
//
}
const displayExtraUserInfo = (param) => {
document.querySelector('#btn-birthdate').addEventListener('click', () => {
displayBirthdate(param);
});
document.querySelector('#btn-phone').addEventListener('click', () => {
displayPhone(param);
});
document.querySelector('#btn-address').addEventListener('click', () => {
displayAddress(param);
});
};```
This should solve it cornelking
const displayExtraUserInfo = (param) => {
document.querySelector('#btn-birthdate').addEventListener('click', () => {
displayBirthdate(param);
});
};