jQuery grabbing value of a dynamic data-* attribute - javascript

Inside of a jQuery click event listener I have some code like this:
function (evt) {
evt.preventDefault();
var reportbox = $(this).closest('.activityItem');
var id = reportbox.data('questionId');
...
}
This works fine for data-question-id attributes, but I'm generalizing the function now and I need to grab the value from any of the following:
data-question-id
data-answer-id
data-comment-id
data-user-id
data-company-id
What's the best way to do this?

If you know only one of those attributes will be present and you want to retrieve that single value you can use this:
var element = $(el);
var dataAttrs = ['question-id', 'answer-id', 'comment-id', 'user-id', 'company-id'];
var data = getUnknownAttr(element, dataAttrs);
function getUnknownAttr(element, potentialAttrs) {
var $element = element instanceof $ ? element : $(element);
var result = null;
potentialAttrs.forEach(function (attr) {
var temp = $element.data(attr);
if (typeof temp !== 'undefined') {
result = temp;
}
});
return result;
}

As Brian points out, you could access the dataset property to retrieve all the element's data-* attributes:
$('.activityItem').each(function () {
var dataAttributes = this.dataset;
Object.keys(dataAttributes).forEach(function (key) {
console.log(key, dataAttributes[key]); // key, value
});
});
Since you only want the first data attribute, you could simply use:
var dataAttributes = this.dataset;
var id = dataAttributes[Object.keys(dataAttributes)[0]];
It's important to note that the dataset property returns the attribute names in camel case without the data prefix. In other words, data-question-id would be questionId.
If you want to retrieve all of an element's attributes, access the attributes property and check which attributes start with data-* and end with -id. It's definitely more verbose, but it may work better in other scenarios.
$('.activityItem').each(function () {
var attributes = this.attributes;
Object.keys(attributes).forEach(function (key) {
var attribute = attributes[key];
if (/^data-.*-id$/.test(attribute.name)) {
console.log(attribute.name, attribute.value);
}
})
});

I just thought of what I was looking for. I guess my question confused you, but here's what I wanted:
$('#test').children().each(function () {
var reportbox = $(this);
var id = reportbox.data(Object.keys(reportbox.data())[0]);
reportbox.text(id);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="test">
<div data-question-id="1"></div>
<div data-answer-id="2"></div>
<div data-comment-id="3"></div>
<div data-user-id="4"></div>
<div data-company-id="5"></div>
</div>
The line of focus being:
var id = reportbox.data(Object.keys(reportbox.data())[0]);
Edit
Alternatively thanks to #Brian's comment I can rewrite this to:
var id = this.dataset(Object.keys(this.dataset)[0]);

Related

Create a unique identifier for a DOM element

I am creating an object that stores various elements and their CSS properties.
The code I have now:
// My object
var cssStorage = {};
function store(element, cssProperty, value) {
// Initialize the (sub-)objects if they don't exist
cssStorage[element.id] = cssStorage[element] || {};
cssStorage[element.id][cssProperty] = cssStorage[element][cssProperty] || {};
// Set the cssProperty to equal the value
cssStorage[element.id][cssProperty] = value;
};
Example:
// My element
var box = document.getElementById("box");
// Let's call the function twice to save to properties
store(box, "display", "block");
store(box, "height", "74px");
Now my Object is populated like so:
cssStorage = {
box: { // <- box is the id of the HTML element <div id = "box"></div>
// The property-value pairs
display: "block",
height: "74px"
}
};
So now, if I type the code in the console:
return cssStorage.box.display; // Returns "block"
As you saw in the first block of code I posted, I used element.id as the element's unique identifier, to be able to use it as shown right above.
My problem is the dependency of my script upon element.id. Some elements of my DOM don't have an id and therefore the function is useless for these elements.
In essence, what I want to achieve is to call the function store when my element doesn't have an ID as follows:
// Some ways to get an element
var box = document.getElementsByClassName("boxes")[0];
var box = document.getElementsByTagName("div")[0];
var box = document.getElementsByName("jack")[0];
// It'll show an error, as the function uses 'element.id' and my element doesn't have one
store(box, "display", "block");
Is there a unique identifier for every node in the DOM?
Something that I could use as the name of:
cssStorage = {
[THE NAME]: {}
};
If not, how can I create a unique identifier for my elements, so that I can use the function as shown above without needing an id, class or other property that my element may not have?
You can easily coin a unique identifier for any element that doesn't yet have one:
var customIDprefix = "__myCustomPrefix__";
var customIDcntr = 0;
function getNextID() {
return customIDprefix + customIDCntr++;
}
And, then you can make sure any element you're using has a unique ID:
function checkID(elem) {
if (!elem.id) {
elem.id = getNextID();
}
}
If you're using ES6, you can also just use a WeakMap or Map object as your CSSStorage mechanism which let the DOM element itself be the key so you don't have to make a string key.
In that case, you'd just do this:
var cssStorage = new Map();
cssStorage[elem] = { // <- elem (your DOM element itself) becomes your key into the cssStorage
// The property-value pairs
display: "block",
height: "74px"
}
You could use an integer to handle a sequence and set the id to elements that does not have it, prefixing to avoid duplicates (for example 'myid' + idSequence++).
Please check if this works. Basically trying to clone the original element and assign it back to the original after adding id with random generator.
function store(element, cssProperty, value) {
if ( element.id == undefined ) {
var clonedElem = element.cloneNode(true);
clonedElem.id = Math.floor((Math.random() * 1000) + 1);
element = clonedElem;
}
// Initialize the (sub-)objects if they don't exist
cssStorage.[element.id] = cssStorage[element] || {};
cssStorage.[element.id][cssProperty] = cssStorage.[element][cssProperty] || {};
// Set the cssProperty to equal the value
cssStorage.[element.id][cssProperty] = value;
};

Javascript: string regex

I am calling a function getParentElm(idStr,element) which accepts an id and an element, and searches up the html tree until it finds a parent element which has an id equal to idStr.
Calling code:
var s = "someId";
var el = getParentElm(s,element);
I would like the idStr parameter to work with strings that match to "someId", for eg "someId123".
I tried :
var s = "/someId/";
but it did not work. Ideally, i do not want to touch the getParentElm function.
Update: Thanks vbranden.
I tried: var s = /someId/ and that worked. I upvoted your comment. Thanks all :)
no regex needed.
use closest to traverse up the dom, and id* to match id's which contains your idStr.
function getParentElm(idStr,element){
return $(element).closest('[id*="' + idStr + '"]');
}
Here, this should work :
function getParentElm(idStr,element) {
var patt = new RegExp(idStr +".*");
while(element.parentNode)
{
if(patt.test(element.parentNode.id))
return element.parentNode;
element=element.parentNode;
}
return false;
}
And just call the function in this way :
var el = getParentElm("someId",document.getElementById('foo'));

Pass a variable directly into getElementById using the parameter (JavaScript)

I'm working on my first JavaScript game and I want to display certain attributes inside of < p> tags. The ID of each < p> tag will be equal to "show-" and the name of the attribute (see example below for the attribute "name").
But I'm having trouble getting the syntax right for getElementById. Any suggestions?
<p id="show-name"></p>
<script>
name = "Bob";
function display(attribute) {
putItHere = "'show-"+attribute+"'"
document.getElementById(putItHere).innerHTML = attribute;
}
display(name);
</script>
You need to target the right element. Currently you are targeting 'show-Bob' and not 'show-name' what your trying to do. So first generate the id, from the key name and then assign a value to that element.
var name = "Bob";
function display(key, value) {
var putItHere = "show-"+ key;
document.getElementById(putItHere).innerHTML = value;
}
display('name', name);
note keep in mind that IDs should be unique within the document
However, other way to do that is to target all elements with a specific tag, for instance
<div data-id="name"></div>
<div data-id="name"></div>
<div data-id="name"></div>
<script type="text/javascript">
var name = "Bob";
function display(key, value) {
var putItHere = "show-"+ key;
var elements = document.querySelectorAll('div[data-id="'+key+'"]');
for(var eid in elements) {
var element = elements[eid];
element.innerHTML = value;
}
}
display('name', name);
</script>
note that that this doesn't work in IE7 and below.
Your attribute is Bob. So putItHere = "'show-"+attribute+"'" is equivalent to: putItHere = "'show-"+"Bob"+"'", which makes no sense.
You should get the following error Uncaught TypeError: Cannot set property 'innerHTML' of null.
This should do:
function display(attrname, val){
document.querySelector('#show-'+attrname).innerText=val;
}
display("name","Bob");
Here is the Fiddle to play with.

Jquery getting nested inputs of Div, placing in Object

I am at my wits end with this nested loop.
I am trying to create an object with nested objects in it grouped by the containing div id.
So far this is what I have:
$('#survey-submit').on('click', function (e) {
var formData = {};
var item = {};
$("div[id^='question-']").each(function () {
var id = '#' + $(this).attr('id');
var $this = $(this);
formData[id] = $this.children('input, select,checkbox').each(function () {
item[this.name] = this.value;
});
//console.debug(formData);
});
console.debug(formData);
return false;
});
The result of the console log is all the input elements of the divs that have the start like question, I get the expected number of new objects all dynamically named, but they all contain the same thing.
The contents of each object are not specific to the this object I am trying to generate it from, any help would be appreciated!
I found a solution to the problem here : jquery how to get form element types, names and values
I wound up doing this:
$("div[id^='question-']").each(function () {
var id = '#' + $(this).attr('id');
var $this = $(this);
var inputs= $(id+' :input').map(function(index, elm) {
return {name: elm.name, value: $(elm).val()};
}).get();
formData[id]=inputs;
});

Find which event is specified for an object using Javascript Or Jquery?

Can we get the properties of HTML elements, with using there name or id, like following:
document.getElementById('id').value; //Javascript**
$('#id').attr('class'); // jquery to get corresponding elements class**
And like these is there any way to get the specified event? In Html side I do this:
"<"input name='student' onclick='show()'">"
My need is,
then how do we get which event is specified for input student?
Not sure I get it, but you can do something like
var el = document.getElementById("id"),
arr = [];
for (var i=0, attrs=el.attributes, l=attrs.length; i<l; i++){
if (attrs.item(i).nodeName.indexOf('on') === 0) {
var o = {};
o[attrs.item(i).nodeName] = attrs.item(i).nodeValue;
arr.push(o);
}
}
this would get all inline event handlers attached to the element and put in an array looking like :
[{'onclick' : 'show()'}, {'onmouseenter' : 'javascript:void(0)'}]
etc...
FIDDLE
var attrs = $('input').get(0).attributes;
var f = Array.prototype.filter.call(attrs, isEvent); //Filter all attributes
//f now contains all attribute nodes that are events
f.forEach(function(e) { console.log(e.nodeName); });
function isEvent(element) {
return element.nodeName.startsWith('on');
}
For the following input markup, it would log onclick, onchange. Works for events that are attached inline or event attributes created with JavaScript.
<input name='student' onclick='show()' onchange='return true;'/>
Or
var el = $('input').get(0);
el.setAttribute('onkeyup', function() {});
getEventsFor(el).forEach(function(e) {
console.log(e.nodeName); //onSomeEvent
console.log(e.nodeValue); // attached handler
});
function isEvent(element) {
return element.nodeName.startsWith('on');
}
function getEventsFor(element) {
var attrs = element.attributes;
return Array.prototype.filter.call(attrs, isEvent);
}
JSFiddle

Categories