still quite new to jQuery / Javascript and am struggling a little.
I have created an attribute called catno that I have assigned to several links. Then I want all instances inside banner-1 to be called into another link separated by a comma. i.e
New Link
I'm just struggling in making this happen. The code I have right now only returns the first catno.
Any help would be great!
JSFIDDLE
<div id="banner-1>
New Link
</div>
$sd('.buy-outfit').attr('href', function() {
return $sd("#banner-1 .add-catno").attr('catno') + ',';
});
Fiddle Demo
var arr_catno = $("#banner-1 a").map(function () { //create array using .map
return $(this).attr('catno'); // add element attribute catno to array
}).get().join(); //use .join() to get comma separated string of array
$('.new-link').attr('href', arr_catno); //assign it to href
.map()
Updated after OP's Comment
Fiddle Demo
$('[id^=banner-] .new-link').attr('href', function () {
return $(this).parent().find('a').map(function () {
return $(this).attr('catno');
}).get().join();
});
Attribute Starts With Selector [name^="value"]
.parent()
.find()
[id^=banner-] --> select all the elements with id starting with banner-
Use $.map to produce an array of catnos and them join them into a string.
$('.new-link').attr('href', function() {
return $.map($(".add-catno"), function (el) {
return $(el).data('catno');
}).join(',');
});
Note in the example that I've changed the catnos attributes to data attributes. Using data is considered good practice for when you want to add arbitrary data to your HTML.
Fiddle
Related
I have a series of blog posts that have an array of tags as a data-tags html attribute such as ["foo", "bar", "baz"]. I'm looking to query the DOM elements that have foo included in the data-tags array.
Example markup:
<li data-tags="['foo', 'bar', 'baz']">...</li>
I know it's possible to query if the data attributes were stored as a singular value, ie:
document.querySelectorAll('[data-specifc-tag="foo"]');
Is it even possible to select elements which arrays include a specific value?
(Vanilla JS only, no jQuery please)
To sum up, the query you need is
document.querySelectorAll('li[data-tags*=\'"foo"\']')
But you have to make sure that each element in your html array is enlosed within double quotes. You may change it to a single quote, but make sure to update the query.
You can search for multiple queries by adding more rules as follows
document.querySelectorAll('li[data-tags*=\'"foo"\']'+'[data-tags*=\'"bar"\']')
Below is a snippet that applies those queries based on some value you may be interested in. I made sure to use the same html structure you put in your question.
Edit:
I added one more function, queryAll, that allows for value queries. So, you can search for elements that must have more than one value.
function entry(search){
var string = '"' + search + '"';
return '[data-tags*=\'' + string + '\']';
}
function queryAll() {
var queries = Array.prototype.slice.call(arguments).map(function(a){
return '[data-tags*=\'' + a + '\']';
});
//example: document.querySelectorAll('li[data-tags*=\'"foo"\']');
return document.querySelectorAll('li'+ queries.join(''));
}
function query(search) {
var string = '"' + search + '"';
//example: document.querySelectorAll('li[data-tags*=\'"foo"\']');
return document.querySelectorAll('li[data-tags*=\'' + string + '\']');
}
query("foo1"); // One li
query("foo"); //Two li's
queryAll("foo1", "bar1"); //one li
queryAll("foo1", "bar1Not"); //nothing
<ul>
<li data-tags='["foo", "bar", "baz"]'>...</li>
<li data-tags='["foo1", "bar1", "baz1"]'>...</li>
<li data-tags='["foo2", "bar2", "baz2"]'>...</li>
<li data-tags='["foo", "bar2", "baz2"]'>...</li>
</ul>
Here's a vanilla js solution to your problem:
const posts = document.querySelectorAll('[data-tags]');
const fooPosts = [];
posts.forEach(post => {
if (/foo/.test(post.getAttribute('data-tags'))) {
fooPosts.push(post);
}
});
// output the filtered posts
console.log(fooPosts);
One liner alternative:
document.querySelectorAll('[data-tags]').forEach(post => /foo/.test(post.getAttribute('data-tags')) && console.log(post));
Or splitted:
document.querySelectorAll('[data-tags]').forEach(post =>
/foo/.test(post.getAttribute('data-tags')) && console.log(post));
Please, note that, as mentioned in the comments, your html markup is invalid. Data attributes should not contain complex data structure like arrays or objects. As suggested, consider performing a .join() on your array data and outputting it in the attribute [data-tags] as a string. In it each value can be separated by comma for readability.
Using the valid markup your solution will be slightly different:
const posts = document.querySelectorAll('[data-tags]');
const fooPosts = [];
posts.forEach(post => {
if (post.getAttribute('data-tags').indexOf('foo') > -1) {
fooPosts.push(post);
}
});
console.log(fooPosts);
The above code is also faster as it's using .indexOf() to filter the DOM nodes.
Here's the above code in a reusable function:
const filterNodesByAttr = (attr, tag) => {
let filtered = [];
const items = document.querySelectorAll(`[${attr}]`);
items.forEach(item => {
if (item.getAttribute(attr).indexOf(tag) > -1) {
filtered.push(item);
}
});
return filtered;
};
const fooPosts = filterNodesByAttr('data-tags', 'foo');
console.log(fooPosts);
You can use CSS to get any element on the page.
I.e.
Get elements with href's [href]
Get divs with data-type of price div[data-type=price]
var attributes = ['data-rel','type'];
attributes.forEach((at)=>{
document.querySelectorAll('['+at+']').forEach((element)=>{
element.append('<b>'+at+'</b>');
});
});
<p>Hello</p>
<p data-rel="title">World!</p>
<p type="emoji">:-)</p>
Trying to do $('div').attr('data', 'ip') through a .each() just returns undefined.
Let's say I have 4 divs
<div class="box" data-ip="ipvalue">content</div>
<div class="box" data-ip="ipvalue">content</div>
<div class="box" data-ip="ipvalue">content</div>
<div class="box" data-ip="ipvalue">content</div>
and I need to iterate each one and get the data-ip value.
My code, it just returns undefined (server, serverObj, are variables).
var serversObj = $('.server');
serversObj.each(function(index, el) {
return $(this).data('ip');
});
What am I doing wrong?
You want to use map if you want to get all of the values of all of the elements
var ips = $(".box") //get all of the elements
.map(
function(){
return $(this).data("ip"); //get the value from the attribute
}
).get(); //returns an Array
Fiddle
Here is what the code should be for the markup sample you gave.
var serversObj = $('.box'),
Ipvals = [];
serversObj.each(function(index, el) {
Ipvals.push( $(this).data('ip'));
});
console.log( Ipvals);
You can also use $.map() to return the same array.
$('.box').each(function(){
console.log($(this).attr('data-ip'));
});
First off, this is totally wrong:
$('div').attr('data', 'ip')
This is setting an attribute called data to a value of "ip", it does nothing involving data attributes. Effectively, you're doing this: <div data='ip'>.
If you want to access a data attribute called data-ip, you want
$('div').data('ip')
If you want to access it for a series of elements, you can't just return it out of a .each loop. That value is discarded. You need to map your input set of elements to a set of outputs:
$('.server').map(function () { return $(this).data('ip') })
This will produce an array that has each element matched by .server's data-ip attribute.
I have this
var matches = bookmarks.filter(function(x) {
return _.contains(x.get("tags"), 'apple');
});
Which will return the bookmark objects that have the apple tags
I want to put an array there instead to pull and all the bookmarks that have the matching values, similar to this
var matches = bookmarks.filter(function(x) {
return _.contains(x.get("tags"), ['apple','orange']);
});
This doesn't work, any way to get it to work?
EDIT: Im sorry, bookmarks is a collection and im trying to return the models that have the apple and orange tags
If tags is a string, your code it would be
return _.indexOf(x.get("tags"), ['apple','orange']) > -1;
Example with indexOf : jsFiddle
If tags is an array, you can use intersection
return _.intersection(['apple','orange'], x.get("tags")).length > 0;
Example with intersection: jsFiddle
There doesn't seem to be a function for that in underscore. However, you can easily combine other functions to accomplish this:
_.mixin({
containsAny: function(arr, values) {
// at least one (.some) of the values should be in the array (.contains)
return _.some(values, function(value) {
return _.contains(arr, value);
});
}
});
I have following object:
$("input:checkbox:checked")
[
<input class="li_checker" type="checkbox" category_uid="1.3" category_language="da">,
<input class="li_checker" type="checkbox" category_uid="1.3.1" category_language="da">
]
If there is any helper in jQuery which allows me to get value of "category_uid" for all elements and returns it as the another array? Expected result:
["1.3", "1.3.1"]
Use map():
var myArray = $("input:checkbox:checked").map(function(){
return this.getAttribute("category_uid");
}).get();
As bpierre suggested, use .map(). His answer is correct.
If you need this behavior for different attributes, you might as well write is as a reusable function (“jQuery plugin”):
jQuery.fn.pluck = function(attr) {
return this.map(function() {
return this.getAttribute(attr);
}).get();
};
$('input:checkbox:checked').pluck('category_uid'); // ["1.3", "1.3.1"]
P.S. category_uid is not a valid attribute in HTML. Consider using custom data-* attributes instead, e.g. data-category-uid="foo".
Just for the fun of it, a third way, this one using attr:
var categories = [];
$("input:checkbox:checked").attr("category_uid", function(index, value) {
categories.push(value);
});
Live example
Off-topic: If you want to have arbitrary, custom attributes on HTML elements, recommend using the data- prefix defined by HTML5 (details). You can use it now, even if you're not using the HTML5 doctype (this is one of the places where HTML5 is just codifying — and reining in — current practice), and it future-proofs a bit.
var myarray=[];
$("input:checkbox:checked").each(function () {
myarray.push($(this).attr('category_uid'));
});
Live Demo
Something like
var checked_cats = new Array();
$("input:checkbox:checked").each(function() {
checked_cats.push($(this).attr('category_uid'));
});
(not tested)
p.s. saw your tweet.
Here's a more "pluginy" way to do this:
(function($){
$.fn.getAttributes = function(attribute){
var result = [];
$(this).each(function(){
var a = $(this).attr(attribute);
if(a)
result.push(a);
});
return result;
}
})(jQuery);
and then use it as follows :
var result = $("input:checkbox:checked").getAttributes("category_uid");
Haven't tested it, but it should work just fine.
Given an arbitrary HTML element with zero or more data-* attributes, how can one retrieve a list of key-value pairs for the data.
E.g. given this:
<div id='prod' data-id='10' data-cat='toy' data-cid='42'>blah</div>
I would like to be able to programmatically retrieve this:
{ "id":10, "cat":"toy", "cid":42 }
Using jQuery (v1.4.3), accessing the individual bits of data using $.data() is simple if the keys are known in advance, but it is not obvious how one can do so with arbitrary sets of data.
I'm looking for a 'simple' jQuery solution if one exists, but would not mind a lower level approach otherwise. I had a go at trying to to parse $('#prod').attributes but my lack of javascript-fu is letting me down.
update
customdata does what I need. However, including a jQuery plugin just for a fraction of its functionality seemed like an overkill.
Eyeballing the source helped me fix my own code (and improved my javascript-fu).
Here's the solution I came up with:
function getDataAttributes(node) {
var d = {},
re_dataAttr = /^data\-(.+)$/;
$.each(node.get(0).attributes, function(index, attr) {
if (re_dataAttr.test(attr.nodeName)) {
var key = attr.nodeName.match(re_dataAttr)[1];
d[key] = attr.nodeValue;
}
});
return d;
}
update 2
As demonstrated in the accepted answer, the solution is trivial with jQuery (>=1.4.4). $('#prod').data() would return the required data dict.
Actually, if you're working with jQuery, as of version 1.4.3 1.4.4 (because of the bug as mentioned in the comments below), data-* attributes are supported through .data():
As of jQuery 1.4.3 HTML 5 data-
attributes will be automatically
pulled in to jQuery's data object.
Note that strings are left intact
while JavaScript values are converted
to their associated value (this
includes booleans, numbers, objects,
arrays, and null). The data-
attributes are pulled in the first
time the data property is accessed and
then are no longer accessed or mutated
(all data values are then stored
internally in jQuery).
The jQuery.fn.data function will return all of the data- attribute inside an object as key-value pairs, with the key being the part of the attribute name after data- and the value being the value of that attribute after being converted following the rules stated above.
I've also created a simple demo if that doesn't convince you: http://jsfiddle.net/yijiang/WVfSg/
A pure JavaScript solution ought to be offered as well, as the solution is not difficult:
var a = [].filter.call(el.attributes, function(at) { return /^data-/.test(at.name); });
This gives an array of attribute objects, which have name and value properties:
if (a.length) {
var firstAttributeName = a[0].name;
var firstAttributeValue = a[0].value;
}
Edit: To take it a step further, you can get a dictionary by iterating the attributes and populating a data object:
var data = {};
[].forEach.call(el.attributes, function(attr) {
if (/^data-/.test(attr.name)) {
var camelCaseName = attr.name.substr(5).replace(/-(.)/g, function ($0, $1) {
return $1.toUpperCase();
});
data[camelCaseName] = attr.value;
}
});
You could then access the value of, for example, data-my-value="2" as data.myValue;
jsfiddle.net/3KFYf/33
Edit: If you wanted to set data attributes on your element programmatically from an object, you could:
Object.keys(data).forEach(function(key) {
var attrName = "data-" + key.replace(/[A-Z]/g, function($0) {
return "-" + $0.toLowerCase();
});
el.setAttribute(attrName, data[key]);
});
jsfiddle.net/3KFYf/34
EDIT: If you are using babel or TypeScript, or coding only for es6 browsers, this is a nice place to use es6 arrow functions, and shorten the code a bit:
var a = [].filter.call(el.attributes, at => /^data-/.test(at.name));
Have a look here:
If the browser also supports the HTML5 JavaScript API, you should be able to get the data with:
var attributes = element.dataset
or
var cat = element.dataset.cat
Oh, but I also read:
Unfortunately, the new dataset property has not yet been implemented in any browser, so in the meantime it’s best to use getAttribute and setAttribute as demonstrated earlier.
It is from May 2010.
If you use jQuery anyway, you might want to have a look at the customdata plugin. I have no experience with it though.
As mentioned above modern browsers have the The HTMLElement.dataset API.
That API gives you a DOMStringMap, and you can retrieve the list of data-* attributes simply doing:
var dataset = el.dataset; // as you asked in the question
you can also retrieve a array with the data- property's key names like
var data = Object.keys(el.dataset);
or map its values by
Object.keys(el.dataset).map(function(key){ return el.dataset[key];});
// or the ES6 way: Object.keys(el.dataset).map(key=>{ return el.dataset[key];});
and like this you can iterate those and use them without the need of filtering between all attributes of the element like we needed to do before.
You should be get the data through the dataset attributes
var data = element.dataset;
dataset is useful tool for get data-attribute
or convert gilly3's excellent answer to a jQuery method:
$.fn.info = function () {
var data = {};
[].forEach.call(this.get(0).attributes, function (attr) {
if (/^data-/.test(attr.name)) {
var camelCaseName = attr.name.substr(5).replace(/-(.)/g, function ($0, $1) {
return $1.toUpperCase();
});
data[camelCaseName] = attr.value;
}
});
return data;
}
Using: $('.foo').info();
You can just iterate over the data attributes like any other object to get keys and values, here's how to do it with $.each:
$.each($('#myEl').data(), function(key, value) {
console.log(key);
console.log(value);
});
I use nested each - for me this is the easiest solution (Easy to control/change "what you do with the values - in my example output data-attributes as ul-list) (Jquery Code)
var model = $(".model");
var ul = $("<ul>").appendTo("body");
$(model).each(function(index, item) {
ul.append($(document.createElement("li")).text($(this).text()));
$.each($(this).data(), function(key, value) {
ul.append($(document.createElement("strong")).text(key + ": " + value));
ul.append($(document.createElement("br")));
}); //inner each
ul.append($(document.createElement("hr")));
}); // outer each
/*print html*/
var htmlString = $("ul").html();
$("code").text(htmlString);
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/prism.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.17.1/themes/prism-okaidia.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1 id="demo"></h1>
<ul>
<li class="model" data-price="45$" data-location="Italy" data-id="1234">Model 1</li>
<li class="model" data-price="75$" data-location="Israel" data-id="4321">Model 2</li>
<li class="model" data-price="99$" data-location="France" data-id="1212">Model 3</li>
</ul>
<pre>
<code class="language-html">
</code>
</pre>
<h2>Generate list by code</h2>
<br>
Codepen: https://codepen.io/ezra_siton/pen/GRgRwNw?editors=1111
One way of finding all data attributes is using element.attributes. Using .attributes, you can loop through all of the element attributes, filtering out the items which include the string "data-".
let element = document.getElementById("element");
function getDataAttributes(element){
let elementAttributes = {},
i = 0;
while(i < element.attributes.length){
if(element.attributes[i].name.includes("data-")){
elementAttributes[element.attributes[i].name] = element.attributes[i].value
}
i++;
}
return elementAttributes;
}
If you know the name of keys you can also use object destructuring to get values like this
const {id, cat, cid } = document.getElementById('prod').dataset;
You can also skip keys you don't need and get the ones you need like this
const { cid, id } = document.getElementById('prod').dataset;
100% Javascript no jQuery ;)
DOMStringMap :
console.log(document.getElementById('target-element-id').dataset);
or custom variable :
var data = {};
Object.entries(document.getElementById('target-element-id').dataset).forEach(([key, val]) => {
data[key] = val;
});
console.log(data);