How do I add objects to the jquery result object? - javascript

I'm not sure if I'm doing something wrong here or not, but I need to base the status of several objects off of the status of another. So I'm basically iterating over the parents, then getting their children and iterating over them to get their children etc. However, I don't know how to add jquery objects on to a jquery result object.
My code looks something like this (where bubbleDown is said result object):
while (i < bubbleDown.length) {
var curRow = bubbleDown[i];
child = curRow.getAttribute('class') //etc
parent = curRow.getAttribute('class') //etc
bubbleDown.add($('.' + child + ':not(.' + parent.replace(' ', '.') + ')'));
i++;
}
I've hidden some logic due to it not being applicable to my question. Anyway, it doesn't work, and not because the jquery fails to return objects. Can anyone help?

The simplest approach is that .add() takes a selector already, just use that overload and keep updating the reference to the object to use the one .add() returns:
var newBubbleDown = $(bubbleDown);
while (i < bubbleDown.length) {
var curRow = bubbleDown[i];
child = curRow.getAttribute('class') //etc
parent = curRow.getAttribute('class') //etc
newBubbleDown = newBubbleDown.add('.' + child + ':not(.' + parent.replace(' ', '.') + ')');
i++;
}
//use newBubbleDown
I can't simplify it any further since I'm not sure of your logic outside, e.g. where i comes from, what child and parent are used for, etc. But just call .add(selector), without feeding it a jQuery object and you're all set.
We're using a new object here since .add() returns a reference to a new jQuery object (one you're not using), so each .add() adds elements, creates a jQuery object containing them, then it's thrown away on the next loop. Instead you need to update the reference so it keeps accumulating elements like you want. I tend to change the .add() implementation to act on the current array in most of my projects because it's more useful that way which would make your original code work, for example).
Note: this will only add elements to the newBubbleDown jQuery object, but your :not() use makes me wonder if this will suit your needs, it wouldn't remove any elements in any case, it'll only find the elements matching that selector and add them to the array. If you need to exclude elements already in the array, you'll need .filter().

you have to do this on a jquery-object item:
var curRow = $(bubbleDown[i]);
or you can use the each() method: (while index holds the current item index)
bubbleDown.each(function(index){
var child = $(this).attr('class')...
...
bubbleDown.add(...);
});
use var to init your variables to avoid problems with IE for example.

Related

JavaScript - anyway to remove all base methods and attributes of an Object but keep one?

I'm building some app which scans the DOM for elements and their children. Right now, I can get the data I need with the following line of code:
var bodyObj = document.getElementsByTagName("BODY")[0].children;
The problem, that the bodyObj object is spammed with unnecessary methods and attributes, I only want to keep the children method and clean the rest. Any way of achieving this?
PS
Pure Js.
Body object with only the children property
You might think to try something like:
var bodyObj = document.getElementsByTagName("BODY")[0];
for (var key in bodyObj){
if (bodyObj.hasOwnProperty(key) && key != 'children'){
delete bodyObj[key];
}
}
...like Florian Cabirol suggested in their answer. However, the <body> object's properties are non-enumerable, meaning they won't show up in the loop. To get the non-enumerable properties, you might try (in ES5+):
Object.getOwnPropertyNames(bodyObj);
But it still won't get them. Next, you might think, "I'll just make an array of all possible property/method names that are in HTMLBodyElements, and loop through them, removing them if they exist." However, if you do something like:
delete bodyObj.baseURI;
console.log(bodyObj.baseURI);
You'll notice that it didn't delete it. That's because you can't remove properties from a DOM object. Attempts to redefine them or to delete them will silently fail.
Body object's children's HTMLElements
To get document.getElementsByTagName("BODY")[0].children without any of its properties/methods, you could do:
var bodyObj = document.getElementsByTagName("BODY")[0].children;
bodyObj = Object.keys(bodyObj).map(function(key) { return bodyObj[key]; });
This would convert it from an HTMLCollection to an array, if you're okay with that. You could always wrap it in an object: bodyObj = {children: bodyObj};.
You can do something like this :
for (var key in bodyObj){
if (bodyObj.hasOwnProperty(key) && key != 'children'){
delete bodyObj[key];
}
}

jquery exclude array elements

I have an array of elements which I want to maintain efficiently, adding and removing arrays of elements over time.
var myElements = $('.initial');
jquery's merge() seems perfect for adding items as it doesn't create a new array and just adds to the existing one:
$.merge(myElements, $('.to-add'));
Is there an equivalent for removing, that also modifies the array in-place? Something like:
$.exclude(myElements, $('.to-remove'));
I do actually have arrays of DOM elements, and the selectors are just used as examples.
Assuming that you're after the relative complement of b in a.
And you don't want to create additional objects while process.
The function is for both plain Array and jQuery set.
(thus used $.each, $.inArray instead of Array.prototype.forEach Array.prototype.indexOf)
I wrote a function that fits your requirement.
$.exclude = function(a,b) {
var idx;
$.each(b, function(i, val) {
while((idx = $.inArray(val, a)) !== -1) {
a.splice(idx, 1);
}
})
return a;
}
test this code here
https://jsfiddle.net/happyhj/uwd4L1dm/8/
and you can use like this.
$.exclude(myElements, $('.to-remove'));
use jquery not method,
var filtered = $(myElements).not($('.to-remove'));
You can use delete to remove an element from an array and then use $.grep to remove the empty space.
//[0] since $("#id") will be a jquery Object
//[0] will return the particular DOM element
var orgArray = [$("#merge1")[0],$("#merge2")[0],$("#merge3")[0]];
var secArray = [$("#merge4")[0],$("#merge5")[0]]
// Will merge other two values;
// jQuery merge will merge contents of
//two array into first array
var merArray = $.merge(secArray, orgArray);
// Want to remove $("#merge2");
var getIndex = merArray.indexOf($("#merge2")[0]);
if(getIndex >-1){
delete merArray[getIndex]
}
// length before filtering
$("#l1").text(merArray.length);
//Now will remove Empty Space
merArray = $.grep(merArray,function(n){
return n==0 || n
});
$("#l2").text(merArray.length);
JSFIDDLE
You are using methods that are meant for array literals. jQuery already has method add() which will return a modified jQuery object that includes the elements matching the selector
var myElements = $('.initial');
var newCollection = myElements.add('.to-add');
newCollection.not('.to-remove').doSomething();
Don't think of jQuery objects as arrays although they are array like.
Reference add()
It's not clear what your overall objective really is but most likely you can manage it with any number of filtering methods that already exist within the api

Can the HTML data attribute hold a reference to a DOM element?

Is it possible with the HTML data- attributes to hold a reference to another DOM element? For example, I could do this with jQuery:
var domel1 = document.getElementById("#mydiv")
var domel2 = document.getElementById("#mydiv2")
$(domEl1).attr('data-domel', domel2)
Then later on, with jQuery I would do:
var domel1 = document.getElementById("#mydiv")
var domel2 = $(domel2).data('domel')
$(domel2).html("blahblahblah")
This might seem like a trivial example because I could just reference domel2 with the same id like I did at first, but there are cases where this could be useful for representing relationships between <div>s.
Yes and no. You cannot store a reference to a DOM element in a data- attribute. However, you can associated a reference to a DOM element to another element using jQuery .data(), which are already using:
$someElement.data('name', someOtherElement);
From the jQuery documentation:
The .data() method allows us to attach data of any type to DOM
elements in a way that is safe from circular references and therefore
from memory leaks.
Note that using .data() to set data will add it to the data store but not add it as a data- attribute in the DOM. However, using .data() to read data will check the data store as well as the data- attribute (if one exists and there's no data store value with the given key).
Not directly. data-* attributes are just attributes, so you can only store a string in them.
But, of course, you can store the id or class of your target element, in order to retrieve it later.
Or you could also store a reference to the element in a property, since properties can have any value.
Not legal, since attributes should be text strings. But since you're using jQuery you could use the .data() method instead.
jQuery .data() basically does everything for you.
But if you cannot use jQuery, or have to implement something more case-specific, you can hold indexes to a global object that holds the actual data.
This way you can support any type of data you need, references, objects, functions (even binded functions).
Here's a vanilla implementation of data, though I'm not sure what are your limitations - you will probably want to change some bits of the code below.
Note that elements in this code are identified by using their id.
// Set data with: elem.data('key',anything)
// Get data with: elem.data('key')
// Remove data (kind of) with: elem.data('key',undefined)
// This will generate random id on element if id is missing
Node.prototype.force_id = function() {
return this.id || (this.id = ('' + Math.random()).replace('0.', 'id-'));
}
// Our own data implementation
window.DATAOFDOM = {}; // I like naming globals as ALLCAPS
Node.prototype.data = function(k, v) {
if (arguments.length == 1) {
// getter
if (window.DATAOFDOM[this.id]) {
return window.DATAOFDOM[this.id][k]; // returns undefined when k isn't there
}
// else: implicitly returns undefined when there's no data for this element
} else {
// setter
this.force_id();
if (!window.DATAOFDOM[this.id])
window.DATAOFDOM[this.id] = {};
return window.DATAOFDOM[this.id][k] = v;
}
}
https://jsfiddle.net/oriadam/63zn9qtd/
This is not an answer because no element can store DOM element as an attribute value. Here is a small polyfill to do the same
If I have the same requirement I would follow this approach,
var DataDOM = (function(){
function DataDOM(){}
var elements = {}, counter = 0;
DataDOM.prototype.set = function(ele){
elements['ele' + counter] = ele;
counter += 1;
return ele + (counter - 1);
}
DataDOM.prototype.get = function(eleRef){
return elements[eleRef];
}
return DataDOM;
})();
Use like below
var dDOM = new DataDOM();
For example if if I want to set DOM reference to a element data attribute
var div = document.getElementById('someId');
var attr = dDOM.set(div);
Then set attr as data to some element
then while retrieving use below method to get it back
var referedElement = dDOM.get(someElement.attr('data'));
Because there is no direct way to store elements as Data AFAIK.

call a function with a specific index value of nodelist

Is it possible to call a function on specific index value of nodelist which is storing div like following :
var txtElem = txtdiv.getElementsByTagName("div");
the thing i want is that i am storing list of divisions in txtElem nodelist now i want to call a function on click event of the 3rd div stored in nodelist. The divisions are created dynamically and they don't have any id so they are not accessible by id.
from what you asked, it seems like this will do:
function toPseudoArray(nodeList) {
var ar = [];
for(var i in nodeList)
if(nodeList[i].nextSibling) // or for that case any other way to find if this is an element
ar.push(nodeList[i]);
return ar;
}
Pass your nodeList to this function, use what it returns as an array that contains your elements, and only your elements.
By the way, you could directly call function on a specific element simply using my_fab_function(txtElem[0]); -- of course, until you don't exceed the count.
The question is quite unclear. Seeing the jQuery tag, these come to my mind:
A way to call a jQuery function on a specified index using .eq():
var n = 1; //the index you need
$(txtElem).eq(n).css('color', 'red');
Simple Javascript to get the DOM element:
var n = 1; //the index you need
var elem = txtElem[n]; //elem will hold the DOM element
//call simple DOM methods on it:
var s = elem.innerHTML;
//you can also call jQuery functions on it:
$(elem).css('color', 'red');
By the way txtElem is not an object, it is a NodeList, an "array-like object".

Appending elements to DOM in a loop structure

Once the page has been loaded, I would like to append an additional element for each existing elements on the page.
I tried something like this:
var divs=document.getElementsByTagName('div');
for(i=0;i<divs.length;i++){
newDiv=document.createElement('div');
divs[i].appendChild(newDiv);
}
Just a warning this will actually freezes the browser because the divs variable is dynamic and divs.length just gets larger and larger each time the loop goes.
Is there a way to determine the number of tags when the DOM is normally loaded for the first time and have a chance to work with the elements statically.
I can't there of another solution so far.
Thanks so much.
Dennis!
The problem is that DOM collections are live, and when the underlying document structure is changed, it will be reflected automatically on the collection, that's why when the length property is accessed it will contain a new length, a common approach is to cache the length before starting the loop:
var divs=document.getElementsByTagName('div');
for(var i = 0, len = divs.length;i<len;i++){
var newDiv = document.createElement('div');
divs[i].appendChild(newDiv);
}
Also notice that you should declare all your variables with the var statement, otherwise it might become global.
Edit: In this case, since you are appending child nodes of the same tagName, the collection will be modified, and the indexes will no longer match, after the first iteration, the index 1 will refer to the newDiv object from the previous iteration, as #Casey recommends it will be safer to convert the collection to a plain array before traversing it.
I use the following function:
function toArray(obj) {
var array = [];
// iterate backwards ensuring that length is an UInt32
for (var i = obj.length >>> 0; i--;) {
array[i] = obj[i];
}
return array;
}
//...
var divs = toArray(document.getElementsByTagName('div'));
//...
Like you said, the divs variable is dynamic, so you have to convert it into an array (which is static) before you use it.
var nodeList = document.getElementsByTagName('div');
var divs = [];
for (var i = 0; i < nodeList.length; i++)
divs.push(nodeList[i]);
// loop again and append the other divs
Another (more elegant) way to do this is:
var divs = Array.prototype.slice.call(document.getElementsByTagName('div'));
But alas, this method does not work in IE.
Using jQuery, this is pretty straight forward. You can get a reference to all the existing divs or any other element on the page and then append a new element very easily without needing to create an explicit loop. Hope this help.
$('div').each(function(){
var newDiv = document.createElement('div');
$(this).append(newDiv);
});
document.getElementsByTagName() does NOT return a plain array, but an instance of HtmlCollection, which behaves like an array, but in fact presents some kind of view to all elements with the given element name in the document.
So, whenever you insert something into the DOM, the length property of divs will be updated too - of course.
So, besides other answers here, this behaviour should make sense now ;-)

Categories