jQuery & Objects, trying to make a lightweight widget - javascript

Trying to make a make generic select "control" that I can dynamically add elements to, but I am having trouble getting functions to work right.
This is what I started with.
$select = $("<select></select>");
$select.addOption = function(value,text){
$(this).append($("<option></option>").val(value).text(text));
};
This worked fine alone but anytime $select is .clone(true)'ed the addOption() function is lost.
This is my object approach but still the function does not work.
function $selectX() {
return $("<select></select>");
}
$selectX.prototype.addOption() = function(value,text){
$(this).append($("<option></option>").val(value).text(text));
};
Hack solution is to add the function manually after creation:
$nameSelect= new $selectX;
$nameSelect.addOption = function(value,text){
$(this).append($("<option></option>").val(value).text(text));
};
Am I barking up the wrong tree?

To add new method to jQuery You need to use jQuery.fn.methodName attribute, so in this case it will be:
jQuery.fn.addOption = function (value, text) {
jQuery(this).append(jQuery('<option></option>').val(value).text(text));
};
But keep in mind that this addOption will be accessible from result of any $() call.

Related

Add function to set of DOM elements using jQuery?

My journey being at least on above basic level in front end is still on and I stumbled upon quite a big problem recently.
I can select DOM element, like
var element=document.getElementById("elementid")
and then add some function to it, like this
function testFunction() {
alert(this.getAttribute('data-self'));
}
element.customFunction=testFunction;
But is there by chance any way of doing this using jQuery?
Tried with attr(), prop(), data() and without any luck on that matter. data() was a close one though, because it allows me to execute function using $('#my-element-id').data('customFunction')(); for example, but still doesn't solve my problem, as this new property of that selected buttons is not accessible any other way.
To sum up: What is the simplest way to add generic function (like in an example) to collection of DOM elements in a way that it's accessible like any other property?
Adding a function directly to a DOM element is not considered a good practice for a variety of reasons.
I'd suggest a jQuery plugin method which is pretty easy:
jQuery.fn.myMethod = function() {
// iterate all items in the jQuery collection and apply any logic you want
return this.each(function() {
// `this` will be the DOM element so you can carry out your operation here
// for example, to flip a background color
if (this.tagName === "INPUT") {
this.style.backgroundColor = "red";
} else {
this.style.backgroundColor = "blue";
}
});
}
// usage of jQuery plugin method:
$("#elementid").myMethod();
$(".boxes, .ovals, .containers").myMethod();
You can also pass arguments to your jQuery plugin method and use those arguments in the implementation of your custom method.
It's pretty similar. jQuery just returns an object so you can add functions to it.
var myElement = $('#elementid');
function testFunction() {
alert($(this).attr('data-self'));
}
myElement.customFunction=testFunction;
myElement.customFunction();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="elementid" data-self="some value"></div>

Can I copy $ so that it always uses a context when creating jQuery objects?

I need to incorporate a little canvas app I built into a huge mess of a Sharepoint site where I do not have any control over anything, everything. The app will go into a "WebPart" wrapper div. To keep things nice and tidy in my JS I was thinking I could a cloned version of jQuery that always uses my wrapper object as context so that I can be sure I'll never select and alter anything outside of my wrapper like:
var ctxed$ = ctxjQuery('#wrapper-el'); //this is what I don't get
(function($){
$('p').addClass('awesome'); //should only affect elements inside #wrapper-el
})(ctxed$)
Anyhow, things I tried did not work out. I tried using $.proxy which did not work as I expected (as context is a different thing here...).
This is the point where I am stuck at the moment:
function getContextifiedjQuery(ctx) {
var fn = function () {
return $.call(arguments[0], ctx);
}
$.each($, function (k, v) {
fn[k] = v;
});
return fn;
}
var wrapper$ = getContextifiedjQuery('#wrapper');
wrapper$('p').css('color', 'red');
wrapper$.each([1, 2, 3], function () {
alert(this)
});
which works as a simple test case, yet when I try to use it in my project I get circular references and things go awry. What is it that I am missing here?
What you are doing with $("p") is basically
ctxjQuery('#wrapper-el')('p')
If you want to work off the context it would be
(function($){
$.find('p').addClass('awesome'); //should only affect elements inside #wrapper-el
})(ctxed$)
If you want to write the wrapper, it would look something like this, but it will only support find(). It will override everything else.
function ctxjQuery(context) {
return function (selector) {
return $(context).find(selector);
}
}
var ctxed$ = ctxjQuery('#wrapper-el'); //this is what I don't get
(function ($) {
$('p').addClass('awesome'); //should only affect elements inside #wrapper-el
})(ctxed$)
JSFiddle
and thinking about it again, you can do something like this:
function ctxjQuery(context) {
var fnc = function(selector){ return $(context).find(selector); };
var $Fnc = $.extend( fnc, $);
return $Fnc;
}
var ctxed$ = ctxjQuery('#wrapper-el'); //this is what I don't get
(function ($) {
console.log($.each);
$('p').addClass('awesome'); //should only affect elements inside #wrapper-el
})(ctxed$)
JSFiddle
It will break a lot of stuff!!! Example $("<p/>") will not create an element since it is overloaded with find(). Basically you would need to recreate the jQuery core function to get all the functionality!
Try this
var ctxed$ = function (selector)
{
return $(selector, '#wrapper-el');
}
(function($){
$('p').addClass('awesome'); //should only affect elements inside #wrapper-el
})(ctxed$);

jQuery plugin namespace for specific objects

I'm am trying to create a jQuery plugin that will add new namespace functions to the context object(s), while maintaining full chain-ability. I'm not sure if it's possible, but here's an example of what I have so far:
(function ($) {
var loadScreen = $('<div />').text('sup lol');
$.fn.someplugin = function (args) {
var args = args || {},
$this = this;
$this.append(loadScreen);
return {
'caption' : function (text) {
loadScreen.text(text);
return $this;
}
};
}
})(jQuery);
This works fine if I do $(document.body).someplugin().caption('hey how\'s it going?').css('background-color', '#000');
However I also need the ability to do $(document.body).someplugin().css('background-color', '#000').caption('hey how\'s it going?');
Since .someplugin() returns it's own object, rather than a jQuery object, it does not work as expected. I also need to be able to later on access .caption() by $(document.body). So for example if a variable is not set for the initial $(document.body).someplugin(). This means that somehow how .caption() is going to be set through $.fn.caption = function () ... just for the document.body object. This is the part which I'm not quite sure is possible. If not, then I guess I'll have to settle for requiring that a variable to be set, to maintain plugin functions chain-ability.
Here's an example code of what I expect:
$(document.body).someplugin().css('background-color', '#000');
$('.non-initialized').caption(); // Error, jQuery doesn't know what caption is
$(document.body).caption('done loading...');
Here's what I'm willing to settle for if that is not possible, or just very inefficient:
var $body = $(document.body).someplugin().css('background-color', '#000');
$('.non-initialized').caption(); // Error, jQuery doesn't know what caption is
$body.caption('done loading...');
The be jquery-chainable, a jQuery method MUST return a jQuery object or an object that supports all jQuery methods. You simply have to decide whether you want your plugin to be chainable for other jQuery methods or whether you want it to return your own data. You can't have both. Pick one.
In your code examples, you could just define more than one plugin method, .someplugin() and .caption(). jQuery does not have a means of implementing a jQuery plugin method that applies to one specific DOM object only. But, there is no harm in making the method available on all jQuery objects and you can only use it for the ones that it makes sense for.
I think you could use this:
(function ($) {
var loadScreen = $('<div />').text('sup lol');
$.fn.someplugin = function (args) {
var args = args || {},
$this = this;
$this.append(loadScreen);
return(this);
}
$.fn.caption = function (text) {
loadScreen.text(text);
return this;
}
})(jQuery);
$(document.body).someplugin().css('background-color', '#000');
$('.non-initialized').caption('whatever');
$(document.body).caption('done loading...');
If there's supposed to be some connection between the two .caption() calls, please explain that further because I don't follow that from your question.

Customising a JQuery Element

Is it inadvisable to add methods to a JQuery element?
eg:
var b = $("#uniqueID");
b.someMethod = function(){};
Update
Just to clarify, I am working on a JS-driven app that is binding JSON data to local JS objects that encapsulate the business logic for manipulating the actual underlying DOM elements. The objects currently store a reference to their associated HTML element/s. I was thinking that I could, in effect, merge a specific instance of a jquery element with it's logic by taking that reference add adding the methods required.
Well, there's nothing inherently wrong with it. It is, however, pretty pointless. For example:
$('body').someMethod = function(){};
console.log($('body').someMethod); // undefined
You are attaching the new function only to that selection, not to all selections of that element.
What you should do instead is to add a new function to jQuery.fn, which is a shortcut for jQuery.prototype:
jQuery.fn.someMethod = function() {
if (this[0].nodeName == 'body') {
// do your function
}
return this; // preserve chaining
};
The problem is that your function would be quite transient. A further requery and it will be gone. You can extend the jQuery object itself by $.fn.someMethod = function() {} and this method will be available for all queries.
$.fn.someMethod = function() {}
var b = $("body");
b.someMethod();
Or you can create a jQuery plugin. You can define a plugin this way:
$.fn.someMethod = function(options) {
# ...
});
Call it using $('body').someMethod();

Using jQuery, how do I add elements to a jQuery object that's yet to be created?

Perhaps I am doing this wrong and suggestions on how to improve my code are appreciated. My situation is this: I have a toolbar with different elements that are populated by a callback. I do not want to use the show() or hide() commands, I prefer to use detach, but I think there should be a nice way to deal with it. Here's my code:
entryView = function _entryView() {
var menuButton = $('<div/>').addClass('menuButton');
toolBar();
$.getJSON('ajax', function(o) {
var $enum2 = ss.IEnumerator.getEnumerator(dto.TransmittalDates);
while ($enum2.moveNext()) {
var dateTime = $enum2.get_current();
$('.menu').append($('<div/>').addClass('menuitem').text(dateTime.toString()));
}
});
}
toolBar = function _toolBar() {
var flyoutMenu = $('<div/>').addClass('menu');
$('.menuButton').click(function(o) {
$('.menubutton').append(flyoutMenu);
});
I did a quick cut and paste and renamed the variables to make them make sense. As you can see on the entry I build the toolbar and the very last thing I do is the ajax call. The menu, however, is not created until the "click" event, so appending is not possible.
I realize that having global variables is bad, so I'm trying to avoid that, but I think the best situation would have the ajax call populate a Menu variable and when the DOM is created, to pull from that same Menu item. How do I pull this off? Is there a better way to do it?
Edit: Fubbed a bit on the toolbar function, I think I have it should be correct now.
I'm confused by some parts of your code:
What's the entryView function and when is it called?
Why does the toolBar function exist as opposed to being inline? From where else is it called?
Why are you creating functions like that? Creating a variable without var is bad practice, always makes global variables, and will be forbidden in ES5 strict mode. You should create functions like this:
var someFunction = function(arg1, arg2) { … };
or like this:
function someFunction(arg1, arg2) { … }
Why are you giving each function a second name (e.g. _toolBar)? The "private" name will only be in scope inside the function.
The menu doesn't have to be in global scope, just in a scope that's common to both functions.
I would refactor it like this (knowing very little about the design of your application), inlining toolBar:
function entryView() {
var menuButton = $('<div/>' {'class': 'menuButton'}), menu = $('<div/>', {'class': 'menu'});
$.getJSON('ajax', function(o) {
var $enum2 = ss.IEnumerator.getEnumerator(dto.TransmittalDates);
while ($enum2.moveNext()) {
var dateTime = $enum2.get_current();
$('.menu').append($('<div/>' {'class': 'menuItem'}).text(dateTime.toString()));
}
});
menuButton.click(function(o) {
menuButton.append(menu);
});
}

Categories

Resources