How to add event handlers to HTML buttons from within javascript code - javascript-events

I have a list of buttons (squares) in HTML like this -
<td><button id="18" ></button></td>
<td><button id="28" ></button></td>
<td><button id="38" ></button></td>
...
Earlier, for each button, I had to put code within the button tag itself to add an event handler, like this -
<button id="18" onclick="squareWasClicked(event)">
function squareWasClicked(event)
{
var element = event.target;
// more code
}
But now I have found out that it is not good practice. So instead, I am trying to add event handlers from within my javascript code. But I don't know how to do that. So far, I have tried this -
function assignEventsToSquares()
{
var i,j;
for(i=1;i<=8;i++)
{
for(j=1;j<=8;j++)
{
var squareID = "" + i + "" + j;
var square = document.getElementById(squareID);
square.onclick = squareWasClicked();
}
}
}
But this simply calls the squareWasClicked() function. So how do I add it as an event handler so that the function will be invoked when the square is clicked? Also, how do I say something like
square.onclick = squareWasClicked(event);
event is not detected in the JavaScript code. Please help.

Use element.addEventListener() (originating from the DOM 2 Events spec). In your case it will be
document.getElementById("18").addEventListener("click", squareWasClicked, false);

square.onclick = squareWasClicked();
This calls your click function and assigns the result to your element's event listener, which isn't what you want. Remove the ()s so that the function itself gets assigned.
Hence, it should look like this
square.onclick = squareWasClicked;

Related

reactJS - Adding an event handler to an element which might, or might not exist at this time

I am not allowed to answer questions yet, but I feel that since this issue has taken me some days to resolve, I should post the solution the only way I can - as a question.
If you have a better solution then please include that in your reply.
Until an element is rendered it is not in the DOM so if you add an event listener to the element in your code you will get an error (element value null).
But you can add a listener to the root element, which is always there. WHen the event is triggered you can then retrieve the className and ID of the element involved in your event-handler.
Code:
var rootElement = document.getElementById('root');
console.log(rootElement);
rootElement.addEventListener('click', rootElementClicked);
console.log('event listener added to root element');
function rootElementClicked(event) {
event.preventDefault();
const { name, value } = event.target;
console.log("Root element clicked with [" + event.target.className, event.target.id);
}
/code
So, the event-listener is app-wide, so a click anywhere will call the event-handler function. Then in the code for that function, the element class and ID will tell you what was clicked.
Note the event.preventDefault(); line - it prevents a refresh of the web page, otherwise the target class & ID are returned as "undefined"
In React you shouldn't need to addEventListener manually since you can use onClick. However if you would like to manually attach click handler on an HTML element in React you can use React Ref. If you pass a ref object to React with <div ref={myRef} /> React will set its .current property to the corresponding DOM node whenever that node changes.
Example: https://codesandbox.io/s/react-hooks-useref-xfvlb
const App = () => {
const elementRef = useRef();
useEffect(() => {
elementRef.current.addEventListener('click', handleOnClick);
},[elementRef])
const handleOnClick = () => {
alert("click")
}
return (
<div className="App">
<div ref={elementRef}>
click on me
</div>
</div>
);
}

Knockout.JS: How to fire event inside a component and consume it

In the file "KoComponents.js", I wrote the following component
ko.components.register('pager-navigator', {
viewModel: function (params) {
var self = this;
self.GoToPageNumber = function (pageNo) {
// Raise event and with the value of the parameter pageNo.
alert('Button clicked with parameter (pageNo) = ' + pageNo);
}
},
template: '<input type="button" value="Click me and raise event" data-bind="click: function(){GoToPageNumber(1);}"/>'
});
In the view, I wrote the following to consume the created component.
<div data-bind='component: {name: "pager-navigator", params: { TotalPagesCount: 20 }}'></div>
My question is: How can I fire event in the component with parameters and consume that event from the view?
A good way to structure your code to share an observable variable between your parent page, and the component. It would be passed in with the params.
So if your parent VM had an observable called "pageNum" then make sure your component does too, and then pass the observable in to the component to link them.
This way, if the component changes the pageNum value then the observable in the parent will also change. So you can subscribe to the variable in the parent, and if it changes, you can execute some code. Essentially you are left with a situation where if the component changes the pageNum, the parent will know and can act accordingly.
It might sound long-winded, but it's a really clean solution, and it really helps you break problems down into sections and cut down on strong couplings.
Hopefully that explanation makes sense, but if you need a fiddle to demonstrate then let me know.
This is the answer:
Create an observable parameter in the page named for example "CurrentPageNumber".
Pass the observable parameter to the component.
In the component, you can change the value of the passed parameter. (act as passing by reference).
In the view, you'll need to add a subscribe method and handle your custom action when the value of CurrentPageNumber changed.
Check the sample code below.
ko.components.register('pager-navigator', {
viewModel: function (params) {
var self = this;
self.PageNumber = params.pageNumber;// pageNumber is an observable passed parameter.
self.GoToPageNumber = function (pageNo) {
self.PageNumber(pageNo);// Act as passing by reference.
}
},
template: '<input type="button" value="Click me and raise event" data-bind="click: function(){GoToPageNumber(2);}"/>'
});
In the view, write:
<div data-bind='component: {name: "pager-navigator", params: { totalPagesCount: totalPagesCount(), pageNumber: CurrentPageNumber}}'></div>
In the View JavaScript, write the following:
var self = this;
// Declare an observable variable named CurrentPageNumber with the value 1.
self.CurrentPageNumber = ko.observable(1);
self.CurrentPageNumber.subscribe(function (newValue) {
// The value of CurrentPageNumber is changed inside the component.
var newPageNo = newValue;
alert('value changed = ' + newPageNo);
self.SearchEmployees(newPageNo);
});

Add or trigger event after inner content to page

I have links on a table to edit or delete elements, that elements can be filtered. I filtered and get the result using ajax and get functions. After that I added (display) the result on the table using inner.html, the issue here is that after filtering the links on the elements not work, cause a have the dojo function like this
dojo.ready(function(){
dojo.query(".delete-link").onclick(function(el){
var rowToDelete = dojo.attr(this,"name");
if(confirm("Really delete?")){
.......
}
});
I need to trigger the event after filtering, any idea?
(I'm assuming that you're using Dojo <= 1.5 here.)
The quick answer is that you need to extract the code in your dojo.ready into a separate function, and call that function at the end of your Ajax call's load() callback. For example, make a function like this:
var attachDeleteEvents = function()
dojo.query(".delete-link").onclick(function(el){
var rowToDelete = dojo.attr(this,"name");
if(confirm("Really delete?")){
.......
}
});
};
Then you call this function both in dojo.ready and when your Ajax call completes:
dojo.ready(function() { attachDeleteEvents(); });
....
var filter = function(someFilter) {
dojo.xhrGet({
url: "some/url.html?filter=someFilter",
handleAs: "text",
load: function(newRows) {
getTableBody().innerHTML = newRows;
attachDeleteEvents();
}
});
};
That was the quick answer. Another thing that you may want to look into is event delegation. What happens in the code above is that every row gets an onclick event handler. You could just as well have a single event handler on the table itself. That would mean there would be no need to reattach event handlers to the new rows when you filter the table.
In recent versions of Dojo, you could get some help from dojo/on - something along the lines of:
require(["dojo/on"], function(on) {
on(document.getElementById("theTableBody"), "a:click", function(evt) {...});
This would be a single event handler on the whole table body, but your event listener would only be called for clicks on the <a> element.
Because (I'm assuming) you're using 1.5 or below, you'll have to do it a bit differently. We'll still only get one event listener for the whole table body, but we have to make sure we only act on clicks on the <a> (or a child element) ourselves.
dojo.connect(tableBody, "click", function(evt) {
var a = null, name = null;
// Bubble up the DOM to find the actual link element (which
// has the data attribute), because the evt.target may be a
// child element (e.g. the span). We also guard against
// bubbling beyond the table body itself.
for(a = evt.target;
a != tableBody && a.nodeName !== "A";
a = a.parentNode);
name = dojo.attr(a, "data-yourapp-name");
if(name && confirm("Really delete " + name + "?")) {
alert("Will delete " + name);
}
});
Example: http://fiddle.jshell.net/qCZhs/1/

Edit button with comments using MooTools/AJAX

So I'm using a PHP API to interact with, to build a forum using MooTools. I can get comments from my database and add comments, but I want to inject an edit button to coincide with each comment.
I inject the comments using:
function domReady() {
$('newComment').addEvent('submit', addComment);
}
function addComment(e){
e.stop();
var req = new Request({
url:'control.php?action=insertPost',
onSuccess:addajaxSuccess
}).post(this);
}
function addajaxSuccess(idNo) {
new Element('span',{
'text':'Post successful.'
}).inject($(newComment));
$('commentList').empty();
domReady();
}
I want to attach an edit button to each comment injected, and add an event listener on the button to change the comment into a textarea for editting, with an update button.
Any ideas?
If you want to bind a global events to a dynamic content you have better look into Element Delegation In mootools.
Basically it's give you the ability to bind event to some container and "listen" to events of that children container base on selectors. I made you a little example here:
http://jsfiddle.net/xwpmv/
mainContainer.addEvents({
'click:relay(.mt-btn)': function (event, target) {
var btn = target;
if(btn.get('value') == 'Edit'){
btn.set('value','Done Editing');
var content = btn.getPrevious();
content.setStyle('display','none');
var textarea = new Element('textarea').set('text',content.get('text'));
textarea.inject(btn,'before');
}
else{
btn.set('value','Edit');
var textarea = btn.getPrevious();
var new_value = textarea.get('value');
textarea.destroy();
var content = btn.getPrevious();
content.set('text',new_value);
content.setStyle('display','block');
}
}
});
Here you can see the mainContainer listen to the click event of every element who has mt-btn class (the buttons)
You have several errors in your code but maybe it is just an example so I didn't relate to it.

jQuery .on() event doesn't work for dynamically added element

I'm making a project where a whole div with buttons is being inserted dynamically when user click a button, and inside that div there's a button, which when the user click on it, it does something else, like alerting something for example.
The problem is when i press on that button in the dynamically added div, nothing happens. The event doesn't fire at all.
I tried to add that div inside the HTML and try again, the event worked. So i guess it's because the div is dynamically added.
The added div has a class mainTaskWrapper, and the button has a class checkButton.
The event is attached using .on() at the end of script.js file below.
Here's my code :
helper_main_task.js : (that's the object that adds the div, you don't have to read it, as i think it's all about that div being dynamically added, but i'll put it in case you needed to)
var MainUtil = {
tasksArr : [],
catArr : ["all"],
add : function(){
var MTLabel = $("#mainTaskInput").val(), //task label
MTCategory = $("#mainCatInput").val(), //task category
MTPriority = $("#prioritySlider").slider("value"), //task priority
MTContents = $('<div class="wholeTask">\
<div class="mainTaskWrapper clearfix">\
<div class="mainMarker"></div>\
<label class="mainTaskLabel"></label>\
<div class="holder"></div>\
<div class="subTrigger"></div>\
<div class="checkButton"></div>\
<div class="optTrigger"></div>\
<div class="addSubButton"></div>\
<div class="mainOptions">\
<ul>\
<li id="mainInfo">Details</li>\
<li id="mainEdit">Edit</li>\
<li id="mainDelete">Delete</li>\
</ul>\
</div>\
</div>\
</div>');
this.tasksArr.push(MTLabel);
//setting label
MTContents.find(".mainTaskLabel").text(MTLabel);
//setting category
if(MTCategory == ""){
MTCategory = "uncategorized";
}
MTContents.attr('data-cat', MTCategory);
if(this.catArr.indexOf(MTCategory) == -1){
this.catArr.push(MTCategory);
$("#categories ul").append("<li>" + MTCategory +"</li>");
}
$("#mainCatInput").autocomplete("option", "source",this.catArr);
//setting priority marker color
if(MTPriority == 2){
MTContents.find(".mainMarker").css("background-color", "red");
} else if(MTPriority == 1){
MTContents.find(".mainMarker").css("background-color", "black");
} else if(MTPriority == 0){
MTContents.find(".mainMarker").css("background-color", "blue");
}
MTContents.hide();
$("#tasksWrapper").prepend(MTContents);
MTContents.slideDown(100);
$("#tasksWrapper").sortable({
axis: "y",
scroll: "true",
scrollSpeed : 10,
scrollSensitivity: 10,
handle: $(".holder")
});
}
};
script.js : (the file where the .on() function resides at the bottom)
$(function(){
$("#addMain, #mainCatInput").on('click keypress', function(evt){
if(evt.type == "click" || evt.type =="keypress"){
if((evt.type =="click" && evt.target.id == "addMain") ||
(evt.which == 13 && evt.target.id=="mainCatInput")){
MainUtil.add();
}
}
});
//Here's the event i'm talking about :
$("div.mainTaskWrapper").on('click', '.checkButton' , function(){
alert("test text");
});
});
It does not look like div.mainTaskWrapper exist.
From the documentation (yes, it is actually bold):
Event handlers are bound only to the currently selected elements; they must exist on the page at the time your code makes the call to .on(). To ensure the elements are present and can be selected, perform event binding inside a document ready handler for elements that are in the HTML markup on the page.
[...]
By picking an element that is guaranteed to be present at the time the delegated event handler is attached, you can use delegated events to avoid the need to frequently attach and remove event handlers.
You might want to bind it to #tasksWrapper instead:
$("#tasksWrapper").on('click', '.checkButton' , function(){
alert("test text");
});
You need to specify a selector with on (as a parameter) to make it behave like the old delegate method. If you don't do that, the event will only be linked to the elements that currenly match div.mainTaskWrapper (which do not exists yet). You need to either re-assign the event after you added the new elements, or add the event to an element that already exists, like #tasksWrapper or the document itself.
See 'Direct and delegate events' on this page.
I know this is an old post but might be useful for anyone else who comes across...
You could try:
jQuery('body')on.('DOMNodeInserted', '#yourdynamicallyaddeddiv', function () {
//Your button click event
});
Here's a quick example - https://jsfiddle.net/8b0e2auu/

Resources