getAttribute by TagName - JS - javascript

My specific situation is that I'm trying to remove/make inactive a link element from the DOM (I have no control over it being generated). The way that I plan to do this is through replacing the 'href' attribute with a nonsense value - the reason I've chosen to do it this way rather than simply using disable = true is so that the function can be reused on other occasions to change other attributes.
The problem I'm having is with .getAttribute where it returns the error "TypeError: elemArr.hasAttribute is not a function".
function removeLink(elem, att, value, replacement) {
var elemArr = document.getElementsByTagName(elem);
for (var i = 0; i < elemArr.length; i++) {
var workingAtt = elemArr.hasAttribute(att);
if (workingAtt.value === filePath) {
elemArr[i].setAttribute(att, replacement);
}
}
}
removeLink("link", "href", "filePath", "#");
Any help with why this error is getting thrown is greatly appreciated.

What's going on in there is that elemArr is an array, and arrays don't have a hasAttribute method. Rewrite your code as
function removeLink(elem, att, value, replacement) {
var elemArr = document.getElementsByTagName(elem);
for (var i = 0; i < elemArr.length; i++) {
//this line here wasn't referring to a specific node but the array
var workingAtt = elemArr[i].hasAttribute(att);
if (workingAtt && elemArr[i].getAttribute(att) === value) {
elemArr[i].setAttribute(att, replacement);
}
}
}
removeLink("link", "href", "filePath", "#");
And it will work.
A more succint approach would be something like this:
function removeLink(elem, att, value, replacement){
var selector = elem + '['+ att +'="'+ value +'"]';
[].forEach.call(document.querySelectorAll(selector), function(node){
node.setAttribute(att, replacement);
});
}
It does basically the same thing, but is quite a bit shorter and more explicit.

.hasAttribute() returns a boolean true or false. Therefore, workingAtt will either equal true or false. Boolean values are not HTMLElements, therefore they do not have value attributes. That's why there's an error.
It looks like you're trying to do something like select elements where there is a href attribute.
If so, you can just filter them:
var myElements = [];
[].filter.call(elemArr, function(el) {
if(el.hasAttribute(att)) {
myElements.push(el);
}
});
// then, do something with myElements

You have several errors in your code:
elemArr.hasAttribute instead of elemArr[i].hasAttribute.
var workingAtt = elemArr.hasAttribute(att); — here, workingAtt will be a boolean value, workingAtt.value is non-existent. You should use elemArr[i].getAttribute(att) and later use workingAtt, NOT workingAtt.value (it will be non-existent again!).
if (workingAtt.value === filePath) you're comparing to filePath while you should most definitely compare to value that you pass in the function.

Related

In a JS for loop over elements, how do you act on those elements in the loop?

Original code:
item_boxes = $(".item-box")
$.each(item_boxes, function() {
var id = $(this).data("id")
$(this).find(".price").text(price_list[id])
})
JS code:
item_boxes = $(".item-box")
for(var i=0; i<item_boxes.length; i++) {
var id = item_boxes[i].getAttribute("data-id")
item_boxes[i].find... .text
// above line doesn't work, because it's jQuery
// item_boxes[i].querySelector(".price"), finds the child element, but then I can't figure out how to add the price info
// item_boxes[i].querySelector(".price").innerHTML(price_list[id]) throws a nomethod error on innerHTML
}
ooops sorry tanks for the responses, but I guess the quesiton wasn't clear, I'm moving TO the latter code (JS). I'd like the latter code to duplicate the same functionailty as former, but currently it does not. item_boxes[i].find throws a no method error on .find, so then I did querySelector, which finds the object, but there's no .text method to change the text.
Basically what the code is doing is looking at all the item_boxes, and on each of them, changing the text of the child price element.
Use the eq(id) method to fetch a jQuery object.
Proceed with data() as well.
var item_boxes = $(".item-box"),
i = 0,
el, id;
for(; i < item_boxes.length; i++) {
el = item_boxes.eq(i);
id = el.data("id");
el.find(".price").text(price_list[id]);
}
Notice with the for loop, all variables are extracted to it's function scope, which is more imaginary in this example. Use $.each(items, function(i, item){/*vars*/}) if you want to keep variables together.
Without jQuery:
var item_boxes = document.querySelectorAll(".item-box"),
i = 0,
el, id;
for(; i < item_boxes.length; i++) {
el = item_boxes[i];
id = el.dataset.id;
//el.querySelectorAll
el.querySelector(".price").innerHTML = price_list[id];
}
Following JQuery docs sample :
$.each([ 52, 97 ], function( index, value ) {
alert( index + ": " + value );
});
You can do much better without jQuery by using modern features, and a transpiler if needed.
for (const box of document.querySelectorAll(".item-box")) {
box.querySelector(".price").textContent = price_list[box.dataset.id];
}
The longest part is the function names, so you can shorten them.
function query(root, sel) {
return root.querySelector(sel);
}
function queryAll(root, sel) {
return root.querySelectorAll(sel)
}
So now it looks like this:
for (const box of queryAll(document, ".item-box")) {
query(box, ".price").textContent = price_list[box.dataset.id];
}
Make the function names as short as you like.
$.each takes two arguments. The first one is what it calls the index, the second one is what it calls the element.
As for "acting" on the data, it depends on whether you're talking about manipulating what you're iterating over or whether you're just manipulating the values that the function is returning.
To change what you're iterating over, use the index to select each respective item in what you're iterating over:
var list = [27, 43, 19];
$.each(list, function(i, element) {
list[i] += 7
});
To change the values being returned, just do whatever. They're variables.
var list = [27, 43, 19];
$.each(list, function(i, element) {
i += 3
element += 91
});
You could use ES6 Map method
It'd be something like
item_boxes.map( (item_box,index) => {
var id = item_box.getAttribute("data-id")
item_box.find... .text //if you actually need to do something with the index
})

My jQuery nested function can't return a value

I have a jQuery plugin with a nested function (stored on the data object so that I can call it later on). My problem is that when I call this function it seems that it can't return a simple value, it either returns undefined or the html object the plugin is attached to...
var findTheObject = function(self,needle) {
var haystack = self.data('galleryData');
console.log('haystack is:');
console.log(haystack);
console.log('needle is:');
console.log(needle);
for (i = 0; i < haystack.length; ++i) {
console.log('looking for needle...');
if (haystack[i]['fileId'] == needle) {
console.log('found needle:');
console.log(haystack[i]);
return haystack[i];
}
}
return 'found no match';
};
this.data('findTheObject',findTheObject); //make callable later on through the data array.
I call it like this:
if (self.data("activeFolder") != null) {
var needle = self.data("activeFolder");
var haystack = self.data('galleryData');
current = null;
//find the object corresponding to data("activeFolder")
current = self.data(findTheObject(self,needle));
console.log('returned object is:');
console.log(current);
quote (Satpal):
Use it like self.data('findTheObject')(self,needle)
In statement self.data(findTheObject(self,needle)), first you are calling global function findTheObject then whatever it returns suppose a string abc then it is looking for self.data('abc')
close quote.
I like to check my questions off when they are answered. Cred for this answer goes to Satpal but since he has chosen not to post it as an answer I will summarize his answer (Satpal if you post an answer I will check that instead and remove my own to give you the cred).

Make use of a global array

I want to use the values that I get from a request, but the response object is a local variable (an array). Therefore I create this global array:
<script type="text/javascript">
var response = [];
as you see, right under the script opening tag, so it is global. Then in the function where I have the response I added this:
jsonResponse.forEach(function(element){
response[element.size] = element.id;
});
And then added this, with the purpose to make use of the values that I've got in my global var from the response object:
getIdOfProductBySize: function() {
var selectedIndex = document.getElementById('dropdown_options').value;
for (var key in response) {
if (key != selectedIndex) {
continue;
} else {
return response[key];
}
}
}
Doesn't work, so I started going step by step (of the order I add the new things) and I noticed that the script breaks after the 2nd thing that I add (where the forEach is).
Maybe I am not declaring the global variable correctly, or maybe I cannot access it this way, or maybe I don't assign the values to it in the correct way, I don't know, so I am asking if someone can give me a hint how to make use of all this working together?
Try this:
var response = {key1: value1};
var i = 2;
jsonResponse.forEach(function(entry) {
console.log(entry);
response["key"+i] = entry.id;
i++;
});
var index;
for (index = 0; index < response.length; ++index)
{
console.log(response[index]);
if(response["key"+index] !== selectedIndex)
continue;
else
return response["key"+index];
}
Looks like you're going to need a two dimensional array.
Looks to me like your "key" value is undefined.
before:
for (var key in response) {
try:
var k=response.whatever;
If that makes sense?
response[element.id] = element.size;
Try this one, i believe element.size returns the actual size of an element and is not what you want to use as index in an array.

How do you write to a span using jQuery?

I'm trying to populate a <span></span> element on the page load with jQuery.
At the moment the value that gets populated into the span is just an integer count.
Here I have named my span userCount:
Users<span id = "userCount"></span>
I am trying to write the value of the span with no success.
$(document).ready(function () {
$.post("Dashboard/UsersGet", {}, function (dataset) {
var obj = jQuery.parseJSON(dataSet);
var table = obj.Table;
var countUsers;
for (var i = 0, len = table.length; i < len; i++) {
var array = table[i];
if (array.Active == 1) {
var name = array.Name;
}
countUsers = i;
}
userCount.innerHTML = countUsers.toString();
});
});
You don't have any usercount variable. Use $(selector) to build a jquery object on which you can call functions like html.
$('#userCount').html(countUsers);
Note also that
you don't need to convert your integer to a string manually.
if you don't break from the loop, countUsers will always be table.length-1.
you have a typo : dataSet instead of dataset. Javascript is case sensitive.
you don't need to parse the result of the request
you don't need to pass empty data : jQuery.post checks the type of the provided parameters
So, this is probably more what you need, supposing you do other things in the loop :
$.post("Dashboard/UsersGet", function (dataset) {
var table = dataset.Table;
var countUsers = table.length; // -1 ?
// for now, the following loop is useless
for (var i=0, i<table.length; i++) { // really no need to optimize away the table.length
var array = table[i];
if (array.Active == 1) { // I hope array isn't an array...
var name = array.Name; // why ? This serves to nothing
}
}
$('#userCount').html(countUsers);
});
Use .html()!
Users<span id = "userCount"></span>
Since you have assigned an id to the span, you can easily populate the span with the help of id and the function .html().
$("#userCount").html(5000);
Or in your case:
$("#userCount").html(countUsers.toString());
Change:
userCount.innerHTML = countUsers.toString();
to:
$("#userCount").html(countUsers.toString());
Instead of:
userCount.innerHTML = countUsers.toString();
use:
$('#userCount').html(countUsers.toString());
You could use
$('#userCount').text(countUsers);
to write data to span
The call back argument should be dataSet rather than dataset?

putting source of all images into an array

What is the cleanest way to put the source attribute string of all images within a div into an array?
I was hoping this would work -
var imageSourceArray = $("#leDiv img").attr('src');
alert(imageSourceArray[3]); //not alerting the source, boo hoo.
Do I need to loop through $("#leDiv img") and add each src string to an array individually? Or is there a more elegant way to do this?
You can use jQuery's map function which is described as:
Pass each element in the current matched set through a function, producing a new jQuery object containing the return values.
For your example:
var mySources = $('#leDiv img').map(function() {
return $(this).attr('src');
}).get();
Edit: Far more elegant solution, there's obviously still some looping involved internally:
var img_sources = $('#leDiv img').map(function(){ return $(this).attr('src') });
You will in fact need to loop over the collection and add sources individually.
var img_sources = [];
$('#leDiv img').each(function(i,e){
img_sources.push($(e).attr('src'))
})
Some background: jQuery.fn.attr() maps to jQuery.access() internally, the key part of which looks like this:
function( elems, key, value, exec, fn, pass ) {
var length = elems.length;
// setter functions omitted here …
// Getting an attribute
return length ? fn( elems[0], key ) : undefined;
}
Note the elems[0] part – only the first item in the collection is fed to the subsequent callback function (jQuery.attr() in fact) responsible for extracting the information.
var imageSourceArray = [];
$('#leDiv img').each(function(){
var src = $(this).attr("src");
imageSourceArray.push(src);
});
alert(imageSourceArray[3]);
you already have the src in a collection when you fetch the the images. It may be more efficient to not store the src attributes in another array:
$('#leDiv img').each(function(i,e){
var dosomethingwith = $(e).attr('src');
})
or you could do:
var ImageCol = $('#leDiv img');
alert(ImageCol[3].attr('src'));

Categories