Modify callback to bind a DOM element with it in JavaScript - javascript

I am trying to attach a DOM element with a callback in JavaScript. Basically, the following is what I want:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="http://static.robotwebtools.org/roslibjs/current/roslib.min.js"></script>
<script src="http://static.robotwebtools.org/EventEmitter2/current/eventemitter2.min.js"></script>
<script>
var ros = new ROSLIB.Ros({ url: "ws://localhost:9090" });
var listener = new ROSLIB.Topic({
ros: ros,
name: "/listener",
messageType: "std_msgs/String",
element: document.getElementById("hi") // this is added to show my need
});
listener.subscribe(function (event) {
// do crazy things here. let say replace the text
event.element.innerHTML = event.message.data; // this is added to show my need
});
</script>
</head>
<body>
<div id="hi"> hello world </div>
</body>
</html>
I want to use the DOM element inside the callback, which was declared while initializing the listener. I found a similar question here but could not make it work in this case.
function Subscriber(element, topic) {
var listener = new ROSLIB.Topic({
ros: ros,
name: topic,
messageType: "std_msgs/String"
});
listener.addEventListener("subscribe", this, false);
this.listener = listener;
this.element = element;
}
Subscriber.prototype.handleEvent = function (event) {
switch (event.type) {
case "subscribe":
this.element.dispatchEvent(new CustomEvent("subscribe", { details: { msg: message, e: element } }));
}
};
var element = document.getElementById("hi");
var topic = "/listener";
var subscriber = new Subscriber(element, topic);
subscriber.handleEvent(function (event) {
// do crazy things here. let say replace the text
event.details.e.innerHTML = event.details.msg.data;
});
As listener is a custom object, addEventListener is unavailable. Therefore, the following error is reported at the console:
listener.addEventListener("subscribe", this, false);
Uncaught TypeError: listener.addEventListener is not a function
How to modify the subscribe callback to bind a DOM element with it in JavaScript?

Related

Refer JS object in HTML element generated by this object

My JavaScript object create some HTML elements (two buttons for example) and after user click on these buttons I should call some method of this object. So the question is how I can refer JS object in HTML element to call its method?
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Title Goes Here</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
function myObj(){
this.a = null;
this.setA = function(a){
this.a = a;
}
this.requestA = function(){
$( "body" ).append($('<input><button onclick="referenceToMyObject.setA($(this).prev().val());">Set A</button>'));
}
return this;
}
</script>
</head>
<body>
<script>
var myObjInst = myObj();
myObjInst.requestA();
</script>
</body>
Creating the event handler inline (onclick="foo()") won’t allow you to reference the object, and is discouraged in any case because you should avoid evaluating strings as code. In addition, your code bypasses JavaScript’s idea of objects somewhat. You can reformulate it as follows:
function MyObj() {
this.a = null;
}
MyObj.prototype.setA = function(a) {
const old = this.a;
this.a = a;
console.log("Updated a from", old, "to", this.a);
};
MyObj.prototype.requestA = function() {
const input = $("<input type='text'>");
const button = $("<button>Set A</button>");
button.click((e) => {
this.setA($(e.target).prev().val());
});
const body = $("body");
body.append(input);
body.append(button);
};
$(document).ready(() => {
const myObjInst = new MyObj();
myObjInst.requestA();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Here, we use button.click to define the event handler and new MyObj() to instantiate the object. Apart from that, I cleaned up the code a bit and added a bit of logging so you can see what’s going on.
You could still define setA and requestA within the constructor, as you do in your example. I chose to define them on the prototype since their behaviour is the same across instances.
Try this and please let me know if this works for you.
(working example in JSFiddle https://jsfiddle.net/galeroy/9nocztk4/1/)
<!doctype html>
<html>
<head>
<script>
var myObject = {
createButton: function(){
var p = document.getElementById('par')
var b = document.createElement('button');
b.innerHTML = 'click me';
b.setAttribute('onclick', 'myObject.myMethod()'); // this is the important part
p.appendChild(b);
},
myMethod: function(){
alert("Button created by object, when clicked, calls another method in the same object")
}
}
function init(){
myObject.createButton();
}
</script>
</head>
<body onload="init()">
<p id="par"></p>
</body>
</html>

Collect Data From Custom Event Handler In Plain Javascript

I'm creating a custom event in plain Javascript and attaching it to a DOM element. The element has multiple event listeners for the same event. The code I've written is:
var element = document.getElementById('demo');
element.addEventListener("myCustomEvent", function(e) {
console.log("myCustomEvent first handler triggered");
});
element.addEventListener("myCustomEvent", function(e) {
console.log("myCustomEvent second handler triggered");
e.data['otherKey'] = 'other value';
return e.data;
});
// Trigger the event
var event = new CustomEvent("myCustomEvent", {
data: {
firstKey: 'first Value'
}
});
element.dispatchEvent(event);
Now what I want is, get the data from last event handler like below:
var modifiedData = element.dispatchEvent(event);
Though I know the above line may not be correct, but I want something like that. If I use jQuery, there is pretty simple thing like $.triggerHandler I can use that iterates over all the listeners for a particular event and gives the return value of last handler if any.
But I want to do it in pure Javascript. Is there any solution for such kind of thing?
Just use the detail property and it should work just as you'd expect:
var element = document.getElementById('demo');
element.addEventListener("myCustomEvent", function(e) {
console.log("myCustomEvent first handler triggered");
e.detail.otherKey = 'second value';
});
element.addEventListener("myCustomEvent", function(e) {
console.log("myCustomEvent second handler triggered");
console.log(e.detail);
});
// Trigger the event
var event = new CustomEvent("myCustomEvent", {
detail: {
firstKey: 'first Value'
}
});
element.dispatchEvent(event);
<div id="demo"></div>
Per comments, here's an idea how you could achieve what you're hoping to do:
var element = document.getElementById('demo');
var originalFun = element.__proto__.addEventListener.bind(element);
var handlers = {};
var wrapperFun = function(e) {
if (e.type in handlers) {
var data = e.detail;
handlers[e.type].forEach(function(fun){
data = fun(data) || data;
});
}
};
element.__proto__.addEventListener = function(type, handler) {
if (typeof handlers[type] === 'undefined') {
handlers[type] = [];
originalFun(type, wrapperFun);
}
handlers[type].push(handler);
};
element.addEventListener("myCustomEvent", function(e) {
console.log("myCustomEvent first handler triggered");
e.otherKey = 'second value';
return e;
});
element.addEventListener("myCustomEvent", function(e) {
console.log("myCustomEvent second handler triggered");
console.log(e);
});
// Trigger the event
var event = new CustomEvent("myCustomEvent", {
detail: {
firstKey: 'first Value'
}
});
element.dispatchEvent(event);
<div id="demo"></div>
The easiest way is to save all Event listener results to some object.
Like const eventObj = {};
So, after each event you can just Object.assign(eventObj, <yourDataFromEvent>
More about Object.assign() here

Conditionally open links in a new tab

My goal is to make these links open in a new tab only if the check box is ticked.
Why is my anchor.getAttribute not a function if I change getElementByID to getElementsByClassName?
<!DOCTYPE html>
<html>
<head> </head>
<title> </title>
<body>
<input id="checkr" type="checkbox">Open in New Window</input>
<br />
Google <br>
W3 Schools <br>
Twitch <br>
<script>
var checkr = document.getElementById('checkr');
var anchor = document.getElementsByClassName('linker');
var link = anchor.getAttribute('href');
function OpenWindow(href) {
if (checkr.checked) {
window.open(href, '_blank');
} else {
window.open(href, '_self');
}
}
anchor.onclick = function() {
OpenWindow(link);
return false;
};
</script>
</body>
</html>
First, getElementsByClassName returns an array-like object...elements plural should be a clue...it's not returning a single thing, it's returning a collection of thing.
So to attach your handlers, you need to loop over them like so:
const linkers = document.getElementsByClassName('linker');
for(const linker of linkers) {
linker.addEventListener('click', function(evt) {
// this is your click event listener
});
}
Second, the way you're trying to get the anchor isn't going to work, because which anchor are you talking about? The best way to do it is let the event itself tell you what anchor was clicked, which it does through it's target property:
const linkers = document.getElementsByClassName('linker');
for(const linker of linkers) {
linker.addEventListener('click', function(evt) {
const href = evt.target.attributes['href'].value;
});
}
Since you don't want the default behavior to happen, call evt.preventDefault():
const linkers = document.getElementsByClassName('linker');
for(const linker of linkers) {
linker.addEventListener('click', function(evt) {
evt.preventDefault();
const href = evt.target.attributes['href'].value;
});
}
Then finally you can get the value of the checkbox and take the appropriate action:
const linkers = document.getElementsByClassName('linker');
for(const linker of linkers) {
linker.addEventListener('click', function(evt) {
evt.preventDefault();
const href = evt.target.attributes['href'].value;
const newWindow = document.getElementById('checkr').checked;
window.open(href, newWindow ? '_blank' : '_self');
});
}
Note that I'm using for...of loops, which may not be available in manky old browsers. If that's a problem, you can replace them with regular for loops with indices (you can't use Array#forEach because the DOM, in its infinite wisdom [cough] doesn't return arrays, but array-like objects).

Using bind function in event listeners

I have been writing a plugin, and i really like this format
Function.prototype._onClick = function() {
// do something
}
Fuction.prototype.addListner = function() {
this.$element.on('click', this._onClick.bind(this));
}
the problem is sometimes i need the element being clicked and the main object. Doing as below i loose the dom element and not using bind looses the main object.
Fuction.prototype.addListner {
this.$element.find('.some-class').on('click', this._onClick.bind(this));
}
To achieve that i go back to ugly version
Fuction.prototype.addListner = function() {
var self = this;
this.$element.find('.some-class').on('click', function() {
self._onClick($(this));
});
}
Is there any better way to do this?
As zerkms, you can use the event.target to achieve what you want.
When using .on, the handler is :
handler
Type: Function( Event eventObject [, Anything extraParameter ] [, ...
] ) A function to execute when the event is triggered. The value false
is also allowed as a shorthand for a function that simply does return
false.
So your _onClick function will receive click event as its 1st parameter, then from event.target, you can now get the clicked item.
var Test = function(sel) {
this.$element = $(sel);
this.value = 'My value is ' + this.$element.data('val');
};
Test.prototype.addListner = function() {
this.$element.find('.some-class').on('click', this._onClick.bind(this));
}
Test.prototype._onClick = function(evt) {
// Get the target which is being clicked.
var $taget = $(evt.target);
//
console.log(this.value);
// use $target to get the clicke item.
console.log($taget.data('val'));
}
var test = new Test('#test');
test.addListner();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<div id="test" data-val="divVal">
<button class="some-class" data-val="Button-A">btnA</button>
<button class="some-class" data-val="Button-B">btnB</button>
</div>

Use objects method as callback

I've got another JavaScript/jQuery-Problem. A minimal example could be this: I've got a div, and want some JavaScript executed, when the mouse enters. But for some reasons (= in reality, there a many divs, and for each data needs to be kept) I want to use a object as handler for the callback. Here's a small example:
function handler($thediv)
{
this.somedata = 8;
$thediv.mouseenter(function() { this.callback(); });
}
handler.prototype.callback = function()
{
alert(somedata);
}
An object is created when the document is loaded:
$(document).ready( function() {
new handler($("div"));
});
Nothing happens - except that the constructor is executed. I've tried and searched for hours now, but I can't fix this... probably too trivial?
Edit: A complete example.
<html>
<script type="text/javascript" src="jquery-1.6.js"></script>
</head>
<body>
<div>
blah blahasdasdadsssssssssssssss
asddddddddddddddddddddddddddd
</div>
</body>
<script type="text/javascript">
$(document).ready(function() {
new handler($("div"));
});
function handler($thediv)
{
this.somedata = 8;
$thediv.mouseenter(this.callback);
}
handler.prototype.callback = function()
{
alert(somedata);
}
</script>
</html>
The biggest issue here is the use of this in various contexts. Within the mouseenter function, this refers to div, not the object.
This should work:
http://jsfiddle.net/Nx5c7/
function handler($thediv)
{
this.somedata = 8;
this.theID=$thediv.attr("id");
var obj=this;
$thediv.mouseenter(function() {
obj.callback();
});
}
handler.prototype.callback = function()
{
alert(this.theID + " : " + this.somedata);
}

Categories