I have a question about jquery and DOM manipulation. How do you handle with DOM controls for e.g.
I have to get value from text input so I could this in ways:
var SomeClass = function() {
var control;
this.setControl = function(c) {
control = c;
}
this.getValue = function() {
return control.val();
}
}
$(document).ready(function() {
var sc = new SomeClass(); // of course control could be passed in contructor as well
sc.setControl($('#CONTROL'));
console.log(sc.getValue());
});
OR
var SomeClass = function() {
var control = $('#CONTROL');
this.getValue = function() {
return control.val();
}
}
$(document).ready(function() {
var sc = new SomeClass();
console.log(sc.getValue());
});
what is your opinion? What is better or maybe this is pile of trash therefore what is the best solution. Plz dont send me to backbone, spine and so on Im interesed in only in jquery.
best!
EDIT:
do you separate logic from UI or you are mixing it?
more complicated example
in js file you have a class that uses text control and in the secound js file also you need values from this input. What you are doing? you just call everytime $('#control') or create a third js file where would be a separated "class" to manipulate this input?
It would make more sense to move the setValue() inside the constructor:
SomeClass = function(c) {
var control = c;
return {
getValue: function() {
return control.val();
}
}
}
var x = new SomeClass($('input'));
alert(x.getValue());
However, I'm not sure how valuable this kind of information hiding will be. Perhaps as some kind of view wrapper.
In many cases you wouldn't need this wrapper, so just:
var $x = $('input'); // keep reference to a bunch of <input> elements.
Related
So, I've been searching through some existing questions dealing with re-usable items in HTML and Javascript, and I'm not sure if there's anything that gives me the start I'm looking for. I'm not super well-versed in js, but rather than re-write the same code over and over again and have to perform the upkeep on it, I'd prefer to build a re-usable framework that I can apply in several places.
The basic layout is this: There's an input field with an "Add" button, each time you add a name, it displays below the input with a checkbox. When you uncheck it, the name is removed from the list.
I'm fine with styling and building the HTML, what I'm lost on is developing an object in js that I can apply in multiple places. What I had in mind was this:
function createInputControl(targetElementId) {
var newInputControl = new ItemInputControl();
newInputControl.onItemAdded = customItemAddedCallback;
newInputControl.onItemRemoved = customItemRemovedCallback;
newInputControl.createInElement(targetElementId);
}
That's the start I'm looking for. An object that I can create that has designated callbacks for when an item is added or removed via user interaction, and a way for me to draw it within an existing element on my page.
EDIT: What I'm looking for here is a skeleton of a javascript object (named ItemInputControl above) with these functions / properties that I can re-use throughout my site.
Ok, so If I understand you correctly - you're looking for help on how to make a globally accessible variable that can be used in your entire application, like jQuery. You have two main options for what you are looking to do
First - you could use an Object Literal, which exposes a single global variable and all of your methods are contained within:
(function (window) {
var inputControl = {
onItemAdded: function () {
// do stuff
},
onItemRemoved: function () {
// do stuff
},
createInElement: function (targetElementId) {
// do stuff
}
};
window.ItemInputControl = inputControl;
})(window);
This is used like so:
ItemInputControl.createInElement("elementId");
Your second option is to use Prototype:
(function (window) {
var inputControl = function () {
// Constructor logic
return this;
};
inputControl.prototype.onItemAdded = function () {
// do stuff
return this;
};
inputControl.prototype.onItemRemoved = function () {
// do stuff
return this;
};
inputControl.prototype.createInElement = function (elementId) {
// do stuff
return this;
};
window.ItemInputControl = inputControl;
})(window);
This would be used like so:
var newInputControl = new ItemInputControl();
newInputControl.createInElement("elementId");
For most cases in individual applications - I prefer to use Object Literals for my framework. If I were building a widely distributed javascript framework, I would probably use a prototype pattern. You can read more on prototype patters here: http://www.htmlgoodies.com/beyond/javascript/some-javascript-object-prototyping-patterns.html
Well, I'm not sure if this is exactly helpful, but perhaps it will contain a few ideas for you.
The two HTML elements needed are stored as format strings, and everything is dynamically added/removed in the DOM.
var listid = 0;
$(document).ready(function() {
var controlHtml = + // {0} = mainid
'<div>' +
'<input id="text-{0}" type="text" />' +
'<div id="add-{0}" class="addButton">Add</div>' +
'</div>' +
'<div id="list-{0}"></div>';
var listHtml = + // {0} = mainid, {1} = listid, {2} = suplied name
'<div id="list-{0}-{1}"><input id="checkbox-{0}-{1}" type="checkbox class="checkboxClass" checked />{2}<div>';
$('#content').append(controlHtml.f('0'));
$('.addButton').click(function(e) { addClick(e); });
});
function addClick(e) {
var id = e.currentTarget.id.split('-')[1];
var name = $('text-' + id).val();
$('.list-' + id).append(listHtml.f(id, listid, name));
listid++;
$('.checkboxClass').click(function() { checkboxClick(e); });
}
function checkboxClick(e) {
$('#' + e.currentTarget.id).remove();
}
String.prototype.f = function () { var args = arguments; return this.replace(/\{(\d+)\}/g, function (m, n) { return args[n]; }); };
And of course very minimal HTML to allow a hook for adding your control:
<body>
<div id="content"></div>
</body>
I've wrote a small example for readability.. I'm trying to get my head around proper js app structure.
I'm new to writing larger js apps. Right now, I've got a constructor, and a whole bunch of prototype functions. I always thought you're NOT supposed to call (or return) from one function to another. But now, at the bottom of my app, I'm instantiating my constructor, then having to call a bunch of functions, as well as build in conditional statements to handle the execution, which seems totally wrong.
This is the idea I've been doing:
function TodaysFood(b, l)
{
this.breakfast = b;
this.lunch = l;
}
TodaysFood.prototype.firstMeal = function()
{
return console.log(this.breakfast);
}
TodaysFood.prototype.secondMeal = function()
{
return console.log(this.lunch);
}
var app = new TodaysFood("eggs", "sandwich");
app.firstMeal();
app.secondMeal();
I'm wondering if this function "linking" is proper?
function TodaysFood(b, l)
{
this.breakfast = b;
this.lunch = l;
}
TodaysFood.prototype.firstMeal = function()
{
return this.secondMeal(this.breakfast);
}
TodaysFood.prototype.secondMeal = function(firstMeal)
{
var twoMeals = [firstMeal, this.lunch];
return this.whatIAte(twoMeals);
}
TodaysFood.prototype.whatIAte = function(twoMeals)
{
return console.log(twoMeals);
}
var app = new TodaysFood("eggs", "sandwich");
app.firstMeal();
Stupid example, but I'm trying to understand how an app should flow. Should I be able to write my whole app in separate, but linked functions, then just kick the whole thing off by instantiating the constructor, and maybe calling one function. Or is the first example more correct -- writing independent functions, then handling the interaction between them after you've instantiate the constructor?
Thanks for any help.
You may want to make it modular, Ala Node.js or within the browser using RequireJS
Here is a slight variation of the second example you could consider, view fiddle
var TodaysFood = function (b, l) {
var self = this;
this.breakfast = b;
this.lunch = l;
this.firstMeal = function () {
console.log(this.breakfast);
return self;
};
this.secondMeal = function () {
console.log(this.lunch);
return self;
}
this.allMeals = function () {
return this.firstMeal().secondMeal();
};
}
var food = new TodaysFood('eggs', 'sandwich');
food.firstMeal().secondMeal().allMeals();
If you plan to use node.js or RequireJS then the above could be modularized by replacing the last two test lines of code with,
module.exports = TodaysFood;
If this is made modular then you would remove the constructor var TodaysFood = function(b, l) { ... and instead accept arguments for b & l within your individual methods like firstMeal & secondMeal. This would make it static and prevent collisions with the constructor values.
I'm sorry if this is basic. I'm self taught and stuggle with what are probably simple features of Javascript programming. Can anyone show me what I should be searching for to find the right kind of answer, OR help me in general with this one?
I have a function inside a UI window to quickly populate it with panels. But I can't seem to get the values entered into each panel get OUT of the UI and into the main "DoSomething" function.
The variable (dlg.tryThis) won't update? Why? What do I need to do?
// making a simple UI
function UI_Builder(doSomething)
{
var dlg = new Window( "dialog", "options" );
dlg.mainGroup = dlg.add( "group", undefined,"main" );
//Created a simple function so I can re-create panels and values very easy
function makePanel (panelName, val) {
var newPanel = eval("dlg.mainGroup." + panelName);
newPanel = dlg.mainGroup.add( 'panel', undefined, panelName );
newPanel.Start = newPanel.add("edittext", [0,0,30,20], val);
dlg.Start = newPanel.Start.text
newPanel.Start.onChanging = function()
{
dlg.Start = newPanel.Start.text
alert("Changed " + panelName + " to: " + dlg.Start) // THIS alerts correctly. It know what value I changed and where I changed it.
return dlg.Start;
}
return dlg.Start;
}
// calling the function to create panels.
dlg.tryThis = makePanel("tryThis", 4);
dlg.anotherOne = makePanel("anotherOne", 3);
dlg.again = makePanel("again", 2);
dlg.mainGroup.btnCancel = dlg.mainGroup.add( "button",[0,0,130,70], "doSomething" );
dlg.mainGroup.btnCancel.onClick = function()
{
doSomething();
}
return dlg
}
// all the UI builder is doing is showing the UI
var dlg = UI_Builder(doSomething);
dlg.show();
function doSomething()
{
dlg.close();
alert(dlg.tryThis) // this is not the NEW value? How do I get it to be the NEW value updated by the ".onChanging" function?
}
try passing dlg to the event handler instead of declaring it globally:
dlg.mainGroup.btnCancel.onClick = function() {
doSomething(dlg);
}
function doSomething(dlg) {
dlg.close();
alert(dlg.tryThis);
}
If you take this approach you should not declare var dlb globally, to avoid confusion. (Shadowing variables is generally a bad practice)
You're calling doSomething without any context.
Use func.apply:
dlg.mainGroup.btnCancel.onClick = function()
{
doSomething.apply(this, []);
}
And handle the object as 'this':
function doSomething()
{
this.close();
alert(this.tryThis) // this is not the NEW value? How do I get it to be the NEW value updated by the ".onChanging" function?
}
There are many topics related to my question and i have been through most of them, but i haven't got it right. The closest post to my question is the following:
How to call functions that are nested inside a JQuery Plugin?
Below is the jquery plugin i am using. On resize, the element sizes are recalculated. I am now trying to call the function resizeBind() from outside of the jquery plugin and it gives me error
I tried the following combinations to call the function
$.fn.splitter().resizeBind()
$.fn.splitter.resizeBind()
Any ideas, where i am getting wrong?
;(function($){
$.fn.splitter = function(args){
//Other functions ......
$(window).bind("resize", function(){
resizeBind();
});
function resizeBind(){
var top = splitter.offset().top;
var wh = $(window).height();
var ww = $(window).width();
var sh = 0; // scrollbar height
if (ww <0 && !jQuery.browser.msie )
sh = 17;
var footer = parseInt($("#footer").css("height")) || 26;
splitter.css("height", wh-top-footer-sh+"px");
$("#tabsRight").css("height", splitter.height()-30+"px");
$(".contentTabs").css("height", splitter.height()-70+"px");
}
return this.each(function() {
});
};
})(jQuery);
I had the same problem. Those answers on related posts didn't work for my case either. I solved it in a round about way using events.
The example below demonstrates calling a function that multiplies three internal data values by a given multiplier, and returns the result. To call the function, you trigger an event. The handler in turn triggers another event that contains the result. You need to set up a listener for the result event.
Here's the plugin - mostly standard jQuery plugin architecture created by an online wizard:
(function($){
$.foo = function(el, options){
// To avoid scope issues, use 'base' instead of 'this'
var base = this;
// Access to jQuery and DOM versions of element
base.$el = $(el);
base.el = el;
// Add a reverse reference to the DOM object
base.$el.data("foo", base);
base.init = function(){
base.options = $.extend({},$.foo.defaultOptions, options);
// create private data and copy in the options hash
base.private_obj = {};
base.private_obj.value1 = (base.options.opt1);
base.private_obj.value2 = (base.options.opt2);
base.private_obj.value3 = (base.options.opt3);
// make a little element to dump the results into
var ui_element = $('<p>').attr("id","my_paragraph").html(base.private_obj.value1 +" "+ base.private_obj.value2+" " +base.private_obj.value3);
base.$el.append(ui_element);
// this is the handler for the 'get_multiplied_data_please' event.
base.$el.bind('get_multiplied_data_please', function(e,mult) {
bar = {};
bar.v1 = base.private_obj.value1 *mult;
bar.v2 = base.private_obj.value2 *mult;
bar.v3 = base.private_obj.value3 *mult;
base.$el.trigger("here_is_the_multiplied_data", bar);
});
};
base.init();
}
$.foo.defaultOptions = {
opt1: 150,
opt2: 30,
opt3: 100
};
$.fn.foo = function(options){
return this.each(function(){
(new $.foo(this, options));
});
};
})(jQuery);
So, you can attach the object to an element as usual when the document is ready. And at the same time set up a handler for the result event.
$(document).ready(function(){
$('body').foo();
$('body').live('here_is_the_multiplied_data', function(e, data){
console.log("val1:" +data.v1);
console.log("val2:" +data.v2);
console.log("val3:" +data.v3);
$("#my_paragraph").html(data.v1 +" "+ data.v2+" " +data.v3);
});
})
All that's left is to trigger the event and pass it a multiplier value
You could type this into the console - or trigger it from a button that picks out the multiplier from another UI element
$('body').trigger('get_multiplied_data_please', 7);
Disclaimer ;) - I'm quite new to jQuery - sorry if this is using a hammer to crack a nut.
resizeBind function is defined as private so you cannot access it from outside of it's scope. If you want to use it in other scopes you need to define it like that
$.fn.resizeBind = function() { ... }
Then you would call it like that $(selector').resizeBind()
You have defined the resizeBind function in a scope that is different from the global scope. If you dont'use another javascript framework or anything else that uses the $ function (to prevent conflict) you can delete the
(function($){
...
})(jQuery);
statement and in this way the function will be callable everywhere without errors
I didn't test it:
this.resizeBind = function() { .... }
I'm trying to create a simple, small and basic javascript framework just for learning purposes.
But the thing is that i'm allready stuck at the very basics.
I'm trying to do something like this:
$('testdiv').testFunction();
And the code i've written for that:
var elementID;
var smallFramework = {
$:function(id) {
this.elementID = id;
},
testFunction:function() {
alert(this.elementID);
}
};
window.$ = smallFramework.$;
But in return I get:
$('testdiv) is undefined
Can anyone help me with this small and hopefully easy question?
To get the behavior you're expecting, you need the $ function to return an object with a method named testFunction.
Try:
var smallFramework = // an object for namespacing
{
$:function(id) // the core function - returns an object wrapping the id
{
return { // return an object literal
elementID: id, // holding the id passed in
testFunction: function() // and a simple method
{
alert(this.elementID);
}
};
}
};
Of course, there are many other ways to achieve the behavior you desire.
If you're trying to add methods to an HTML element you could do something along these lines.
$ = function( elementId ) {
var element = document.getElementById( elementId );
element.testFunction = function(){
alert( this.id );
return this; // for chaining
}
return element;
}
$('test').testFunction();
Try
smallFramework.$('testdiv');
instead. According to the code you posted, that's where your $ function ended up.
Or alternatively, it looks like you're trying to replicate something like jQuery. You might want to try something like this.
var $ = smallFramework = (function () {
var f =
{
find:function(id) {
f.elementID = id;
return f; //every function should return f, for chaining to work
},
testFunction:function() {
alert(f.elementID);
return f;
}
}
return f.find //the find function will be assigned to $.
//and also assigned to smallFramework.
//the find function returns f, so you get access to testFunction via chaining
// like $("blah").testFunction()
})() //note this function gets called immediately.
this code may look confusing to someone new to JavaScript because it depends heavily on the concept of closures. I suggest that if this doesn't make sense, spend some time at Douglas Crockford's JavaScript website. This is important because the code above will bite if you happen to use this in the find function because this won't be bound to f, as you may expect it to be when you use it from $ or smallFramework.