adding a js method - object

I have a js function that is named getID which is basically return document.getElementById(id)
I want to make another function, getTag that would return getElementsByTagName.
The part that I can't seem to manage is that I want to be able to call them like this:
getID('myid').getTag('input') => so this would return all the input elements inside the element with the id myid
Thanks!
ps: getTag would also have to work if it's called by it's own, but then it would just return document.getElementsByTagName
UPDATE:
Thanks to all that have replied! Using your suggestions I came up with this, which works well for me:
function getEl(){
return new getElement();
}
function getElement() {
var scope = document;
this.by = function(data){
if (data.id) scope = scope.getElementById(data.id);
if (data.tag) scope = scope.getElementsByTagName(data.tag);
return scope;
}
}
and I use it like this:
var inputs = getEl().by({id:"msg", tag:"input"});

The way to do that is to prototype Object. To do that, you'll need the following piece of code:
Object.prototype.getTag = function(tagName) {
return this.getElementsByTagName(tagName);
}
However, this will expand all objects because what you really need to prototype, an HTMLElement, is very hard to do consistently. All the experts agree that you should never expand the Object prototype. A much better solution would be to create a function that gets the tag name from another argument:
function getTag(tagName, element) {
return (element || document).getElementsByTagName(tagName);
}
// Usage
var oneTag = getTag('input', getID('myid')); // All inputs tags from within the myid element
var twoTag = getTag('input'); // All inputs on the page

This would require that whatever is returned by getID('myid') (an HTML element) exposes a method named getTag(). This is not the case. Browsers implement the DOM specification and expose the methods defined there.
While you technically can enhance native objects with your own methods, it's best not to do it.
What you try to do has been solved rather nicely in JS libraries like jQuery already, I recommend you look at one of them before you invest time in mimicking what they can do. For example, your line of code would become:
$("#myid input")
in jQuery. jQuery happens to be the most widely used JS library around, there are many others.

Basically, you're going to create a single object that contains each of your methods and also stores all data returned by the native functions. It would look something like this (not tested, but you get the idea):
var MyLib = {
getID: function(id) {
var element = document.getElementById(id);
this.length = 1;
this[0] = element;
return this;
},
getTag: function(tag) {
var elements;
if (this.length) {
for (var i = 0; i < this.length; i++) {
var byTag = this[i].getElementsByTagName(tag);
for (var j = 0; j < byTag.length; j++) {
elements.push(byTag[j]);
}
}
}
for (var i = 0; i < elements.length; i++) {
this[i] = elements[i];
}
this.length = elements.length;
return this;
}
};
You can then use it like this:
var elements = MyLib.getID('myid').getTag('input');
for (var i = 0; i < elements.length; i++)
console.log(elements[i]); // Do something
The only real problem with this approach (besides it being very tricky and hard to debug) is that you have to treat the result of every method like an array, even if there is only a single result. For example, to get an element by ID, you'd have to do MyLib.getID('myid')[0].
However, note that this has already been done before. I recommend you take a look at jQuery, if only to see how they accomplished this. Your code could be simplified to this:
$("#myid input")
jQuery is more lightweight than you think, and including it on your page will not slow it down. You have nothing to lose by using it.

Just use the DOMElement.prototype property.
You'll get something like this :
function getTag(tagName) {
return document.getElementsByTagName(tagName);
}
DOMElement.prototype.getTag = function(tagName) {
return this.getElementsByTagName(tagName);
}
But you should use jQuery for this.
EDIT: My solution doesn't work on IE, sorry !

You could define it as follows:
var Result = function(el)
{
this.Element = el;
};
Result.prototype.getTag = function(tagName)
{
return this.Element.getElementsByTagName(tagName);
};
var getTag = function(tagName)
{
return document.getElementsByTagName(tagName);
};
var getID = function(id)
{
var el = document.getElementById(id);
return new Result(el);
};
Whereby a call to getID will return an instance of Result, you can then use its Element property to access the HTML element returned. The Result object has a method called getTag which will return all child elements matching that tag from the parent result. We then also define a seperate getTag method which calls the document element's getElementsByTagName.
Still though...JQuery is so much easier... $("#myId input");

Unless this is an academic exercise on how to chain methods in JavaScript (it doesn't seem to be, you simply seem to be learning JavaScript), all you have to do is this:
var elements = document.getElementById("someIdName");
var elementsByTag = elements.getElementsByTagName("someTagName");
for (i=0; i< elementsByTag.length; i++) {
alert('found an element');
}
If you want to define a reusable function all you have to do is this:
function myFunction(idName,tagName) {
var elements = document.getElementById(idName);
var elementsByTag = elements.getElementsByTagName(tagName);
for (i=0; i< elementsByTag.length; i++) {
alert('found a ' + tagName + ' element within element of id ' + idName);
}
}
It's true that if this is all the JavaScript functionality you need on your page, then there is no need to import jQuery.

Related

Prevent nested lists in text-editor (froala)

I need to prevent/disable nested lists in text editor implemented in Angular. So far i wrote a hack that undos a nested list when created by the user. But if the user creates a normal list and presses the tab-key the list is shown as nested for a few milliseconds until my hack sets in back to a normal list. I need something like event.preventDefault() or stopPropagation() on tab-event keydown but unfortunately that event is not tracked for some reason. Also the froala settings with tabSpaces: falseis not showing any difference when it comes to nested list...in summary i want is: if the user creates a list and presses the tab-key that nothing happens, not even for a millisecond. Has anyone an idea about that?
Froala’s support told us, there’s no built-in way to suppress nested list creation. They result from TAB key getting hit with the caret on a list item. However we found a way to get around this using MutationObserver
Basically we move the now nested list item to his former sibling and remove the newly created list. Finally we take care of the caret position.
var observer = new MutationObserver(mutationObserverCallback);
observer.observe(editorNode, {
childList: true,
subtree: true
});
var mutationObserverCallback = function (mutationList) {
var setCaret = function (ele) {
if (ele.nextSibling) {
ele = ele.nextSibling;
}
var range = document.createRange();
var sel = window.getSelection();
range.setStart(ele, 0);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
};
var handleAddedListNode = function (listNode) {
if (! listNode.parentNode) {
return;
}
var parentListItem = listNode.parentNode.closest('li');
if (!parentListItem) {
return;
}
var idx = listNode.children.length - 1;
while (idx >= 0) {
var childNode = listNode.children[idx];
if (parentListItem.nextSibling) {
parentListItem.parentNode.insertBefore(childNode, parentListItem.nextSibling);
} else {
parentListItem.parentNode.appendChild(childNode);
}
--idx;
}
setCaret(parentListItem);
listNode.parentNode.removeChild(listNode);
};
mutationList.forEach(function (mutation) {
var addedNodes = mutation.addedNodes;
if (!addedNodes.length) {
return;
}
for (var i = 0; i < addedNodes.length; i++) {
var currentNode = addedNodes[i];
switch (currentNode.nodeName.toLowerCase()) {
case 'ol':
case 'ul':
handleAddedListNode(currentNode);
break;
// more optimizations
}
}
})
};

Javascript accessing object and array defined in modular function

This is a bit foreign to me and I'm probably not understanding it correctly. This is what I have:
var imgModule = (function() {
var imgLocations = {};
var images = [];
imgLocations.setImage = function(img, location) {
imgLocations[img] = location;
}
imgLocations.getImg = function(img) {
return imgLocations[img];
}
imgLocations.setImageArray = function(img) {
images.push(img);
}
imgLocations.getImageArray = function() {
return images;
}
return imgLocations;
}());
I want to be able to access the imgLocations Object and images array from outside this function. The setting functions work, but
document.getElementById("but").onclick = function() {
console.log(imgModule.imgLocations.getImageArray());
console.log(imgModule.imgLocations.getImg(imgName));
}
Both return "undefined". How do I access these variables? And how can I improve this function? Please be patient with me and explain what I'm doing wrong :) I'm trying to learn it the right way instead of defining a global variable outside all functions.
The reason why this isn't working, is because your imgModule is returning the imgLocations object. That being the case, imgModule will actually be the imgLocations object. So you would access your methods like so:
imgModule.setImage()
imgModule.getImg()
imgModule.getImageArray()
imgModule.setImageArray()
And as #gillesc stated. If you are wanting to keep the current syntax of imgModule.imgLocations.getImg() then you could return the imgLocations like so
return {
imgLocations: imgLocations
}
doing so would allow you to add more functionality to your module
return {
imgLocations: imgLocations,
otherObject: otherObject
}
...
imgModule.otherObject.someFunctionCall();
The problem is you are returning the object created and are not setting it as a property of an object.
So in your case this is how it would work.
document.getElementById("but").onclick = function() {
console.log(imgModule.getImageArray());
console.log(imgModule.getImg(imgName));
}
What you need to do is return it like this
return {
imgLocations: imgLocations
}
If you want the API you are attending to create and still have access to the array which you can not do currently.
You don't access imgModule.imgLocations, since what you return is imgLocations, you should access them as:
document.getElementById("but").onclick = function() {
console.log(imgModule.getImageArray());
console.log(imgModule.getImg(imgName));
}
It seems you try to write module pattern.
For deep understanding, I recommend you following article:
The Module Pattern, by Addy Osmani
and pay attention to example with counter:
var testModule = (function () {
var counter = 0;
return {
incrementCounter: function () {
return counter++;
},
resetCounter: function () {
console.log( "counter value prior to reset: " + counter );
counter = 0;
}
};
})();
// Usage:
// Increment our counter
testModule.incrementCounter();
// Check the counter value and reset
// Outputs: counter value prior to reset: 1
testModule.resetCounter();

Define a literal Javascript object so a property referenced directly calls a function and not its sub-ordinates [duplicate]

JavaScript allows functions to be treated as objects--if you first define a variable as a function, you can subsequently add properties to that function. How do you do the reverse, and add a function to an "object"?
This works:
var foo = function() { return 1; };
foo.baz = "qqqq";
At this point, foo() calls the function, and foo.baz has the value "qqqq".
However, if you do the property assignment part first, how do you subsequently assign a function to the variable?
var bar = { baz: "qqqq" };
What can I do now to arrange for bar.baz to have the value "qqqq" and bar() to call the function?
It's easy to be confused here, but you can't (easily or clearly or as far as I know) do what you want. Hopefully this will help clear things up.
First, every object in Javascript inherits from the Object object.
//these do the same thing
var foo = new Object();
var bar = {};
Second, functions ARE objects in Javascript. Specifically, they're a Function object. The Function object inherits from the Object object. Checkout the Function constructor
var foo = new Function();
var bar = function(){};
function baz(){};
Once you declare a variable to be an "Object" you can't (easily or clearly or as far as I know) convert it to a Function object. You'd need to declare a new Object of type Function (with the function constructor, assigning a variable an anonymous function etc.), and copy over any properties of methods from your old object.
Finally, anticipating a possible question, even once something is declared as a function, you can't (as far as I know) change the functionBody/source.
There doesn't appear to be a standard way to do it, but this works.
WHY however, is the question.
function functionize( obj , func )
{
out = func;
for( i in obj ){ out[i] = obj[i]; } ;
return out;
}
x = { a: 1, b: 2 };
x = functionize( x , function(){ return "hello world"; } );
x() ==> "hello world"
There is simply no other way to acheive this,
doing
x={}
x()
WILL return a "type error". because "x" is an "object" and you can't change it. its about as sensible as trying to do
x = 1
x[50] = 5
print x[50]
it won't work. 1 is an integer. integers don't have array methods. you can't make it.
Object types are functions and an object itself is a function instantiation.
alert([Array, Boolean, Date, Function, Number, Object, RegExp, String].join('\n\n'))
displays (in FireFox):
function Array() {
[native code]
}
function Boolean() {
[native code]
}
function Date() {
[native code]
}
function Function() {
[native code]
}
function Number() {
[native code]
}
function Object() {
[native code]
}
function RegExp() {
[native code]
}
function String() {
[native code]
}
In particular, note a Function object, function Function() { [native code] }, is defined as a recurrence relation (a recursive definition using itself).
Also, note that the answer 124402#124402 is incomplete regarding 1[50]=5. This DOES assign a property to a Number object and IS valid Javascript. Observe,
alert([
[].prop="a",
true.sna="fu",
(new Date()).tar="fu",
function(){}.fu="bar",
123[40]=4,
{}.forty=2,
/(?:)/.forty2="life",
"abc".def="ghi"
].join("\t"))
displays
a fu fu bar 4 2 life ghi
interpreting and executing correctly according to Javascript's "Rules of Engagement".
Of course there is always a wrinkle and manifest by =. An object is often "short-circuited" to its value instead of a full fledged entity when assigned to a variable. This is an issue with Boolean objects and boolean values.
Explicit object identification resolves this issue.
x=new Number(1); x[50]=5; alert(x[50]);
"Overloading" is quite a legitimate Javascript exercise and explicitly endorsed with mechanisms like prototyping though code obfuscation can be a hazard.
Final note:
alert( 123 . x = "not" );
alert( (123). x = "Yes!" ); /* ()'s elevate to full object status */
Use a temporary variable:
var xxx = function()...
then copy all the properties from the original object:
for (var p in bar) { xxx[p] = bar[p]; }
finally reassign the new function with the old properties to the original variable:
bar = xxx;
var A = function(foo) {
var B = function() {
return A.prototype.constructor.apply(B, arguments);
};
B.prototype = A.prototype;
return B;
};
NB: Post written in the style of how I solved the issue. I'm not 100% sure it is usable in the OP's case.
I found this post while looking for a way to convert objects created on the server and delivered to the client by JSON / ajax.
Which effectively left me in the same situation as the OP, an object that I wanted to be convert into a function so as to be able to create instances of it on the client.
In the end I came up with this, which is working (so far at least):
var parentObj = {}
parentObj.createFunc = function (model)
{
// allow it to be instantiated
parentObj[model._type] = function()
{
return (function (model)
{
// jQuery used to clone the model
var that = $.extend(true, null, model);
return that;
})(model);
}
}
Which can then be used like:
var data = { _type: "Example", foo: "bar" };
parentObject.createFunc(data);
var instance = new parentObject.Example();
In my case I actually wanted to have functions associated with the resulting object instances, and also be able to pass in parameters at the time of instantiating it.
So my code was:
var parentObj = {};
// base model contains client only stuff
parentObj.baseModel =
{
parameter1: null,
parameter2: null,
parameterN: null,
func1: function ()
{
return this.parameter2;
},
func2: function (inParams)
{
return this._variable2;
}
}
// create a troop type
parentObj.createModel = function (data)
{
var model = $.extend({}, parentObj.baseModel, data);
// allow it to be instantiated
parentObj[model._type] = function(parameter1, parameter2, parameterN)
{
return (function (model)
{
var that = $.extend(true, null, model);
that.parameter1 = parameter1;
that.parameter2 = parameter2;
that.parameterN = parameterN;
return that;
})(model);
}
}
And was called thus:
// models received from an AJAX call
var models = [
{ _type="Foo", _variable1: "FooVal", _variable2: "FooVal" },
{ _type="Bar", _variable1: "BarVal", _variable2: "BarVal" },
{ _type="FooBar", _variable1: "FooBarVal", _variable2: "FooBarVal" }
];
for(var i = 0; i < models.length; i++)
{
parentObj.createFunc(models[i]);
}
And then they can be used:
var test1 = new parentObj.Foo(1,2,3);
var test2 = new parentObj.Bar("a","b","c");
var test3 = new parentObj.FooBar("x","y","z");
// test1.parameter1 == 1
// test1._variable1 == "FooVal"
// test1.func1() == 2
// test2.parameter2 == "a"
// test2._variable2 == "BarVal"
// test2.func2() == "BarVal"
// etc
Here's easiest way to do this that I've found:
let bar = { baz: "qqqq" };
bar = Object.assign(() => console.log("do something"), bar)
This uses Object.assign to concisely make copies of all the the properties of bar onto a function.
Alternatively you could use some proxy magic.
JavaScript allows functions to be
treated as objects--you can add a
property to a function. How do you do
the reverse, and add a function to an
object?
You appear to be a bit confused. Functions, in JavaScript, are objects. And variables are variable. You wouldn't expect this to work:
var three = 3;
three = 4;
assert(three === 3);
...so why would you expect that assigning a function to your variable would somehow preserve its previous value? Perhaps some annotations will clarify things for you:
// assigns an anonymous function to the variable "foo"
var foo = function() { return 1; };
// assigns a string to the property "baz" on the object
// referenced by "foo" (which, in this case, happens to be a function)
foo.baz = "qqqq";
var bar = {
baz: "qqqq",
runFunc: function() {
return 1;
}
};
alert(bar.baz); // should produce qqqq
alert(bar.runFunc()); // should produce 1
I think you're looking for this.
can also be written like this:
function Bar() {
this.baz = "qqqq";
this.runFunc = function() {
return 1;
}
}
nBar = new Bar();
alert(nBar.baz); // should produce qqqq
alert(nBar.runFunc()); // should produce 1

cloning/copying a dojo data store

Hi can some one please tell me how to copy one data store to another in dojo. I tried it in following way but it doesn't work. Here I'm try to copy data from jsonStore to newGridStore.
jsonStore.fetch({query:{} , onComplete: onComplete});
var onComplete = function (items, request) {
newGridStore = null;
newGridStore = new dojo.data.ItemFileWriteStore({
data : {}
});
if (items && items.length > 0) {
var i;
for (i = 0; i < items.length; i++) {
var item = items[i];
var attributes = jsonStore.getAttributes(item);
if (attributes && attributes.length > 0) {
var j;
for (j = 0; j < attributes.length; j++) {
var newItem = {};
var values = jsonStore.getValues(item, attributes[j]);
if (values) {
if (values.length > 1) {
// Create a copy.
newItem[attributes[j]] = values.slice(0, values.length);
} else {
newItem[attributes[j]] = values[0];
}
}
}
newGridStore.newItem(newItem);
}
}
}
}
Based on the comments asked above. You are trying to copy values to a new Store for the single reason to be able to detect which values have changes and then save them individually, without having to send the entire store.
This approach is totally wrong.
Dojo has isDirty() and offers you the ability to revert() a store back to it's original values. It knows which values have changed and you don't need to do this.
Take a look at the bog standard IFWS here: http://docs.dojocampus.org/dojo/data/ItemFileWriteStore
Make sure you read everything from here: http://docs.dojocampus.org/dojo/data/ItemFileWriteStore#id8
What you want to do is create your own _saveCustom method which you will override your store with, and then when you save, you will be able to see which values have changed.
Click on the demo at the very bottom of the page. It shows you EXACTLY how do to it using _saveCustom

Firefox gBrowser.getBrowserForTab but no gBrowser.getTabForBrowser?

In the Javascript for a Firefox extension, you can call gBrowser.getBrowserForTab but there is no gBrowser.getTabForBrowser. So I wrote my own and it works, and I'm just curious if there's any reason I shouldn't be doing this, or if there's anything wrong with the code. The following is in my init method that gets called when the window loads.
gBrowser.getTabForBrowser = function(browser) {
for (var i=0; i<gBrowser.browsers.length; i++) {
if (gBrowser.getBrowserAtIndex(i) === browser) {
return gBrowser.tabContainer.getItemAtIndex(i);
}
}
return null;
}
(or should it be gBrowser.prototype.getTabForBrowser = ...?)
Far as I know there is no built in getTabForBrowser function, so you would have to roll your own. However, your code assumes that browser nodes are stored in the same DOM order as tab nodes. I can't say for sure if this assumption is ever broken, but considering tabs can be re-positioned by the user arbitrarily, it is not something I'd rely on.
Fortunately, each tab object has a linkedBrowser property. So you could rewrite your code like so:
gBrowser.getTabForBrowser = function(browser) {
var mTabs = gBrowser.mTabContainer.childNodes;
for (var i=0, i<mTabs.length; i++) {
if (mTabs[i].linkedBrowser == browser) {
return mTabs[i];
}
}
return null;
}

Resources