JS Ojbect Event onkeyup - javascript

I have a module like this:
let Search = {
settings: {
inputField: document.getElementById('search_field')
},
init: function() {
this.bindAction();
},
bindAction: function() {
this.settings.inputField.addEventListener("onkeyup", function(e) {
let value = this.settings.inputField.value;
console.log(value);
e.preventDefault();
})
}
};
export default Search;
And I import it into my main app like so:
import Search from './components/Search';
Search.init();
But the onkeyup event doesn't fire.
What am I doing wrong?

There is no such event name as onkeyup, so the listener doesn't fire. The event's name is keyup:
this.settings.inputField.addEventListener("keyup", function(e) {
You can use on when you're assigning a listener by assigning to a listener property using dot notation, for example:
this.settings.inputField.onkeyup = function(e) {
When using addEventListener, never prefix the event name with on - when assigning to a property, always prefix the event name with on.
The other problem is that your calling context is wrong for the listener - inside the listener, this will refer to the element, not to the Search object. Use an arrow function instead, so that the this of the parent block will be inherited:
this.settings.inputField.addEventListener("keyup", (e) => {

Related

Lit element, close drop down when click outside of component

I'm working with Lit Element and I'm trying add an event listener on 'Click' that will a variable state that will set the dropdown to be expand or not. But once the drop down is 'closed' I want to remove that event to avoid unnecessary event calls on 'Click.
Adding the event works great but I cannot remove it.
Here is the idea:
public willUpdate(changedProps: PropertyValues) {
super.willUpdate(changedProps);
if (changedProps.has("_tenantsExpanded")) {
document.removeEventListener("click", (ev) => this._eventLogic(ev, this));
if (this._tenantsExpanded)
document.addEventListener("click", (ev) => this._eventLogic(ev, this));
}
}
The fct logic:
private _eventLogic(e: MouseEvent, component: this) {
const targets = e.composedPath() as Element[];
if (!targets.some((target) => target.className?.includes("tenant"))) {
component._tenantsExpanded = false;
}
}
Code in my render's function:
${this._tenantsExpanded
? html` <div class="tenants-content">${this._tenantsContent()}</div> `
: html``}
Important note: I want the click event to be listened on all the window, not just the component itself. The same for removing the event.
PS: I don't know why e.currentTaget.className doesn't give me the actual className, but results to an undefined.
When you use removeEventListener you have to pass a reference to the same function you used when adding the listener.
In this example the function is stored in fn.
(You might have to change the this reference here, it depends a bit on your whole component).
const fn = (ev) => this._eventLogic(ev, this);
document.addEventListener("click", fn);
document.removeEventListener("click", fn);

Did I convert my Jquery event listener into Javascript properly? I don't understand how the Javascript works without targeting the id?

I'm trying to convert jquery into javascript. My app is a simple to do list and I'm targeting a button with an id called #clear-completed. Whenever I click that button on my app, it deletes the completed todo items, but I don't understand where it is being targeting in my new Javascript code.
Here is the original Jquery code
$('#footer').on('click', '#clear-completed', this.destroyCompleted.bind(this));
So I changed it to Javascript and this code worked
var footer = document.getElementById('footer');
footer.addEventListener('click', this.destroyCompleted.bind(this))
What I don't understand is what happened the the #clear-completed id and how does my new javascript code still work, even though I am not specifying to target the #clear-completed button?
Here is the code for the destroyCompleted function
destroyCompleted: function () {
this.todos = this.getActiveTodos();
this.filter = 'all';
this.render();
},
In the debugger it runs through the activeTodos function, but I don't see anywhere where the id #clear-completed is targeted?
getActiveTodos: function () {
return this.todos.filter(function (todo) {
return !todo.completed;
});
},
getCompletedTodos: function () {
return this.todos.filter(function (todo) {
return todo.completed;
});
},
Did I write my Jquery into Javascript properly? Or did I miss something?
Also, if the id had more than one event listener how would you code that properly? for example
$('#todo-list')
.on('change', '.toggle', this.toggle.bind(this))
.on('dblclick', 'label', this.edit.bind(this))
.on('keyup', '.edit', this.editKeyup.bind(this))
.on('focusout', '.edit', this.update.bind(this))
.on('click', '.destroy', this.destroy.bind(this));
The equivalent JavaScript would be:
document.querySelector('#footer').addEventListener('click', event => {
const element = event.target.closest('#clear-completed');
if (
event.currentTarget !== element &&
event.currentTarget.contains(element)
) {
this.destroyCompleted(event);
}
});
The signature $(target).on(event, selector, handler) that you're using is called a delegated event handler, so the handler is invoked on the target element as you have correctly reproduced, but it is only invoked when the event targets an element matching selector which is descendant of target, not including target itself.
Matching the selector is reproduced above by checking that event.currentTarget .contains() the element returned by event.target .closest(selector).
You could even break this logic out into a helper function to make it more readable:
document.querySelector('#footer').addEventListener('click', event => {
const matches = selector => {
const element = event.target.closest(selector);
return (
event.currentTarget !== element &&
event.currentTarget.contains(element)
);
};
if (matches('#clear-completed')) {
this.destroyCompleted(event);
}
});
Since you need this pattern multiple times, it makes sense to move it into another reusable function:
function delegate (target, type, selector, handler) {
const matches = event => {
const element = event.target.closest(selector);
return (
event.currentTarget !== element &&
event.currentTarget.contains(element)
);
};
target.addEventListener(type, event => {
if (matches(event)) {
handler(event);
}
});
}
const element = document.querySelector('#todo-list');
delegate(element, 'change', '.toggle', e => this.toggle(e));
delegate(element, 'dblclick', 'label', e => this.edit(e));
delegate(element, 'keyup', '.edit', e => this.editKeyup(e));
delegate(element, 'focusout', '.edit', e => this.update(e));
delegate(element, 'click', '.destroy', e => this.destroy(e));
You targeted '#clear-completed' in the jquery function by passing the argument,'#clear-completed', to your on event handler. Jquery on says:
https://api.jquery.com/on/
.on( events [, selector ] [, data ], handler )
selector
Type: String
A selector string to filter the descendants of the selected elements that trigger the event. If the selector is null or omitted, the event is always triggered when it reaches the selected element.

How to bypass Bound Javascript OnChange Functions

I have a .js File that bind several OnChange Events to my Controls.
Now i have a function() that is changing all of these Controls...
My Issue is that now all the onchange events are fireing...
What options do I have around this , without unbinding everything all of my onChange Functions?
Sample Code:
$('#Sections,#Sections2,#Sections3,#Sections,4').bind('change', function () {
SomeAwesomeFunctionUnrelatedToTheOneBelow('CUSTOM');
});
Then later from another ( change function )
function SetText(name, value) {
if (value != null)
{ $('#' + name).val(value); }
else
{ $('#' + name).val(''); }
}
After this executres all 4 onchange functions are run ... i need a way around this.
Use jQuery's Event Namespacing to mark each event with a namespace that will later let you un-bind (or trigger) only the change event of that specific namespace.
Binding:
// binding a 'change' event with 'ns1' namespace
$('#some_element').bind("change.ns1", function() { ... })
// binding a 'change' event with 'ns2' namespace
$('#some_element').bind("change.ns2", function() { ... })
Un-Binding:
// un-binding only the 'change' event with 'ns1' namespace
$('#some_element').unbind("change.ns1")
// un-binding only the 'change' event with 'ns2' namespace
$('#some_element').unbind("change.ns2")

Binding a function that is already bound to another element

I have a bunch of elements that get three different classes: neutral, markedV and markedX. When a user clicks one of these elements, the classes toggle once: neutral -> markedV -> markedX -> neutral. Every click will switch the class and execute a function.
$(document).ready(function(){
$(".neutral").click(function markV(event) {
alert("Good!");
$(this).addClass("markedV").removeClass("neutral");
$(this).unbind("click");
$(this).click(markX(event));
});
$(".markedV").click(function markX(event) {
alert("Bad!");
$(this).addClass("markedX").removeClass("markedV");
$(this).unbind("click");
$(this).click(neutral(event));
});
$(".markedX").click(function neutral(event) {
alert("Ok!");
$(this).addClass("neutral").removeClass("markedX");
$(this).unbind("click");
$(this).click(markV(event));
});
});
But obviously this doesn't work. I think I have three obstacles:
How to properly bind the changing element to the already defined function, sometimes before it's actually defined?
How to make sure to pass the event to the newly bound function [I guess it's NOT accomplished by sending 'event' to the function like in markX(event)]
The whole thing looks repetitive, the only thing that's changing is the alert action (Though each function will act differently, not necessarily alert). Is there a more elegant solution to this?
There's no need to constantly bind and unbind the event handler.
You should have one handler for all these options:
$(document).ready(function() {
var classes = ['neutral', 'markedV', 'markedX'],
methods = {
neutral: function (e) { alert('Good!') },
markedV: function (e) { alert('Bad!') },
markedX: function (e) { alert('Ok!') },
};
$( '.' + classes.join(',.') ).click(function (e) {
var $this = $(this);
$.each(classes, function (i, v) {
if ( $this.hasClass(v) ) {
methods[v].call(this, e);
$this.removeClass(v).addClass( classes[i + 1] || classes[0] );
return false;
}
});
});
});
Here's the fiddle: http://jsfiddle.net/m3CyX/
For such cases you need to attach the event to a higher parent and Delegate the event .
Remember that events are attached to the Elements and not to the classes.
Try this approach
$(document).ready(function () {
$(document).on('click', function (e) {
var $target = e.target;
if ($target.hasClass('markedV')) {
alert("Good!");
$target.addClass("markedV").removeClass("neutral");
} else if ($target.hasClass('markedV')) {
alert("Bad!");
$target.addClass("markedX").removeClass("markedV");
} else if ($target.hasClass('markedX')) {
alert("Ok!");
$target.addClass("neutral").removeClass("markedX");
}
});
});
OR as #Bergi Suggested
$(document).ready(function () {
$(document).on('click', 'markedV',function (e) {
alert("Good!");
$(this).addClass("markedV").removeClass("neutral");
});
$(document).on('click', 'markedX',function (e) {
alert("Bad!");
$(this).addClass("markedX").removeClass("markedV");
});
$(document).on('click', 'neutral',function (e) {
alert("Ok!");
$(this).addClass("neutral").removeClass("markedX");
});
});
Here document can be replaced with any static parent container..
How to properly bind the changing element to the already defined function, sometimes before it's actually defined?
You don't bind elements to functions, you bind handler functions to events on elements. You can't use a function before it is defined (yet you might use a function above the location in the code where it was declared - called "hoisting").
How to make sure to pass the event to the newly bound function [I guess it's NOT accomplished by sending 'event' to the function like in markX(event)]
That is what happens implicitly when the handler is called. You only need to pass the function - do not call it! Yet your problem is that you cannot access the named function expressions from outside.
The whole thing looks repetitive, the only thing that's changing is the alert action (Though each function will act differently, not necessarily alert). Is there a more elegant solution to this?
Yes. Use only one handler, and decide dynamically what to do in the current state. Do not steadily bind and unbind handlers. Or use event delegation.

Mootools class binds with element as parameter

Is it possible to send over the target of an event bound to a mootools class with Binds?
ie:
checkbox.addEvent('change', this.checkBoxChangeAgain(this));
Where this == checkbox
ps. This doesn't work:
checkbox.addEvent('change', this.checkBoxChangeAgain(checkbox));
the reason why it does not work is that by doing method(this) you actually invoke it immediately. method.bind(checkbox) will decorate the function and change the scope to be the checkbox when it does get called later.
why not proxy it?
var self = this;
checkbox.addEvent('change', function(e) {
self.checkBoxChangeAgain(this);
});
new Class({
checkBoxChangeAgain: function(checkbox) {
this; // instance
checkbox; // == org checkbox
}
});
by default the first argument to an event handler will be the event and the scope will be the trigger element.
hence:
checkbox.addEvent('change', this.checkBoxChangeAgain);
will mean that:
new Class({
checkBoxChangeAgain: function(event) {
this === event.target;
}
});
which means you can also:
checkbox.addEvent('change', this.checkBoxChangeAgain.bind(this));
and that will work out as:
new Class({
checkBoxChangeAgain: function(event) {
this != event.target; // true
event.target === checkbox; // true
this; // the class instance
}
});
I hope this gives you ideas. Also, search for bindWithEvent here on SO - in particular, replacement for bind with event.
Similar to how Dimitar's answer works, but using Class.Binds, the easiest way is to use .pass() http://mootools.net/docs/core/Types/Function#Function:pass
this.checkBoxChangeAgain.pass(checkbox)

Categories