How can I monitor the value of a hidden input element? - javascript

I have a SELECT element which adds its value to a hidden INPUT via JavaScript every time an OPTION is clicked (along with a visual representation of each selection) and I'd like to be able to monitor changes for another JavaScript function. For the sake of modularity, I can't integrate the second function into the first one. I would also rather not poll the hidden INPUT's value to avoid hacks. Currently I am using the onclick event on the DIV that contains both the SELECT and the hidden INPUT, but that's also quite hack-ish. Do you know a way to monitor a hidden INPUT element for changes?

So, you have:
Function A, which updates the hidden INPUT.
Function B, which should be called when the INPUT is updated.
Why not create an "event" of your own that that function A calls/dispatches whenever it is done working?
I believe most Javascript frameworks support the concept of custom events pretty easily, but it's just a series of function calls.
For example, create some object D which represents the dispatcher for a single event. Yes this is silly, but I'm trying to keep the concept simple. This object D, holds a list of functions which have "registered" for its event, and when the event is dispatched, it calls those functions.
Something like:
var d = (function() {
var funcs = [];
function fire() {
for (var i=0; i<funcs.length; ++i) {
funcs[i].call();
}
}
function register(newfunc) {
funcs.push(newfunc);
}
return {
fire: fire,
register: register
};
})();
You just have two things left to do - make function A fire the event:
function A() {
// do stuff to update the INPUT
d.fire();
}
and also, onload, "register" function B to be called when the event happens:
d.register(B);
This approach maintains the seperation-of-modules principle (or whatever its called) since A and B know nothing of each other, and only need to know about a third object.

Related

How do you assign onclick within for loop in javascript?

function initnav(){
var cbox=document.getElementsByClassName('box');
for(var i=0;i<cbox.length;i++){
cbox[i].innerHTML=cbox[i].id;
<!--so far I have set their captions after setting their ids same as how I want them to appear.-->
<!--what comes next is what doesn't really work.-->
getElementById(cbox[i].id).onclick=Mclick(cbox[i].id);
}
};
function Mclick(id){alert(id);}
The whole thing is in a js file, and promptly linked from my html file.
As planned, all the buttons should appear and be clickable, but what is happening instead is only one of them is visible and that one is not working when I click on it.
When I create a lot of div-oriented buttons, I wish I could run for loop and be able to assign each of them as clickable instead of writing lines as many as they are.
How do you assign onclick within for loop in javascript?
You're calling the function instead of assigning it.
getElementById(cbox[i].id).onclick = Mclick;
Of course, now your function will receive an event argument instead of an id. (Passing the id inside the loop is a huge pain; easiest fix is to not bother trying.) But it also gets the attached element as this, which is convenient:
function Mclick() {
alert(this.id);
}
Other comments:
You should try not to be in the habit of using innerHTML if you're not assigning a string that contains known HTML. Saves you from having to care about escaping. Use textContent instead.
Assigning to onclick is a bit inflexible; you can only ever assign one click handler this way, and it's hard to notice if you accidentally overwrote an existing handler. Use addEventListener.
getElementById(element.id) should surely be equivalent to element.
Don't use HTML comments within JavaScript! :) They only work for... weird backwards-compatibility reasons. JavaScript comments are either // ... or /* ... */.
Best not to capitalize a function name unless it's supposed to be a constructor; you may notice that SO's highlighting made Mclick green, because it thinks it's a class name.
So I'd end up with:
function initnav() {
var cbox = document.getElementsByClassName('box');
for(var i = 0; i < cbox.length; i++) {
cbox[i].textContent = cbox[i].id;
cbox[i].addEventListener('click', alert_id);
}
}
function alert_id(event) {
alert(this.id);
}
So basically you don't call the for loop since the for loop is in the function. If you want to call all your variables and the statements in the for loop you have put the statements in the function and call the function outside of the function but inside of the script.

JavaScript code explanation (events + canvas) [sigma.js]

Here is some code that I have from sigma.js:
function f(event)
{
sigInst.iterNodes(function(n){
node = n;
},[event.content[0]]);
alert();
}
sigInst.bind('click',f).bind('outnodes',f).draw();
I don't understand this:
from where function f gets the event? no one passes it.
line },[event.content[0]]);
Can I add events to canvas elements? sigma.js draws a canvas and then (I don't understand how) there is an event listeners on click and outnodes. How does this happens?
Thanks
from where function f gets the event? no one passes it.
it is sent automatically by the bind function - when the handler is executed.
line },[event.content[0]]);
It's just an argument for the iterNodes function which is an array of size 1 which has the value of a property named content which is also an array , so it takes its first cell.
Sigma uses its own custom events dispatcher. As you can see here:
https://github.com/jacomyal/sigma.js/blob/master/src/classes/eventdispatcher.js#L129
It dispatches events with certain parameters of its own. When you bind to something, it always is executed through some sort of dispatch/trigger. The dispatch/trigger, as seen in the link, adds custom parameters which is why you're able to access content.

javascript: register a second event listener. How to prevent certain events go to it

I have a bit of code where I register a keydown listener to my input type text.
The usual
if (...) {
mytext. addEventListener('keydown', myhandler ,true);
} else {
mytext.attachEvent('onkeydown', myhandler ); // older versions of IE
}
This works perfectly fine.
My problem begins when somebody using my API register an event listener keydown as well.
How can I ensure that certain events are not passed to his/her code unless I want so ?
In short, that would imply that event-notification would reach me first which I believe is not guaranteed to be the case ?
Note: this is nothing to do with event-propagation on parent events (bubbling up). I am just dealing with another listener on the same HTML element
Any suggestions, ideas ?
I thought of overriding the methods attachEvent and addEventListener but I am not sure if it's a sound idea or not.
Thanks
I also wouldn't recommend to override the addEventListener method. But I also think that it can't be bad to try certain things out for oneself to test and see if and where there are certain problems. So anyway, here is an idea of how you could do it:
var f = Element.prototype.addEventListener;
Element.prototype.addEventListener = function(type, func, x){
var that = this;
if (that.evts === undefined) {
that.evts = {};
}
if (that.evts[type] === undefined) {
that.evts[type] = [];
f.call(that, type, function(){
for (var i = 0; i < that.evts[type].length; i++) {
that.evts[type][i].apply(that, arguments);
}
}, x);
}
that.evts[type].push(func);
};
I'm not a hundred percent confident that I didn't miss something but in theory this should override the method in a way, that the order in which eventlisteners are added should be the same as the order in which the handlers are executed.
This is done by saving the handler functions in an array, every time the addEventListener method is called, and only add one single eventlistener that traverses that array and executes every function in it.
Of course you would also have to change the removeEventListener method to remove the items in your array. And you would have to do the same for Document.prototype.addEventListener and Window.prototype.addEventListener.
FIDDLE
Note that I added a property called evts (which contains the function arrays) to the element to which the listener was added. This is probably not the best way to do it, since anyone can override that property. It probably would be better to store your arrays somewhere in a local variable or at least rename it to something that is unlikely to be overridden by accident.

Binding jQuery click events via looping through hash grabs last element in hash?

I have a number of divs I am toggling on and off. I initially was manually binding the handlers(as per the code below), but decided to do some refactoring. However, a binding issue arose where the last key/value in the hash is the one always selected. In the example code, that would be the contact_data div. I think the problem is that the data is not being closed over, but I am not certain how to force this in JS.
var link_div = {
"#about_toggle" : "#about_stuff",
//more pairs
"#contact_toggle" : "#contact_data"
};
/*
* Before refactoring:
$("#about_toggle").click( function()
{
$("#about_stuff").toggle();
});
*/
//After
for(var key in link_div)
{
$(key).click(function()
{
alert(link_div[key]);
toggle_on_element(link_div[key]);
});
}
Actually the problem is, the data is being closed over. The anonymous function you pass in the event handler will close over your loop variable, therefore all event handlers reference the same variable. You need to invoke another function to avoid this:
for(var key in link_div)
{
$(key).click(function(k)
{
return function() {
alert(link_div[k]);
toggle_on_element(link_div[key]);
}
}(key));
}
It's all about closures, which go back to the environment frame binding model of the language. Essentially key iterates through your loop and at the end, points to the last value in the map (which by the way, may not guarantee order, im not sure of the specific implementations). As such, the anonymous function (which is shared among all the elements, because it was created once, and therefore refers to one environment frame in memory) will for all elements, toggle link_div[key], but key, for all elements, has only one value.
You can solve this by either wrapping the anonymous function to make it unique for each binding (like jAndy did), or use a naming convention to make life a little easier:
$('.togglers').click(function(){ $('#'+$(this).attr('id')+'_stuff').toggle(); });

Rendering suggested values from an ext Combobox to an element in the DOM

I have an ext combobox which uses a store to suggest values to a user as they type.
An example of which can be found here: combobox example
Is there a way of making it so the suggested text list is rendered to an element in the DOM. Please note I do not mean the "applyTo" config option, as this would render the whole control, including the textbox to the DOM element.
You can use plugin for this, since you can call or even override private methods from within the plugin:
var suggested_text_plugin = {
init: function(o) {
o.onTypeAhead = function() {
// Original code from the sources goes here:
if(this.store.getCount() > 0){
var r = this.store.getAt(0);
var newValue = r.data[this.displayField];
var len = newValue.length;
var selStart = this.getRawValue().length;
if(selStart != len){
this.setRawValue(newValue);
this.selectText(selStart, newValue.length);
}
}
// Your code to display newValue in DOM
......myDom.getEl().update(newValue);
};
}
};
// in combobox code:
var cb = new Ext.form.ComboBox({
....
plugins: suggested_text_plugin,
....
});
I think it's even possible to create a whole chain of methods, calling original method before or after yours, but I haven't tried this yet.
Also, please don't push me hard for using non-standard plugin definition and invocation methodics (undocumented). It's just my way of seeing things.
EDIT:
I think the method chain could be implemented something like that (untested):
....
o.origTypeAhead = new Function(this.onTypeAhead.toSource());
// or just
o.origTypeAhead = this.onTypeAhead;
....
o.onTypeAhead = function() {
// Call original
this.origTypeAhead();
// Display value into your DOM element
...myDom....
};
#qui
Another thing to consider is that initList is not part of the API. That method could disappear or the behavior could change significantly in future releases of Ext. If you never plan on upgrading, then you don't need to worry.
So clarify, you want the selected text to render somewhere besides directly below the text input. Correct?
ComboBox is just a composite of Ext.DataView, a text input, and an optional trigger button. There isn't an official option for what you want and hacking it to make it do what you want would be really painful. So, the easiest course of action (other than finding and using some other library with a component that does exactly what you want) is to build your own with the components above:
Create a text box. You can use an Ext.form.TextField if you want, and observe the keyup event.
Create a DataView bound to your store, rendering to whatever DOM element you want. Depending on what you want, listen to the 'selectionchange' event and take whatever action you need to in response to the selection. e.g., setValue on an Ext.form.Hidden (or plain HTML input type="hidden" element).
In your keyup event listener, call the store's filter method (see doc), passing the field name and the value from the text field. e.g., store.filter('name',new RegEx(value+'.*'))
It's a little more work, but it's a lot shorter than writing your own component from scratch or hacking the ComboBox to behave like you want.
#Thevs
I think you were on the right track.
What I did was override the initList method of Combobox.
Ext.override(Ext.form.ComboBox, {
initList : function(){
If you look at the code you can see the bit where it renders the list of suggestions to a dataview. So just set the apply to the dom element you want:
this.view = new Ext.DataView({
//applyTo: this.innerList,
applyTo: "contentbox",
#qui
Ok. I thought you want an extra DOM field (in addition to existing combo field).
But your solution would override a method in the ComboBox class, isn't it? That would lead to all your combo-boxes would render to the same DOM. Using a plugin would override only one particular instance.

Categories

Resources