JQuery collection array suddenly becomes DOM HTMLDivElement - javascript

In my code I have a function that accepts an array of JQuery collections and applies some code to each via a for-loop.
The problem is that as soon as I reference one it somehow becomes a HTMLDivElement instead of a collection object.
function vacant_now($timetables, now){
console.log("1:" + $timetables);//still fine
for (var i=0; i < $timetables.length; i++){
console.log("2:" + $timetables[i]);//problem is here
var $timetable = $timetables[i];
console.log("3:" + $timetable);
$timetable.find(".booking").each(function(){ ...
it's called like this:
vacant_now($page.find(".timetable"), now);
I'm stumped.

The jQuery collection array is an array of DOM elements.
Doing this: $('#myDiv')[0];
returns the same as: document.getElementByID('myDiv');
Solution:
Use $('.timetable').eq(index);
for (var i=0; i < $timetables.length; i++){
$timetables.eq(i);
}
Cheers

Using [i] on a jQuery object returns the dom element. If you want the jQuery object at a specific index use the .eq() function:
console.log("2:" + $timetables.eq(i));
Example - http://jsfiddle.net/8FeEf/1/

With jQuery you can use the .each method, and after you must 'jquerify' the object :
function vacant_now($timetables, now) {
$timetables.each(function() {
var $timetable = $(this);
});
}
A jquery collection is in fact an array of DOM elements.
You can also use a for, the syntaxe is a little bit more verbose.
In all case, you must jquerify the object.
Exemple : http://jsfiddle.net/FTcpD/

Related

How to use For loop in Javascript, like Each loop in jQuery

It's very easy to use Each loop in jQuery. But I want to use For loop in JS, without using jQuery.
I can use Each loop like this. And $(this) selector is very helpful to reach child elements.
$('.documents .document.selected').each(function(index){
console.log($(this).find('.document-name p').text())//this code gives me all selected element's name
})
I want to reach child elements by using For loop, but this code doesn't even works...
for (let i = 0; i < $('.documents .document.selected').length; i++) {
console.log($('.documents .document.selected')[i].find('.document-name p').text())//but this is not
}
I want to use For loop by using $(this) selector, but it's not working with For loop:
for (let i = 0; i < $('.documents .document.selected').length; i++) {
console.log($(this).find('.document-name p').text())
}
How to use For loop very easy as I show you or do you have any suggest?
Your code doesn't work because accessing an array-index property on an jQuery collection gives you the native DOM element in return, not a jQuery collection containing the element.
$('.documents .document.selected')[i].find
should be
$('.documents .document.selected').eq(i).find
But that's still using jQuery. If you want to avoid jQuery completely, you need to remove all references to $. Try:
const selectedDocs = document.querySelectorAll('.documents .document.selected');
for (let i = 0; i < selectedDocs.length; i++) {
console.log(selectedDocs[i].querySelector('.document-name p').textContent);
}
Or avoid manual iteration and invoke the iterator instead:
for (const selectedDoc of document.querySelectorAll('.documents .document.selected')) {
console.log(selectedDoc.querySelector('.document-name p').textContent);
}
querySelector is the vanilla DOM method equivalent of jQuery's .find.

Passing multiple values with GetElementById (& Split after certain a character)

I have little to no experience of JavaScript but I do know that the getElementID only carries one value so how can I have 2 values passed?
Can I use it twice like I have down below or would I be better to use another GetElementBy/GetElementsBy method to do it?
<script type="text/javascript">
$(document).ready(function () {
hash();
function hash() {
var hashParams = window.location.hash.substr(1).split('&');
for (var i = 0; i < hashParams.length; i++) {
var p = hashParams[i].split('=');
document.getElementById("<%=start.ClientID%>").value = decodeURIComponent(p[1]);
document.getElementById("<%=end.ClientID%>").value = decodeURIComponent(p[1]);;
}
}
});
</script>
EDIT
So I've decided to use the loop twice and its working but the values I'm passing contain text I need removed. Is there a way in which I can cut off the split after a certain character? Here is my new code
<script type="text/javascript">
$(document).ready(function () {
hash();
function hash() {
var hashParams = window.location.hash.substr(1).split('#');
for (var i = 0; i < hashParams.length; i++) {
var p = hashParams[i].split('=');
document.getElementById("<%=start.ClientID%>").value = decodeURIComponent(p[1]);
}
var hashParams = window.location.hash.substr(1).split('&');
for (var i = 0; i < hashParams.length; i++) {
var p = hashParams[i].split('=');
document.getElementById("<%=end.ClientID%>").value = decodeURIComponent(p[1]);;
}
}
});
</script>
And here is the text that appears in the search bar when forwarded from the previous page.
localhost:56363/Bookings.aspx#start=27/02/2018 12:30&end=27/02/2018 17:30
The start and end input boxes fill with the values but the start input box (27/02/2018 12:30&end) has characters I want cut off (&end).
Is there a way to stop a split after a certain character?
Using it twice as you have is perfectly acceptable. And, if they are separate things, then it makes sense.
While you could also use getElementsByTagName(), getElementsByName() or getElementsByClassName(), usually using document.querySelectorAll() is the more modern choice.
If they have something in common with them (like say a class), you could use it like this:
const nodeList = document.querySelectorAll('.classToGet');
Array.prototype.forEach.call(nodeList, element => element.value = decodeURIComponent(p[1]));
document.querySelectorAll() (as well as the getElementsBy functions) return a NodeList, which is kind of like an Array, but doesn't have an Array's functions, so you need to Array.prototype.forEach.call() to loop over them.
document.querySelectorAll() accepts a string like you would give to CSS, and the NodeList has all elements that match that.
And FYI, there is an equivalent document.querySelector() which gets a single element, so you could use it for IDs:
document.querySelector("#<%=start.ClientID%>")
Note the # like you would have for CSS at the beginning.
ID is a unique identifier, unlike class, so there should be only one of it with the same name in your DOM.
getElementById is intended to find the one element in the DOM with the specified ID.
If you need to get multiple elements, then yes make multiple calls to getElementById.
See here for the documentation on the getElementById method showing that it only accepts a single ID parameter: https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById

Using `push` on jQuery collection

jQuery collections are array-like Objects with a length property and methods from Array.prototype like splice, sort, and push. I'm aware that those methods are not chainable like typical jQuery methods, but the methods do work as expected.
Are there any reasons not to manipulate jQuery collections using those Array.prototype methods such as browser compatibility or issues with other jQuery methods?
Example
In a loop, add elements to a jQuery collection for later manipulation.
var $divs = $(); // Create empty jQuery collection.
for (i = 0; i < 8; i++) {
var div = document.createElement('div');
div.innerHTML = i+1;
$divs.push(div); // Add newly created div to collection
}
$divs.addClass('red').appendTo(document.body);
View on Codepen
Using push here instead of $divs = $divs.add(div); is more succinct and seems more efficient. If jsperf were working right now, I'm sure it would show better performance with push.
Here is .add from the jQuery source:
add: function( selector, context ) {
return this.pushStack(
jQuery.uniqueSort(
jQuery.merge( this.get(), jQuery( selector, context ) )
)
);
},
In this function 3 more methods are called... .merge, .uniqueSort (which is actually SizzleJS), and .pushStack...
In addition to just .push, jQuery will:
Merge your input if it's an array of selectors. (.merge)
Remove any duplicate entries. (.uniqueSort)
Accept flexible input with DOM elements (.pushStack)
So, to conclude that, if you're worried about input, .add is good to use, since jQuery makes it flexible (regular DOM or jQuery objects work), but it also removes duplicate entries and can take in multiple items with an array.
You can pass an Array into jQuery, this means you can make full use of a native Array
// build
var j, el, ly = [];
for (j = 0; j < 3; ++j) {
el = document.createElement('span');
ly.push(el);
}
// wrap
var jq = $(ly); // jQuery object [<span>, <span>, <span>]
Adding more elements can be done with pushStack and another Array
jq.pushStack([document.createElement('div')]);
jq; // [<span>, <span>, <span>, <div>]
Or more generally using add which accepts any input the normal jQuery constructor takes
jq.add(document.createElement('hr'));
jq; // [<span>, <span>, <span>, <div>, <hr>]

jQuery - text() method?

Being fairly new to jquery and javascript I can't seem to understand how the .text() method really works. I read through the jQuery documentation but still can't figure it out.
for (var i=0; i < arrayLength; i++){
var currentElement = $(".artist")[i];
var currentArtist = currentElement.text;
console.log(currentElement);
console.log(currentArtist);
}
currentArtist returns "undefined" in the console. It works fine on the $(".artist") alone, but not when I use the [i] or anything additional for that matter. What am I missing here? How else could I grab a text value inside a selector?
By using the [] operator on jQuery object you're accessing the raw element node that was found by jQuery. This raw element doesn't have the jQuery methods anymore, nor a text property.
If you want to get single element from jQuery object and keep the jQuery wrapper, use eq method.
var artistElement = $(".artist").eq(i);
artistElement.text(); // gets the text content of the element
The code you've posted is also not very optimized. For instance, with every loop iteration you're searching the document over and over again for elements with class artist. Better to cache that search result in a variable before performing the loop. And if the loop iterates over all .artist elements, you can use jQuery's each method.
$(".artist").each(function () {
var artist = $(this); // this poits to the raw element thus wrapping into jQuery object
console.log(artist.text());
});
var currentArtist = currentElement.text;
Should be:
var currentArtist = currentElement.text();
You should use a each():
$(".artist").each(function(i,val){
var currentArtist = $(val).text();
console.log(val);
console.log(currentArtist);
});
$(".artist") produce a jQuery object that could be like this:
[div, div, div, div, prevObject: jQuery.fn.jQuery.init, context: document, selector: ".artist"...]
So the result of $(".artist")[i] is a HTMLElement and do not have a text method, that's why you're getting undefined
Also text() is a function and may be followed with ()
But if you want to keep the for loop you can do
for (var i=0; i < arrayLength; i++){
var currentElement = $(".artist")[i];
var currentArtist = $(currentElement).text();
console.log(currentElement);
console.log(currentArtist);
}
.text() shows the text of an html element or set of html elements that would be visible to the user.

jQuery append() for multiple elements after for loop without flattening to HTML

I have a loop:
for (index = 0; index < total_groups; index += 1) {
groups[index].list_item = $(list_item_snippet);
// Closure to bind the index for event handling
(function (new_index) {
groups[index].list_item.find('.listing-group-title')
.html(groups[index].Group.Name)
.click(function(e){
fns.manageActiveGroup(new_index, groups);
return false;
});
})(index);
// Append to DOM
mkp.$group_listing.append(groups[index].list_item);
};
I would rather not call append() each time the loop fires.
I know that I could use a String and concatenate the markup with each loop iteration and append the string to mkp.$group_listing at the end, however this flattens the object and the bindings are lost (I am not relying on IDs).
Is there a way to perhaps add my objects to an array and append them all in one go at the bottom without flatening to HTML?
Assumptions:
$(list_item_snippet) contains some HTML defining a list item (and includes an element with class .listing-group-title).
groups is a block of JSON defining a 'group' in my script
The closure works perfectly
Edit:
Found that I can use the following syntax to append multiple elements:
mkp.$group_listing.append(groups[0].list_item, groups[1].list_item );
But i obviously need to automate it - it's not an array it's just optional additional function parameters so I'm not sure how to do this.
To append an array of elements to a selector you can use this:
$.fn.append.apply($sel, myArray);
In your case, since it's actually the .list_item property of each array element that you need you can use $.map to extract those first:
$.fn.append.apply(mkp.$group_listing, $.map(groups, function(value) {
return value.list_item;
}));
Instead of bind it the way you've done, if you bind it using on() like below,
$(document).on('click', '.listing-group-title', function() {
// click handler code here
});
You can flatten the HTML and append it in one statement and it'll still work.
Note: For better efficiency, replace document in the above statement to a selector matching the closest parent of .listing-group-title
Yes. Use the jQuery add method to add all your items to a jQuery object. Then append that one object.
http://api.jquery.com/add/
EDIT: Example:
var arr = $();
for (index = 0; index < total_groups; index += 1) {
groups[index].list_item = $(list_item_snippet);
// Closure to bind the index for event handling
(function (new_index) {
...
})(index);
// Add to jQuery object.
arr.add(groups[index].list_item));
};
mkp.$group_listing.append(arr);

Categories

Resources