Select an object property within an attribute of the DOM - javascript

I looked at this thread but it didn't help: Find an element in DOM based on an attribute value
I need a Vanilla JS solution to select a username that is in a custom attribute of an <a> tag in the DOM. I used const users = document.querySelectorAll('a[metadata]') which returns a NodeList of all the <a> tags with all their attributes. If I type console.log(users[0]), I get:
<a href="#" class="btn-normal" metadata="
{
'username':'johnny134',
'category':'shoes',
'summary':'Just a guy called Johnny',
'userId':1223432432
}"
</a>
To access the username, I have tried const usernames = document.querySelectorAll('a[metadata="username"]'); but just get back undefined. I think my issue is that username is in an object and I can't figure out how to select it.
Thanks in advance

First, note that document.querySelectorAll returns a list of elements that match the given query, not a list of the values of attributes of those elements that you specify in the query.
From const users = document.querySelectorAll('a[metadata]'), you can get the metadata attribute of, say, the first element, like so:
const metadata0 = users[0].getAttribute("metadata");
And then, since it's a JSON string, you parse it:
const user0 = JSON.parse(metadata0);
Now you can use user0 like a regular JavaScript object:
user0.username
// johnny134
P.S. The JSON in the metadata attribute is not valid, and you may get an error when you try to parse it. Use double quotes in JSON, not single quotes.

Related

Get object by HTML tag and remove from localstorage

this is my first question on stackoverflow.
and a little bit experience on code.
so I have a localstorage like this:
myDB: "[{"key:"123","label":"abc"}]
I have a div with "abc" as value:
<div id="name">abc</div>
And many id's div clone with different value
<div id="name">abc</div>
<div id="name">cde</div>
<div id="name">efg</div>
I want to read the value of the ID "name", make a if/else like looking "abc" are in the localstorage, if yes delete it with the key. else not delete.
I have thinking of using document.getElement to get value from ID and compare it to localstorage and using if else to do that thing. But there are many clone have that event to trigger the function to delete it. So the function don't know which ID's value to be compare and delete it.
I really awkward for this newbie question. But I have to ask, many thanks first :)
*New question:
I want to delete last element of the localstorage.
Can I convert localstorage to array then using array.pop(). Then convert the changed array again to the localstorage?
First, as was mentioned by others, id must be unique. You can use any other attribute instead, for example, class:
<div class="name">abc</div>
<div class="name">def</div>
<div class="name">ghi</div>
<div class="name">jkl</div>
<div class="other">mno</div>
Then, to query these elements, you could use document.getElementsByClassName("name") which will return you an array-like object. You can convert this object to an array of values using a combination of spread syntax and map method:
let values = [...document.getElementsByClassName("name")].map(e => e.innerHTML);
To work with the local storage you can use localStorage.setItem and localStorage.getItem. As you know, the local storage stores only strings, so JSON.parse and JSON.stringify methods will be helpful too.
Here is the example of code:
localStorage.setItem("myDB", '[{"key":"123","label":"abc"}, {"key":"456","label":"mno"}]');
console.log('Local storage before: ', localStorage.getItem("myDB"));
// extracting div values to an array
let values = [...document.getElementsByClassName("name")].map(e => e.innerHTML);
// creating a js object from myDB string
let db = JSON.parse(localStorage.getItem("myDB"));
// leaving only those elements, which labels are not in the values array
localStorage.setItem("myDB", JSON.stringify(db.filter(item => !values.includes(item.label))));
console.log('Local storage after: ', localStorage.getItem("myDB"));
JSFiddle link: https://jsfiddle.net/v03wpgq1/4/
id attributes should be unique on the page, otherwise only the last one on the page has the ability to be referenced easily (or at least, properly).
There should be a section that contains these which makes them easily queryable. Perhaps inside of an element with an id guaranteed to uniquely hold a set of name value pairs.
Using document.querySelectorAll is your best bet for finding these elements, and will be made easier by creating a structure that can be queried.
With a set of items to look for, it should be easy to iterate and test for values and keys inside of localStorage.

Return object bound to a dynamic element when label for object is also dynamic

I am using CKEditor. Within my page, the user can dynamically add/remove the element containing the WYSIWYG ckeditor text editor.
CKEDITOR.instances returns an object which contains within it all the ck_editor objects on my page.
When the user clicks the add button, then the following code successfully grabs that element:
CKEDITOR.instances[“my_textarea_0_body"]
The issue is when the user clicks delete to remove that element, and then reclicks add. I now want to grab that new ckeditor element on the page. However, now I need to grab it with this:
CKEDITOR.instances[“my_textarea_1_body"]
Notice that the number changed. So, the user is able to toggle the add/remove of this element any number of times. Example: if they did it 100 times I would need to have a way to grab that object like so:
CKEDITOR.instances[“my_textarea_100_body"]
The problem is that I never know what that number will be. That number is vital for me to create the string in order to grab the appropriate object.
Question: How can I grab this dynamically labeled object that is contained within the CKEDITOR.instances object? I know that my desired object will always be the LAST object appended within that CKEDITOR.instances object.
I assume that CKEDITOR.instancess a kind of a map (dictionary), so you can get all key names by Object.keys(). And then select the last/first/ or n-th instance name.
var mapping_length = Object.keys(CKEDITOR.instances).length;
var object_label = Object.keys(CKEDITOR.instances)[mapping_length - 1];
CKEDITOR.instances[object_label];
This will return the desired object from within that dictionary object.
Regex indeed is your friend here. /^CKEDITOR\.instances\["my_textarea_\d+_body"\]$/.test(str) should get the job done. (if you copy and paste any of your initial examples to test, it will fail however since you've got an angled quote illegal character in there)
console.log(/^CKEDITOR\.instances\["my_textarea_\d+_body"\]$/.test('CKEDITOR.instances["my_textarea_0_body"]'))
I think I understand what you're getting at though - you know the vague structure of the key, but not exactly what it will be when you're trying to retrieve it. In that case, you'd want to search through the keys of the CKEDITOR.instances object for any that match that pattern. So, let matchingKeys = Object.keys(CKEDITOR.instances).filter(key => /^my_textarea_\d+_body$/.test(key)). That will return a set of all keys that match that pattern.
You can create a helper function which checks for a regex match. The regex for that field should be:
my_textarea_\d+_body
Then you can modify/add the new object key to instances

Javascript reading multidimensional form

This has driven me "doo-lally" this afternoon!
A vendor (Zaxaa) uses a multi-dimentional form thus:
<form method="post" name="zaxaa" action="xxxx">
<input type="text" name="products[0][prod_name]" value="ABC">
<input type="text" name="products[0][prod_type]" id="pt" value="FRONTEND">
</form>
** This is my understanfing of how a multdimentional array is set up, and it seems to pass the variables to the server OK.
However, dependant on what other inputs are set to on the test form, the [prod_type] (and others) may need to change to "OTO" This is obviously going to be a javascript function, (but not the variant that starts with "$" on code lines ... whatever that type is!)
I have tried
document.zaxaa.products[0].prod_type.value
document.getElementById('products[0][prod_type]').value
document.getElementsByName('products[0][prod_type]').value
but in everycase, I get "products is not defined". (I have simplified the form as there are ten product[0] fields)
I've solved it... mainly a glaring error on my part. The getElementById worked fine ... except in my test script I'd used getElementById[xxx] and not getElementById(xxx)!! ie "[" rather than "(" Does help if you get the syntax right!
But I will take notice of those other methods, such as enclosing both array arguments in ["xxx"].
getElementById didn't work because the only one of those elements that has an id is the second input, with id="pt".
On any modern browser, you can use querySelector to get a list of the inputs using a CSS selector:
var nameInput = document.querySelector('input[name="products[0][prod_name]"]');
var typeInput = document.querySelector('input[name="products[0][prod_type]"]');
Then use their value property. So for instance, to set the name to "OTO":
document.querySelector('input[name="products[0][prod_name]"]').value = "OTO";
Use querySelectorAll if you need a list of relevant inputs, e.g.:
var nameInputs = document.querySelectorAll('input[name="products[0][prod_name]"]');
Then loop through them as needed (the list as a length, and you access elements via [n] where n is 0 to length - 1).
Re
* This is my understanfing of how a multdimentional array is set up...
All that HTML does is define input elements with a name property. That name property is sent to the server as-is, repeated as necessary if you have more than one field with that name. Anything turning them into an array for you is server-side, unrelated to JavaScript on the client. (The [0] is unusual, I'm used to seeing simply [], e.g name="products[][prod_name]".)
You can access it in this syntax:
document.zaxaa['products[0][prod_name]'].value
document.zaxaa.products[0].prod_type.value
The name is a single string, not making a nested structure to access the input. It would need to be
document.zaxaa["products[0][prod_type]"].value
// or better:
document.forms.zaxaa.elements["products[0][prod_type]"].value
The complicated name does only serve to parse the data into a (multidimensional) array on the server side, but all data will be sent "flattened".
document.getElementById('products[0][prod_type]').value
The id of your input is pt, so this should work as well:
document.getElementById("pt").value
document.getElementsByName('products[0][prod_type]').value
getElementsByName does return a collection of multiple elements - which does not have a .value property itself. Instead, access the first element in the collection (or iterate it completely):
document.getElementsByName('products[0][prod_type]')[0].value

How to read raw content of data-* attribute using jQuery?

Maybe I am missing something, but I use data-* attribute, namely data-content with such entries like "apple", "cow", etc. Suddenly I was hit when I set my data-content with string "null" (I checked using Chromium HTML is:
<p class="text" data-content="null">null</p>
But when I read data-content using jQuery:
text_elem.data('content')
I get null value, not "null" string.
So my question is, how to read it in raw form, so I could get "null" string exactly as it is in HTML?
If you want to read it in the raw form you would need to use .attr instead of .data. Because that is what data api does it parses the content of the data attribute (ex JSON --> object, numeric string value to number etc...).
text_elem.attr('data-content');
Or you could use dataset (Not supported in older browsers) with the DOM element.
text_elem[0].dataset.content; //this will return a string
You get null instead of "null" because jQuery has for some reason decided to parse data- attributes as JSON when fetching using .data().
To get the raw data, use .attr() instead. This will be faster, and it doesn't make a copy of the value into jQuery.cache, which is very often unnecessary.
Basically, only use .data() for data- attributes if you need the data stored in jQuery.cache and if you're alright with it being parsed as JSON.
The .find() method could be use to search an XML document for the element name specified. For the HTML document, if you want to access the attribute you must use the .attr() method.

get attribute from selectNodes in JavaScript and XML?

I am trying to get an attribute from an XML node from JavaScript.
item.selectNodes("enclosure[#url]")
That is not working like I thought it would :(
Any hints ?
thanks!
[#url]
is a predicate, which does not select the attribute but filters the "enclosure" nodes that do have a url attribute.
In XPath,
enclosure/#url
would select the attribute.
This:
item.selectNodes("enclosure[#url]")
will give you a collection of enclosure nodes that have a url attribute.
To get a collection of url attribute nodes that are on enclosure nodes, do this:
item.selectNodes("enclosure/#url")
Which you must then loop over to get the values of each one. Remember this gives you attribute nodes, not attribute values. You can use attributeNode.nodeValue to get the value from the node.
If you are expecting just one such node, then use selectSingleNode instead of selectNodes. This will give you the first matching node, instead of a collection of all matching nodes.

Categories