Mootools: inject vs adopt - events

I want to dynamically add some preconfigured HTML-Elements in use of a 'click'-event with mootools.
So I can make it work with my basic knowledge, although it isn´t very nifty. I coded this so far...
This is my preconfigured element, with some text, a classname and some event, cause i wanna have events already added, when it´s inserted into my container:
var label = new Element('label', {
'text': 'Label',
'class': 'label',
'events': {
'click': function(el){
alert('click');
}
}
});
Here is my function, which adds the label-Element:
function addText(){
$('fb-buildit').addEvent('click', function(){
row.adopt(label, textinput, deletebtn);
$('the-form').adopt(row.clone());
row.empty();
/*
label.clone().inject($('the-form'));
textinput.inject($('the-form'));
deletebtn.inject($('the-form'));
*/
});
}
The second part which uses inject also works, but there, my click-Event, which fires the "alert('click')" works too. The method with adopt doesn´t add any event to my label Object, when its inserted in the dom.
Can anyone help me with this. I just wanna know why adobt ignores my "events" settings and inject doesn´t.
Thanks in advance.
(sorry for my english ^^)

you go label.clone().inject but row.adopt(label) and not row.adopt(label.clone()) -
either way. .clone() does not cloneEvents for you - you need to do that manually.
var myclone = label.clone();
myclone.cloneEvents(label);
row.adopt(label);
this is how it will work
as for why that is, events are stored in the Element storage - which is unique per element. mootools assigns a uid to each element, eg, label = 1, label.clone() -> 2, label.clone() -> 3 etc.
this goes to Storage[1] = { events: ... } and so forth. cloning an element makes for a new element.uid so events don't work unless you implicitly use .cloneEvents()
you are sometimes not doing .clone() which works because it takes the ORIGINAL element along with its storage and events.
suggestion consider looking into event delegation. you could do
formElement.addEvent("click:relay(label.myLabel)", function(e, el) {
alert("hi from "+ el.uid);
});
this means no matter how many new elements you add, as long as they are of type label and class myLabel and children of formElement, the click will always work as the event bubbles to the parent.

Related

change:source event in JointJS

Merry Christmas, everyone!
I want to do something when the source element or target element of a joint.dia.Link is changed. Firstly I tried to put the code in the callback function of 'change:source' and 'change:target' events. However, it turns out that those callback functions are called as soon as the link's position changes, instead of being called when the source element or target element changes. Then I tried to put the code in the LinkView.pointup() function, by adding a tag, which is set in the callback function of 'change:source' and 'change:target' events, to indicate the changed element. The resulted code looks like this:
link.on('change:source', function(){this.src_changed = true;});
link.on('change:target', function(){this.dest_changed = true;});
joint.shapes.custom.ModelLink = joint.dia.Link.extend({
defaults: joint.util.deepSupplement({
type: 'custom.ModelLink',
}, joint.dia.Link.prototype.defaults)
});
joint.shapes.custom.ModelLinkView = joint.dia.LinkView.extend({
pointerdown: function () {
joint.dia.LinkView.prototype.pointerdown.apply(this, arguments);
},
pointermove: function () {
joint.dia.LinkView.prototype.pointermove.apply(this, arguments);
},
pointerup: function (evt, x, y) {
var link = this.model;
if(link.src_changed) { // do something}
if(link.dest_changed) {// do something}
joint.dia.LinkView.prototype.pointerup.apply(this, arguments);
}
});
However, I found src_changed and dest_changed are both set to true sometimes when I am just dragging one end of the link. Why does this happen? How can I fix this? Or any new approach to do some response to the change of source element or target element?
Besides, After I reset the events of joint.shapes.uml.State using model.set('events', events), the text doesnot change on the graph? How can I refresh graph to show the changed state element?
Thanks very much!
The change:source and change:target events are indeed triggered also when the position of the arrowheads changes. In general, the source and target of a link can either be a point (an object with x and y properties) or an element (and in the near future also a link) - an object with id property pointing to the linked element. If you're only interested in source/target being an element, you can just check in your handlers for the change:source and change:target events whether the source/target of the link contains the id property:
if (this.get('source').id) { /*... do something ...*/ }

map keyboard keys with mootools

I am looking to make the enter key behave exactly like the tab key on a form.
I am stuck on the fireEvent section.
var inputs = $$('input, textarea');
$each(inputs,function(el,i) {
el.addEvent('keypress',function(e) {
if(e.key == 'enter') {
e.stop();
el.fireEvent('keypress','tab');
}
});
});
How do I fire a keypress event with a specified key? Any help would be greatly appreciated.
this will work but it relies on dom order and not tabindex
var inputs = $$('input,textarea');
inputs.each(function(el,i){
el.addEvent('keypress',function(e) {
if(e.key == 'enter'){
e.stop();
var next = inputs[i+1];
if (next){
next.focus();
}
else {
// inputs[0].focus(); or form.submit() etc.
}
}
});
});
additionally, textarea enter capture? why, it's multiline... anyway, to do it at keyboard level, look at Syn. https://github.com/bitovi/syn
the above will fail with hidden elements (you can filter) and disabled elements etc. you get the idea, though - focus(). not sure what it will do on input[type=radio|checkbox|range] etc.
p.s. your code won't work because .fireEvent() will only call the bound event handler, not actually create the event for you.
Take a look at the class keyboard (MooTools More).
It can fire individual events for keys and provides methodology to disable and enable the listeners assigned to a Keyboard instance.
The manual has some examples how to work with this class, here's just a simple example how I implemented it in a similar situation:
var myKeyEv1 = new Keyboard({
defaultEventType: 'keydown'
});
myKeyEv1.addEvents({
'shift+h': myApp.help() // <- calls a function opening a help screen
});
Regarding the enter key in your example, you have to return false somewhere to prevent the enter-event from firing. Check out this SO post for more details.

jQuery — trigger a live event only once per element on the page?

Here's the scenario
$("p").live('customEvent', function (event, chkSomething){
//this particular custom event works with live
if(chkSomething){
doStuff();
// BUT only per element
// So almost like a .one(), but on an elemental basis, and .live()?
}
})
Here's some background
The custom event is from a plugin called inview
The actual issue is here http://syndex.me
In a nutshell, new tumblr posts are being infnitely scrolled via
javascript hack (the only one out there for tumblr fyi.)
The inview plugin listens for new posts to come into the viewport, if the top of an image is shown, it makes it visible.
It's kinda working, but if you check your console at http://.syndex.me check how often the event is being fired
Maybe i'm also being to fussy and this is ok? Please let me know your professional opinion. but ideally i'd like it to stop doing something i dont need anymore.
Some things I've tried that did not work:
stopPropagation
.die();
Some solutions via S.O. didnt work either eg In jQuery, is there any way to only bind a click once? or Using .one() with .live() jQuery
I'm pretty surprised as to why such an option isnt out there yet. Surely the .one() event is also needed for future elements too? #justsayin
Thanks.
Add a class to the element when the event happens, and only have the event happen on elements that don't have that class.
$("p:not(.nolive)").live(event,function(){
$(this).addClass("nolive");
dostuff();
});
Edit: Example from comments:
$("p").live(event,function(){
var $this = $(this);
if ($this.data("live")) {
return;
}
$this.data("live",true);
doStuff();
});
This one works (see fiddle):
jQuery(function($) {
$("p").live('customEvent', function(event, chkSomething) {
//this particular custom event works with live
if (chkSomething) {
doStuff();
// BUT only per element
// So almost like a .one(), but on an elemental basis, and .live()?
$(this).bind('customEvent', false);
}
});
function doStuff() {
window.alert('ran dostuff');
};
$('#content').append('<p>Here is a test</p>');
$('p').trigger('customEvent', {one: true});
$('p').trigger('customEvent', {one: true});
$('p').trigger('customEvent', {one: true});
});
This should also work for your needs, although it's not as pretty :)
$("p").live('customEvent', function (event, chkSomething){
//this particular custom event works with live
if(chkSomething && $(this).data('customEventRanAlready') != 1){
doStuff();
// BUT only per element
// So almost like a .one(), but on an elemental basis, and .live()?
$(this).data('customEventRanAlready', 1);
}
})
Like Kevin mentioned, you can accomplish this by manipulating the CSS selectors, but you actually don't have to use :not(). Here's an alternative method:
// Use an attribute selector like so. This will only select elements
// that have 'theImage' as their ONLY class. Adding another class to them
// will effectively disable the repeating calls from live()
$('div[class=theImage]').live('inview',function(event, visible, visiblePartX, visiblePartY) {
if (visiblePartY=="top") {
$(this).animate({ opacity: 1 });
$(this).addClass('nolive');
console.log("look just how many times this is firing")
}
});
I used the actual code from your site. Hope that was okay.

Titanium Mobile: reference UI elements with an ID?

How do you keep track of your UI elements in Titanium? Say you have a window with a TableView that has some Switches (on/off) in it and you'd like to reference the changed switch onchange with a generic event listener. There's the property event.source, but you still don't really know what field of a form was just toggled, you just have a reference to the element. Is there a way to give the element an ID, as you would with a radiobutton in JavaScript?
Up to now, registered each form UI element in a dictionary, and saved all the values at once, looping through the dictionary and getting each object value. But now I'd like to do this onchange, and I can't find any other way to do it than create a specific callback function for each element (which I'd really rather not).
just assign and id to the element... all of these other solution CAN work, but they seem to be over kill for what you are asking for.
// create switch with id
var switcher0 = Ti.Ui.createSwitch({id:"switch1"});
then inside your event listener
myform.addEventListener('click', function(e){
var obj = e.source;
if ( obj.id == "switch1" ) {
// do some magic!!
}
});
A simple solution is to use a framework that helps you keep track of all your elements, which speeds up development quite a bit, as the project and app grows. I've built a framework of my own called Adamantium.js, which lets you use a syntax like jQuery to deal with your elements, based on ID and type selectors. In a coming release, it will also support for something like classes, that can be arbitrarily added or removed from an element, tracking of master/slave relationships and basic filter methods, to help you narrow your query. Most methods are chainable, so building apps with rich interaction is quick and simple.
A quick demo:
// Type selector, selects all switches
$(':Switch')
// Bind a callback to the change event on all switches
// This callback is also inherited by all new switch elements
$(':Switch').bind('change', function (e) {
alert(e.type + ' fired on ' + e.source.id + ', value = ' + e.value);
});
// Select by ID and trigger an event
$('#MyCustomSwitch').trigger('change', {
foo: 'bar'
});
Then there's a lot of other cool methods in the framework, that are all designed to speed up development and modeled after the familiar ways of jQuery, more about that in the original blog post.
I completely understand not wanting to write a listener to each one because that is very time consuming. I had the same problem that you did and solved it like so.
var switches = [];
function createSwitch(i) {
switches[i] = Ti.UI.createSwitch();
switches[i].addEventListener('change', function(e) {
Ti.API.info('switch '+i+' = '+e.value);
});
return switches[i];
}
for(i=0;i<rows.length;i++) {
row = Ti.UI.createTableViewRow();
row.add(createSwitch(i));
}
However keep in mind that this solution may not fit your needs as it did mine. For me it was good because each time I created a switch it added a listener to it dynamically then I could simply get the e.source.parent of the switch to interact with whatever I needed.
module Id just for the hold it's ID. When we have use id the call any another space just use . and use easily.
Try This
var but1 = Ti.Ui.createButton({title : 'Button', id:"1"});
window.addEventListener('click', function(e){
var obj = e.source;
if ( obj.id == "1" ) {
// do some magic!!
}
});
window.add(but1);
I, think this is supported for you.
how do you create your tableview and your switcher? usually i would define a eventListener function while creating the switcher.
// first switch
var switcher0 = Ti.Ui.createSwitch();
switch0.addEventListener('change',function(e){});
myTableViewRow.add(switch0);
myTableView.add(myTableViewRow);
// second switch
var switch1 = ..
so no generic event listener is needed.

Event removal in Mootools, and syntax of event addition

So I have been adding my events thusly:
element.addEvent('click', function() {
alert('foobar');
});
However, when attempting to remove said event, this syntactically identical code (with "add" switched to "remove") does not work.
element.removeEvent('click', function() {
alert('foobar');
});
I assume this is because the two functions defined are not referenced the same, so the event is not technically removed. Alright, so I redefine the event addition and removal:
element.addEvent('click', alert('foobar'));
element.removeEvent('click', alert('foobar'));
Which works great, except now when the page loads, the click event is fired even before it's clicked!
The function is removed, though, which is great......
update: when you do .addEvent('type', function(){ }) and .removeEvent('type', function(){ }), even though the functions may have the same 'signatures', they are two separte anonymous functions, assigned on the fly. function 1 is !== to function 2 - hence there is no match when MooTools tries to remove it.
to be able to remove an exact handler, o:
function handler(){ ... }
el.addEvent('click', handler);
// .. later
el.removeEvent('click', handler);
Internally, events are actually a map of keys to functions in element storage. have a look at this fiddle i did a while back for another SO question - http://www.jsfiddle.net/mVJDr/
it will check to see how many events are stacked up for a particular event type on any given element (or all events).
similarly, removeEvent looks for a match in the events storage - have a look on http://jsfiddle.net/dimitar/wLuY3/1/. hence, using named functions like Nikolaus suggested allows you to remove them easily as it provides a match.
also, you can remove events via element.removeEvents("click") for all click events.
your page now alerts because you pass on alert as the function as well as execute it with the params 'foobar'. METHOD followed by () in javascript means RUN THE METHOD PRECEDING IT IMMEDIATELY, NOT LATER. when you bind functions to events, you pass the reference (the method name) only.
to avoid using an anonymous function and to pass argument,s you can do something like:
document.id('foobar').addEvent('click', alert.bind(this, 'foo'));
as bind raps it for you, but removing this will be even more complicated.
as for event delegation, it's:
parentEl.addEvents({
"click:relay(a.linkout)": function(e, el) {
},
"mouseover:relay(li.menu)": function(e, el) {
}
});
more on that here http://mootools.net/docs/more/Element/Element.Delegation#Element:removeEvent
keep in mind it's not great / very stable. works fine for click stuff, mouseenter is not to be used delegated, just mouseover - which means IE can fire mouseout when it should not. the way i understand it, it's coming improved in mootools 2.0
edit updating to show an example of bound and unbound method within a class pattern in mootools
http://www.jsfiddle.net/wmhgw/
var foo = new Class({
message: "hi",
toElement: function() {
return this.element = new Element("a", {
href: "http://www.google.com",
text: "google",
events: {
"click": this.bar.bind(this), // bind it
"mouseenter": this.bar // unbound -> this.element becomes this
}
});
},
bar: function(event) {
event.stop();
// hi when bound to class instance (this.message will exist)
// 'undefined' otherwise.
console.log(this.message || "undefined");
}
});
document.id(new foo()).inject(document.body);
the mouseenter here will be unbound where this will refer to the default scope (i.e the element that triggered the event - the a href). when bound, you can get the element via event.target instead - the event object is always passed on to the function as a parameter.
btw, this is a slightly less familiar use of class and element relation but it serves my purposes here to illustrate binding in the context of classes.
assig the function to a variable and use the same reference to add and remove the event.
if you use an anonymous function you will get to different references
var test = function(){ alert('test: ' + this.id); }
$('element').addEvent('click', test);
...
$('element').removeEvent('click', test);
addEvent : Attaches an event listener to a DOM element.
Example -
$('myElement').addEvent('click', function(){
alert('clicked!');
});
removeEvent : Works as Element.addEvent, but instead removes the specified event listener.
Example -
var destroy = function(){ alert('Boom: ' + this.id); } // this refers to the Element.
$('myElement').addEvent('click', destroy);
//later...
$('myElement').removeEvent('click', destroy);
This means when you add an event with a eventhandler not an anonymous function if you than remove the event than it will be removed.

Resources