Let's say I have a function in a view that triggers when some kind of state is changed.
What would be best to name it and why?
stateChange
stateChanged
onStateChange
onStateChanged
I personally prefer to use onEventName names keeping the native naming convention for DOM event handlers.
Like myElement.onclick = function() { /* ... */ } for click event.
So for myEvent I'm using a handler named onMyEvent.
And if I have event stateChange, then I'll use onStateChange handler.
But really this question is more specific for each team of developers and code-style conventions inside the team/company.
So the main goal in such kinds of questions is to keep the code style the same in all parts to ensure readability.
Therefore if you're working in a team, just keep sticky to the team's code writing conventions and if you're working alone on existing code, try to keep its code style (sure if that style is not obviously ugly).
UPDATE: Understanding.
What is the event? Roughly it's an action initiated outside or inside the program, in other words, something happens in the system, e.g. some state changes (the state of the keyboard, of the mouse, of I/O devices, etc.) doesn't matter how (the user clicked on mouse or some program sent the mouse click signal to the system).
Say the browser window is subscribed to get notifications about some events and the operating system sends them to it as soon as possible, we'll assume that at the same time when something happens. So if the user clicks his mouse when the browser window is active and the document has a focus, the browser says to the document to fire the click event. And here our onclick handler starting its invocation. In other words, the system says to us that now happens a change of some state. And we're handling this change and not handling a fact saying to us that the state has been changed.
Let's assume that our handler is named onClicked. Since the handler's name saying in past tense we can get a reasonable question: "When clicked, how long ago it happened? How many times it was clicked? Hmm, maybe it's too late to handle this action (or actions?) at all...". So this name tells us that something happened sometime in past.
In contrast when our handler is named onClick it's obvious that click event just fired and fired once and we were notified about it immediately. And we're going to handle the click event - the information saying to us that the state of the mouse changed right now (not mouse clicked, but the event of click).
So the names in the past tense are more appropriate for the cases when we need to check if some state has been changed or not. E.g. if the variable stores the state = 1 we can call the function isStateChanged(); which will compare the value in state variable with the real value at the current moment. And here the past tense is a good choice for naming.
onStateChanged because this function triggers whenever some kind of state is changed.
I Googled a few names and noted the number of results returned. You can get some indication of the relative popularity of the most common forms for event handlers:
stateChanged 168k
stateChange 81k [1]
handleStateChange 61k
onStateChange 59k
onStateChanged 12k
beforeStateChange 2k
[1] Results show stateChange used mostly as the name of an event, not a handler.
Using different event types gives a much stronger recommendation towards the onStateChange form:
change [2]
onChange 2000k
onChanged 85k
handleChange 36k
beforeChange 27k
afterChange 22k
click [2]
onClick 48000k
onClicked 58k
handleClick 50k
beforeClick 8k [3]
onDrag 100k
handleDrag 36k
beforeDrag 32k
afterDrag 4k
onDragged 5k
[2] Too many results unrelated to programming.
[3] Apparently certain Microsoft API's can anticipate when the user is going to click.
My bet is for stateChanged due:
stateChange looks like an order, and looks like it receives a param with the new state.
onStateChange and onStateChanged are more keys for storing the handlers not the name for the handler itself.
IMHO
I usually go for a 2 factor event name. As an app grows in size, you may have more than one object who's state changes or perhaps a controller that can broadcast change events for more than one object and would therefore want to be able to differentiate between then both in code and in your head:
Object1:event
Object2:event
As for which event name, I think it comes down to personal preference and consistency.
I think one should make a difference based on the actual moment when the action is happening. For me onStateChange means that it is currently changing and I can be notified about this technically speaking right before the change.
OnStateChanged means the action already happened and I am notified at the end of it.
So, in between onStateChange and onStateChanged there is an important intention difference.
First one says "prepare yourself for this change" while the second one says "it's already happened".
Edit: I got carried away by the intention and didn't realize the naming itself.
Why the on prefix? This is reserved for handlers. The handlers will do something related to (on) that event.
So I would go with stateChange and stateChanged.
Related
I think I found a hard requirement to change behavior in a beforeChange handler based on the bindingContext of where a change is coming from. I already made some changes where a beforeChange handler gets to see the newValue along with the oldValue and where the handler can return boolean false to prevent the change from going forward.
The reason for this is the same objects may be bound to two (or more) different html nodes. The parent node may use different observables to wrap the same model object. But at some point, the child nodes are bound to properties through the same observables, sharing the same observable in more than one view element. When a change happens, I need to know from which of the two view elements the change originated.
I can save the bindingContext where the observable was made in the closure of my callbacks. But now I need the event to tell me what the bindingContext of the element was which just initiated the value change.
I can see the binding context somewhere in this domData thing, as follows: the valueUpdateHandler is called with the event object as argument it doesn't care about. But the event.target is our element that originated the change. I can see on that event target something like: "__ko__1655641582113" which I guess I am supposed to access with this ko.util.domData.
ko.utils.domData.get(arguments[0].target, "1__ko__1655641582113")
and lo and behold here I get an object with {alreadyBound: true, context: ko.bindingContext}
So I could force my way into this secret place and then get that context
ko.utils.domData.get($0, "1__ko__1655641582113").context.$parentContext.$rawData
and from there I could tell if I am in the element that should go forward with the change or not.
I am sure if anybody read this far that you'd be puzzled asking why in the hell I want to do that. But think about it, the observables are cool and all, but you sometimes need different behaviors based on where in your app the user is. In some areas they are just supposed to view, in others they can make (controlled) updates.
In other words, I know what I want is right and just, but I am wondering why it is so hard to get to what I want. For example, why the domData property is such a cryptic thing with a (timestamp) instead of a constant predictably named property. It's as if to tell me that I am making a big mistake even going there...
I'm using Backbone.js. In my view, I have a textarea whose keyup is bound to a function like this (but see edit below):
this.model.save({text: self.$('textarea').val()}, {patch: true});
In the view's initialize function, I bind the model's change event to the view's render function:
initialize: function() {
this.listenTo(this.model, 'change', _.bind(this.render, this));
},
Trouble is, when the user types in the textarea, the following sequence of events occurs:
The keyup event fires.
The keyup handler calls save on the model.
The call to save triggers the model's model's change event.
The view, listening for the model's change event, calls render.
The textarea is replaced in the DOM.
The textarea is no longer focused, and the text cursor position is lost.
What is the best practice for situations like this, where a texarea's keyup event needs to trigger a sync? Some options I have considered:
Don't bind change to render. Disadvantage: If the model data changes due to anything other than the user typing, the textarea doesn't automatically update.
Read and remember the cursor position at the beginning of render. Set the cursor position at the end of render. Disadvantage: Depends on cursor manipulation features for which browser support is spotty.
In the keyup handler, set a temporary property on the view telling it not to re-render. Unset it after the model has been saved. Disadvantage: Feels like spaghetti code, fights against the structure of Backbone.
Are there any options I'm not seeing? Do you recommend one of the options above?
Edit:
I didn't want to distract from the main point, but since it came up in one of the answers: I'm not binding directly to keyup, but intermediating it with _.debounce. Thus, the event handler only runs once the user stops typing, as defined by a certain amount of time elapsing since the last keyup.
First of all I'd like to discourage this as it seems like really strange behaviour to save your model on keyup. If there is a use-case which really necessitates this I'd suggest using the input event at the very least - otherwise you'll end up saving the model every time the user presses even an arrow key, shift, ctrl etc.
I think you'll also want to debounce the input events by 500ms or so you're not actually saving the model every single keystroke.
To address your comment in point 1:
Disadvantage: If the model data changes due to anything other than the
user typing, the textarea doesn't automatically update
You need to ask yourself the likelihood of this happening and how important it is that the view is rerendered if this was to happen.
Finally, if you decide that this is indeed likely and it is important that the view is rerendered, then you can try something like this
http://jsfiddle.net/nuewwdmr/2/
One of the important parts here is the mapping of model attribute names to the name field of your inputs. What I've done here follows the sequence of events you described above. The difference is that when the model changes, we inspect the changed attributes and update the value of the corresponding element in the template.
This works fine in a very simple situation, the happy path, where the user is typing in a "normal" way into the input. If the user, however, decides to go back to the start of the input and change a letter to capitalize it, for example, the cursor will jump to end of the string after the change event in the model occurs.
The behaviour you require here is really two-way data-binding which is by no means trivial, especially with Backbone given just how little functionality a Backbone View has.
My advice would be your point 1
Don't bind change to render
Edit
If you want to look further into model / view binding you could take a look at two libraries:
stickit
epoxy
I've used stickit before and it's...fine. Not great. It's ok for simple bindings, for example binding a "top-level" model attribute to an input element. Once you get into nested attributes you'll run into problems and you'll then have to look into something like Backbone Deep Model.
Like I said, Backbone's View doesn't offer very much. If you've got the time I'd suggest looking into using React components in place of Backbone Views, or even look at some of the interesting stuff that ampersand have to offer.
I have an object, and when that object is instantiated, it attaches a click event handler to the <body>. (The process of attaching happens within that object's definition)
This object is instantiated when the URL is changed (when the user navigates to another page).
There is always one type of this object 'per page', and as previously noted, it reinstantiates when the pange is changed, and the old object will no longer exist.
The attaching process looks like this:
var doc = $(document.body);
doc.off('click');
doc.on('click', function(){
do_stuff();
});
I am using this because I noticed that if simply attach the event handler, omitting the .off(), the handler will fire more times on a simple click as I navigate through the site (because it was attached/registered with every instantiation of that object).
Now, I could move this attachment process somewhere else, for example in the code section where the instantiation occurs, so it won't depend on that object and assure that the handler will be attached only once, but that would deprive me of access to some local variables and I would have to make them accessible to that code section.
My question is: Does this cost a lot performance-wise? I have noticed some posts here, on stackoverflow, emphasizing this is not optimal, but most of the examples displayed code with .off() or unbinding happening inside the .on()/binding.
IMPORTANT NOTE: I am using backbone.js. It is a 'one-page site'. The objects are basically views and their instantiation occurs in the router.
In short, no, there's no meaningful performance penalty to using off. Now I won't swear on a stack of bibles that it's impossible for off to cause a performance issue, but I will say that in 99 out of 100 (maybe more like 999 in 1,000 or 9999 in 10,000) real world cases you will never have to worry about off 'causing a performance problem.
To put it another way, off won't ever cause a noticeable performance slow-down unless you do something really crazy with it, or have a really crazy site that inadvertently does something really crazy with it.
NOT calling off on the other hand can cause lots of issues, performance-related and otherwise.
How do I completely unbind inline javascript events from their HTML elements?
I've tried:
undelegating the event from the body element
unbinding the event from the element
and even removing the event attribute from the HTML element
To my surprise at least, only removing the onchange attribute (.removeAttr('onchange')) was able to prevent the event from firing again.
<input type="text" onchange="validateString(this)"></input>
I know this is possible with delegates and that's probably the best way to go, but just play along here. This example is purely hypothetical just for the sake of proposing the question.
So the hypothetical situation is this:
I'm writing a javascript validation library that has javascript events tied to input fields via inline HTML attributes like so:
<input type="text" onchange="validateString(this)"></input>
But, I'd like to make the library a little better by unbinding my events, so that people working with this library in a single-page application don't have to manage my event handlers and so that they don't have to clutter their code at all by wiring up input events to functions in my hypothetical validation library... whatever. None of that's true, but it seems like a decent usecase.
Here's the "sample" code of Hypothetical Validation Library.js:
http://jsfiddle.net/CoryDanielson/jwTTf/
To test, just type in the textbox and then click elsewhere to fire the change event. Do this with the web inspector open and recording on the Timeline tab. Highlight the region of the timeline that correlates to when you've fired the change event (fire the change event multiple times) and you'll see the event listeners (in the window below) increase by 100 on each change event. If managed & removed properly, each event listener would be properly removed before rendering a new input, but I have not found a way to properly do that with inline javascript events.
What that code does is this:
onChange, the input element triggers a validation function
That function validates the input and colors the border if successful
Then after 1 second (to demonstrate the memory leak) the input element is replaced with identical HTML 100 times in a row without unbinding the change event (because I don't know how to do that.. that's the problem here). This simulates changing the view within a single-page app. This creates 100 new eventListeners in the DOM, which is visible through the web inspector.
Interesting Note. $('input').removeAttr('onchange'); will actually prevent the onchange event from being fired in the future, but does not garbage collect the eventListener/DOM stuff that is visible in the web inspector.
This screenshot is after change event fires 3 times. Each time, 100 new DOM nodes are rendered with identical HTML and I've attempted to unbind the onchange event from each node before replacing the HTML.
Update: I came back to this question and just did a quick little test using the JSFiddle to make sure that the answer was valid. I ran the 'test' dozens of times and then waited -- sure enough, the GC came through and took care of business.
I don't think you have anything to worry about. Although the memory can no longer be referenced and will eventually be garbage collected, it still shows up in the Web Inspector memory window. The memory will be garbage collected when the GC decides to garbage collect it (e.g., when the browser is low on memory or after some fixed time). The details are up to the GC implementer. You can verify this by just clicking the "Collect Garbage" button at the bottom of the Web Insepctor window. I'm running Chrome 23 and after I enter text in your validation box about 5 or 6 times, the memory usage comes crashing down, apparently due to garbage collection.
This phenomenon is not specific to inline events. I saw a similar pattern just by repeatedly allocating a large array and then overwriting the reference to that large array, leaving lots of orphaned memory for GC. Memory ramps up for a while, then the GC kicks in and does its job.
My first sggestion would have been to use off('change') but it seems you've already tried that. It's possible that the reason it's not working is because the handler wasn't attached with .on('change'). I don't know too much about how jQuery handles listener like this internally, but try attaching with .on('change', function ()... or .bind('change', function ()... instead.
I am making mouse click events and I'm trying to dispatch it to some node several times in a row. For that I am using the same MouseEvent object and for some reason this approach does not work. Yet, when I create event manually each time, system works. Does anybody know what is the reason for this behavior?
I've tried to change the timeStamp, but problem still occurs. I can solve the problem like I mentioned before, but I am interested in how this MouseEvent and corresponding dispatching and handling subsystems really work. MouseEvent specification that I've found on MDC pages seems to lack a lot of information.
Tnx for the help!
This is actually a security mechanism, dispatching an event that has been dispatched before isn't allowed. An event always has additional data associated with it, for example whether it comes from a trusted source (user's keyboard rather than JavaScript code). Some attacks (mostly against MSIE because it had mutable event objects) were using this - they caught a trusted event, changed it and dispatched it again elsewhere (changing might not always be required, dispatching it at a different element is enough for some attacks). In the end disallowing redispatching of events turned out to be the best solution. After all, this functionality isn't really required: creating a new event object with identical properties (minus hidden data) isn't exactly hard.
Pretty much all the security issues in this area were related to the file input control. Some time ago Firefox decided to change the file input UI radically and disallow entering the file name directly. I wonder whether this change made redispatching of events a non-issue. I doubt that anybody will be willing to risk opening this can of worms again however.
I think the reason you can't reuse the same MouseEvent object is because the event system maintains some internal state in the event objects so they can implement things like bubbling and cancelling. You may just have to stick with creating distinct event objects.
Reading Document Object Model Events may give you a better understanding of how the DOM event system works.
Without knowing what you have now ill just go under assumption.
Make an event function:
function clickEvent(event) {
//do something
}
Attach it:
obj.onclick = clickHandler;
And you can do this multiple times to multiple objects.