I would like to trigger an events without duplicating code. So I decided to create a constructor function (class) and then a new object with a variable that connects to an event handler. Im just testing it but I cant get the if statement to trigger the alert() using this.link it works like this: if(el) but not like this: if(el === this.link)
var faqOne = document.getElementById("faqOne");
var hiddenOne = document.querySelector("p.faqOneHidden");
faqOne.addEventListener("click", function (e) {
showFaqOne.showClickedFaq(e);
}, false);
function DisplayQFaqs(link, faq) {
this.link = link;
this.faq = faq;
}
DisplayQFaqs.prototype.showClickedFaq = function (e) {
var el = e.target;
if (el === this.link) {
alert('hi');
}
};
var showFaqOne = new DisplayQFaqs(faqOne, hiddenOne);
Related
I have this function check(e) that I'd like to be able to pass parameters from test() when I add it to the eventListener. Is this possible? Like say to get the mainlink variable to pass through the parameters. Is this even good to do?
I put the javascript below, I also have it on jsbin: http://jsbin.com/ujahe3/9/edit
function test() {
if (!document.getElementById('myid')) {
var mainlink = document.getElementById('mainlink');
var newElem = document.createElement('span');
mainlink.appendChild(newElem);
var linkElemAttrib = document.createAttribute('id');
linkElemAttrib.value = "myid";
newElem.setAttributeNode(linkElemAttrib);
var linkElem = document.createElement('a');
newElem.appendChild(linkElem);
var linkElemAttrib = document.createAttribute('href');
linkElemAttrib.value = "jsbin.com";
linkElem.setAttributeNode(linkElemAttrib);
var linkElemText = document.createTextNode('new click me');
linkElem.appendChild(linkElemText);
if (document.addEventListener) {
document.addEventListener('click', check/*(WOULD LIKE TO PASS PARAMETERS HERE)*/, false);
};
};
};
function check(e) {
if (document.getElementById('myid')) {
if (document.getElementById('myid').parentNode === document.getElementById('mainlink')) {
var target = (e && e.target) || (event && event.srcElement);
var obj = document.getElementById('mainlink');
if (target!= obj) {
obj.removeChild(obj.lastChild);
};
};
};
};
Wrap your event listener into a function:
document.addEventListener(
'click',
function(e,[params]){
check(e,[params]);
}
);
One solution would be to move the "check" function up inside your test() function. As an inner function, it would automatically be able to refer to variables in its outer scope. Like this:
function test() {
if (!document.getElementById('myid')) {
var mainlink = document.getElementById('mainlink');
var newElem = document.createElement('span');
mainlink.appendChild(newElem);
var linkElemAttrib = document.createAttribute('id');
linkElemAttrib.value = "myid";
newElem.setAttributeNode(linkElemAttrib);
var linkElem = document.createElement('a');
newElem.appendChild(linkElem);
var linkElemAttrib = document.createAttribute('href');
linkElemAttrib.value = "jsbin.com";
linkElem.setAttributeNode(linkElemAttrib);
var linkElemText = document.createTextNode('new click me');
linkElem.appendChild(linkElemText);
if (document.addEventListener) {
document.addEventListener('click', function(e) {
if (document.getElementById('myid')) {
if (document.getElementById('myid').parentNode === mainlink) {
var target = (e && e.target) || (event && event.srcElement);
if (target!= mainlink) {
mainlink.removeChild(mainlink.lastChild);
};
};
};
});
};
What I typically do in this situation is save arguments to the object (whenever it's convenient), and then retrieve them in the function, like this:
// Listener function receives e (the event object) by default.
function eventReceiver(e) {
var obj;
// Find object which triggered the event
e.srcElement ? obj = e.srcElement : obj = e.target;
// obj.someProperty has been set elsewhere, replacing a function parameter
alert(obj.someProperty);
}
This is cross browser, and allows you to pass objects and values through the properties of the event target.
I initially started with the this keyword, but that behaves differently cross-browser. In FF, it's the object that the event was triggered on. In IE, it's the event itself. Thus, the srcElement / target solution was born. I'm interested to see the other solutions though - have a +1.
I want to be able to set a function onbroadcast in SpeechRecognition after I create a new SpeechRecognition object so that I can call this function internally if certain conditions are met.
I would like to be able to set it in the same way that you would set something like onerror in webkitSpeechRecognition. When I look at onerror in the Developer Tools it looks like it might be done via some sort of getter/setter like what is described here but I can't be certain.
Is this possible?
recognition.js:
var SpeechRecognition = function () {
var recognitionObject = new webkitSpeechRecognition();
recognitionObject.onresult = function (event) {
if(event.results.length > 0) {
if (onbroadcast !== null && onbroadcast === 'function') {
onbroadcast('there are results');
}
}
}
recognitionObject.onerror = function (event) {
console.log(event);
}
recognitionObject.onend = function (event) {
console.log(event);
}
recognitionObject.start();
}
SpeechRecognition.prototype.onbroadcast = null;
main.js:
var sr = new SpeechRecognition();
sr.onbroadcast = function(msg) {
document.getElementById('id') = msg;
}
You need to refer to onbroadcast as a property of your instance (this.onbroadcast). It doesn't magically become available as a variable inside the constructor scope.
function SpeechRecognition() {
var self = this; // a reference to the instance
var recognitionObject = new webkitSpeechRecognition();
recognitionObject.onresult = function (event) {
if (event.results.length > 0 && typeof self.onbroadcast === 'function') {
self.onbroadcast('there are results');
// ^ a property access
}
};
recognitionObject.onerror = recognitionObject.onend = function (event) {
console.log(event);
};
recognitionObject.start();
}
I'm really stuck on this.. I need to send an event when both Load module and Hide module code was executed, and only then send the event. Ideas on how to achieve this?
// Load module
(
function() {
var s=document.createElement('script');
s.type='text/javascript';
s.async=true;
s.src='https://example.com/bundles.js';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
)();
// Hide module
var inverval = setInterval(hideClass, 100);
function hideClass () {
if ($(".class").hide().length > 0) clearInterval(inverval);
}
// When both happend = Send a event to Google Analytics
DigitalData.push({'event':Module, 'eventLabel':'Page'});
If this is your only option, then perhaps there's something you are going about wrongly. Anyway, let's see ... Only when both events have taken place.
var HandleTwoEvents = function (key1, key2) {
this.count = 0;
this.pack = [];
$self = this;
this.startListening = function(fn) {
fn = fn || function () {}
window.addEventListener(key1, function (ev) {
if ($self.pack.indexOf(key1) < 0) {
$self.pack.push(key1);
$self.count++;
if ($self.count == 2) {
fn();
$self.count = 0;
}
}
console.log(key1, ev);
});
window.addEventListener(key2, function (ev) {
if ($self.pack.indexOf(key2) < 0) {
$self.pack.push(key2);
$self.count++;
if ($self.count == 2) {
fn();
$self.count = 0;
}
}
console.log(key2, ev);
});
}
}
Forgive me, i always use this function to create events
function createEvent(name, obj) {
var evt = document.createEvent("Event");
evt.initEvent(name, true, true);
evt.data = obj;
dispatchEvent(evt);
}
Now, to log both events ...
var both = new HandleTwoEvents("EventKeyOne", "EventKeyTwo");
both.startListening(function () {console.log("This means that both Events have taken place")});
Now, let's test ...
createEvent("EventKeyOne", {});
//key, data are the arguments ... function defined in startListening above does not execute, and upon inspection, both.count is seen to be 1
createEvent("EventKeyTwo", {});
//Now, function executes.
//It also works if "EventKeyTwo" is raised before "EventKeyOne"
Happy Coding!
PS: I'm sure there's a better way to handle the use of the $self variable, with some function binding, i guess. I've never been able to learn it.
I create a hammer instance like so:
var el = document.getElementById("el");
var hammertime = Hammer(el);
I can then add a listener:
hammertime.on("touch", function(e) {
console.log(e.gesture);
}
However I can't remove this listener because the following does nothing:
hammertime.off("touch");
What am I doing wrong? How do I get rid of a hammer listener? The hammer.js docs are pretty poor at the moment so it explains nothing beyond the fact that .on() and .off() methods exist. I can't use the jQuery version as this is a performance critical application.
JSFiddle to showcase this: http://jsfiddle.net/LSrgh/1/
Ok, I figured it out. The source it's simple enough, it's doing:
on: function(t, e) {
for (var n = t.split(" "), i = 0; n.length > i; i++)
this.element.addEventListener(n[i], e, !1);
return this
},off: function(t, e) {
for (var n = t.split(" "), i = 0; n.length > i; i++)
this.element.removeEventListener(n[i], e, !1);
return this
}
The thing to note here (apart from a bad documentation) it's that e it's the callback function in the on event, so you're doing:
this.element.addEventListener("touch", function() {
//your function
}, !1);
But, in the remove, you don't pass a callback so you do:
this.element.removeEventListener("touch", undefined, !1);
So, the browser doesn't know witch function are you trying to unbind, you can fix this not using anonymous functions, like I did in:
Fiddle
For more info: Javascript removeEventListener not working
In order to unbind the events with OFF, you must:
1) Pass as argument to OFF the same callback function set when called ON
2) Use the same Hammer instance used to set the ON events
EXAMPLE:
var mc = new Hammer.Manager(element);
mc.add(new Hammer.Pan({ threshold: 0, pointers: 0 }));
mc.add(new Hammer.Tap());
var functionEvent = function(ev) {
ev.preventDefault();
// .. do something here
return false;
};
var eventString = 'panstart tap';
mc.on(eventString, functionEvent);
UNBIND EVENT:
mc.off(eventString, functionEvent);
HammerJS 2.0 does now support unbinding all handlers for an event:
function(events, handler) {
var handlers = this.handlers;
each(splitStr(events), function(event) {
if (!handler) {
delete handlers[event];
} else {
handlers[event].splice(inArray(handlers[event], handler), 1);
}
});
return this;
}
Here's a CodePen example of what Nico posted. I created a simple wrapper for "tap" events (though it could easily be adapted to anything else), to keep track of each Hammer Manager. I also created a kill function to painlessly stop the listening :P
var TapListener = function(callbk, el, name) {
// Ensure that "new" keyword is Used
if( !(this instanceof TapListener) ) {
return new TapListener(callbk, el, name);
}
this.callback = callbk;
this.elem = el;
this.name = name;
this.manager = new Hammer( el );
this.manager.on("tap", function(ev) {
callbk(ev, name);
});
}; // TapListener
TapListener.prototype.kill = function () {
this.manager.off( "tap", this.callback );
};
So you'd basically do something like this:
var myEl = document.getElementById("foo"),
myListener = new TapListener(function() { do stuff }, myEl, "fooName");
// And to Kill
myListener.kill();
When users click "search" input element, the search text inside the input will disappear and since I have several controls like that, I thought I could make the code reusable. Here is my code formerly done and working with jQuery but now in YUI I cannot make it work.
var subscriptionBoxTarget = "div.main div.main-content div.side-right div.subscription-box input";
var ssbNode = YAHOO.util.Selector.query(subscriptionBoxTarget);
var ssbValue = YAHOO.util.DOM.getAttribute(ssbNode,"value");
var subscriptionBox = new RemovableText(ssbNode,ssbValue,null);
subscriptionBox.bind();
////////////////////////////////
//target : the target of the element which dispatches the event
// defaultText : the default for input[type=text] elements
// callBack : is a function which is run after everthing is completed
function RemovableText(target,defaultText,callBack)
{
var target = target; //private members
var defaultText = defaultText;
var callBack = callBack;
//instance method
this.bind = function()
{
mouseClick(target,defaultText);
mouseOff(target,defaultText);
if(callBack != null)
callBack();
}
//private methods
var mouseClick = function(eventTarget,defaultValue)
{
var _eventTarget = eventTarget;
var _defaultValue = defaultValue;
/*$(eventTarget).bind("click",function(){
var currentValue = $(this).val();
if(currentValue == defaultValue)
$(this).val("");
});*/
YAHOO.util.Event.addListener(_eventTarget,"click",function(e){
alert(e);
});
}
var mouseOff = function(eventTarget,defaultValue)
{
var _eventTarget = eventTarget;
var _defaultValue = defaultValue;
/*$(eventTarget).bind("blur",function(){
var currentValue = $(this).val();
if(currentValue == "")
$(this).val(_defaultValue);
});*/
YAHOO.util.Event.addListener(_eventTarget,"blur",function(e){
alert(e);
});
}
}
You have a lot of unnecessary code here.
The input parameters passed to the RemovableText constructor are available by closure to all the methods defined inside. You don't need to, and shouldn't redefine named params as vars.
function RemovableText(target, defaultText, callback) {
this.bind = function () {
YAHOO.util.Event.on(target, 'click', function (e) {
/* You can reference target, defaultText, and callback in here as well */
});
YAHOO.util.Event.on(target, 'blur', function (e) { /* and here */ });
if (callback) {
callback();
}
};
}
The definition of an instance method from within the constructor seems dubious, as is the requirement that the values passed to the constructor must be kept private. Just assign them to instance properties (this._target = target; etc) and add instance methods to the prototype. If the functionality you're after is just this simple, then why bother with methods at all?
Using the click event does not support keyboard navigation. You should use the focus event.
I'm not sure why you would have a callback passed at construction that fires immediately after attaching the event subscribers.