Get all ellements and attributes of an element root using jQuery - javascript

I need to find all elements name in tag and all attributes in each element by using JQuery , each element has different attributes name and value i need to know this attributes name and value but don't know how
<Circuit>
<C Type="Bipolar" Name="c1">
<Cathode row="10" col="20"/>
<Anode row="15" col="50"/>
</C>
<AND Name="and1">``
<Input>
<Pin row="10" col="40"></Pin>
<Pin row="20" col="40"></Pin>
</Input>
<Output>
<Pin row="30" col="50"></Pin>
</Output>
</AND>
<Diode Name="d1" Value="30">
<Anode row="30" col="50"></Anode>
<Cathode row="40" col="60"></Cathode>
</Diode>
<R Type="Constant" Name="r1">
<Node row="60" col="80"></Node>
<Node row="70" col="80"></Node>
</R>
</Circuit>
I'll explain what i need, first need to know all elements (tag name) only in tag
o/p like this
elements in circuit tag
C
AND
Diode
R
after this need to know all attributes and elements in each element
like this
first element (C) has 2 attribute
first attribute is "Type" and the value of this attribute ="Bipolar"
second attribute is "Name" and the value of this attribute ="c1"
and has 2 elements
Cathode
Anode
and the same in each element

In jQuery there is the attribute matcher [attr], out of many other selectors
$("[Name]")
will get you all the elements that has the attribute Name
you can then loop through and get the actual name with jQuerys attr() function
$("[Name]").each(function() {
console.log($(this).attr("Name"));
console.log($(this).attr("Value"));
});
Edit
In the comments I found out that the question actually is how to serialize the whole structure into a data set. Therefore this might be a possible duplicate https://stackoverflow.com/a/5287305/576725

The following prior question shows how to get all the attributes of an element using JavaScript. The same, and the elements can be obtained using jQuery:
Get all Attributes from a HTML element with Javascript/jQuery
So that your code works cross-browser define your xml variable as follows:
var xml = $( $.parseXML( 'XML-String' ) );
You can the get all the elements and attributes as follows:
var all = xml.find('*').map(function(v,i) {
var ux = {};
$.each( this.attributes, function(index, node) {
ux[ node.nodeName ] = node.nodeValue;
});
return $.extend({}, { nodeName: this.nodeName }, {attributes:ux});
})
.get();
Which should give you output that looks like:
[{"nodeName":"Circuit","attributes":{}},{"nodeName":"C","attributes":{"Type":"Bipolar","Name":"c1"}},{"nodeName":"Cathode","attributes":{"row":"10","col":"20"}}, ....... .. .. .. ]
JS FIDDLE DEMO

Related

Javascript: Select all data-qa attributes on HTML Page

Using only JavaScript, without the use of JQuery etc, what is the most efficient way to select all attributes names that have a certain data attribute (let's say data-qa).
<p data-foo="0"></p><br/><h6 data-qa="apple"></h6>
<p data-foo="0"></p><br/><h6 data-qa="book"></h6>
<p data-foo="0"></p><br/><h6 data-qa="car"></h6>
Expected result should be list :
apple
book
car
This question gets the parent elements, I want the attributes themselves. Select all elements with "data-" attribute without using jQuery
Resources:
Selenium find element via Data-Qa attribute
Data-QA Attribute: A better way to select elements for UI test automation
The code below works by getting all of the elements using document.querySelectorAll, mapping the elements to the values of the data attributes, and then filtering out the ones that are falsy or '0'.
let getAllDataAttrs = name => Array.from(
document.querySelectorAll(`[data-${name}]`)
).map(elem => elem.getAttribute(`data-${name}`))
.filter(val => val && val !== '0');
console.log(getAllDataAttrs('foo'));
<p data-foo="0"></p><br/>
<h6 data-foo="apple"></h6>
<p data-foo="0"></p><br/>
<h6 data-foo="book"></h6>
<p data-foo="0"></p><br/>
<h6 data-foo="car"></h6>
For reference, below is essentially the core vanilla javascript functionality needed ...
Using querySelectorAll, querySelector and getElementById should get you started.
// get "data-qa" value by "id"
var data = document.getElementById('id-test2').getAttribute('data-qa');
console.log(data);
// get "id" value by "data-qa"
var data = document.querySelector('[data-qa="data-qa-test2"]').id;
console.log(data);
// get all elements with attribute "data-qa"
var elems = document.querySelectorAll('[data-qa]');
// get all "ids"
var data = [];
elems.forEach(function(elem){
data.push(elem.id);
});
console.log(data);
// get all "data-qa"
var data = [];
elems.forEach(function(elem){
data.push(elem.getAttribute('data-qa'));
});
console.log(data);
<div id="id-test" data-qa="data-qa-test"></div>
<div id="id-test1" data-qa="data-qa-test1"></div>
<div id="id-test2" data-qa="data-qa-test2"></div>
<div id="id-test3" data-qa="data-qa-test3"></div>

Protractor Custom Locator is getting failed to locate the element

In our project, the automation team will include custom attribute 'lid' to the element where ever we cannot identify the element uniquely.
I created a custom locator method to detect the element using custom attribute 'lid'. It is below:
by.addLocator('lid', function(value, parentElement)
{
parentElement = parentElement || document;
var nodes = parentElement.querySelectorAll('lid');
return Array.prototype.filter.call(nodes, function(node)
{
return (node.getAttribute('lid') === value);
});
});
Here the html code of the page
<div class="col-sm-8 cbx">
<p class="ng-binding"><input type="radio" ng-model="theme"
lid="create-user-them-radio1" ng-value="1" ng-click="user.theme = -1" class="ng-
pristine ng-untouched ng-valid ng-not-empty" name="1194" value="1"> Use the default
for this domain</p>
<p class="litetext ng-binding">Following options override
the domain's default value</p>
<p class="ng-binding"><input type="radio" ng-model="theme"
lid="create-user-them-radio2" ng-value="2" class="ng-pristine ng-untouched ng-
valid ng-not-empty" name="1195" value="2"> Select theme for the user</p>
</div>
I want to use 'lid' attribute and locate the element and wrote the above method to work with any attribute and value.
I call the method from the test file as below:
element(by.lid("create-user-them-radio1")).click();
I am getting 'No element found using locator: by.lid("create-user-theme-radio1")'
Need help to resolve the issue.
why you need to create custom method if you can do the same things by defaults methods
element(by.xpath('.//li[contains(#ng-if, "$select.open")]')).click();
PS: if you will run tests on prod mode, then this locator will not work. attributes like ng-%blabla% embedded only in dev mode.
I am able to find the correct solution for this and the solution is as below:
by.addLocator('lid', function(value, parentElement) {
var nodes;
if(parentElement)
nodes = parentElement.querySelectorAll();
else
nodes = document.querySelectorAll('body *');
return Array.prototype.filter.call(nodes, function(node) {
if( node.hasAttribute("lid") && node.getAttribute('lid') === value)
return node;
});
});
querySelectorAll() needs to give the input as 'tag', if no parentElement is sent then it queries all the tags under 'body' tag otherwise all elements from the parentElement. After that loop through all the elements and check whether attribute 'lid' exists or not. If exists verify the value, if the value is desired value then it return that element.

Find an element in a DOM given the exact html of the element

I needed an efficient way to search for a 13 digit numeric code (call it ncode) appearing in an arbitrary way in a html page e.g. it might be <p>ncode</p> or it could be <input type="hidden" value="ncode"> or <span content="ncode"></span>.
I created a regex that finds the html fragment I need and in a specific case it returns
<span itemprop="gtin13" content="0885913103914"></span>
Having done that, I thought it would be easy to use jQuery to find the actual DOM element but I can't seem to find anything that works. I tried $(body).filter(":contains()") and various similar things.
So the question is, what is the best way to located the element in the DOM whose html matches a known string?
If it can't be done, I guess I'll have to parse the found html string for different properties and then select elements using those properties. It just seems there should be a way of directly selecting the element so I can query and manipulate it in the DOM.
Try this simple approach
Find the name of the node which represent the html-to-find
Find the classes of the node which represent the html-to-find
Prepare a selector with the above two information and execute the same.
Demo
var htmlToFind = '<span class="clas31" attribute1="4">fdgdfg</span>';
var nodeName = $( htmlToFind )[0].nodeName;
var classList = [].slice.call($( htmlToFind )[0].classList).join(".");
var isFound = $( nodeName + "." + classList ).length > 0
console.log( isFound );
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<span class="class1" attribute1="2">345345</span>
<span class="class2" attribute1="2"><a>a34rrt5345</a></span>
<span class="clas31" attribute1="4">fdgdfg</span>
<div asd="sd">rdfgdfg</div>
</div>
You can optimize this further by checking the other attributes of html-to-find as well.
If the number of nodes returned by nodename and classes are more than 1, then filter them further as
var htmlToFind = '<span class="clas31" attribute1="4">fdgdfg</span>';
var nodeName = $( htmlToFind )[0].nodeName;
var classList = [].slice.call($( htmlToFind )[0].classList).join(".");
var length = $( nodeName + "." + classList ).length
console.log( "total matches by classnames and node name " + length );
var exactMatches = [];
$( nodeName + "." + classList ).each( function(){
if ( this.outerHTML == htmlToFind )
{
exactMatches.push( this )
}
});
console.log( "exactMatches" , exactMatches )
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<span class="class1" attribute1="2">345345</span>
<span class="class2" attribute1="2"><a>a34rrt5345</a></span>
<span class="clas31" attribute1="4">fdgdfg</span>
<span class="clas31" attribute1="4">fdg2dfg</span>
<div asd="sd">rdfgdfg</div>
</div>
You can do what you're asking but I'd recommend not doing it, purely for performance reasons. If you take every element in the document body and parse them with some defined logic (dependent on how you want to identify the particular elements) then you can get a collection of just the matching elements.
This example shows finding the elements with the specific value anywhere in the element...
// this returns the element(s) that contain the given text
var elements = $("body span").filter(function() {
return this.outerHTML.match("0885913103914");
}).toArray();
elements.forEach(function(el) { console.log(el.innerText); });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<span itemprop="gtin13" content="0885913103914">Span with correct content attribute.</span><br />
<span itemprop="gtin13" content="0885913103915">Span with incorrect content attribute.</span><br />
<span itemprop="gtin13" content="0885913103914">Span with correct content attribute.</span><br />
<span itemprop="gtin13" content="0885913103915">Span with 0885913103914 inside the contents.</span><br />
You would need to adjust the filter function to suit other criteria. Basically, if you return true then the element is included in the output.
If you can do this effectively with your original regex solution then I'd recommend that instead.

the correct way to append an input element to a form

So far anytime i have needed to add an input to a form dynamically i have done:
var row = "<input type='text' id='someId' name='someName' value='someValue'>";
$('#form').append(row);
I know there is a better way. I have read about setAttribute or .attr(). how would i start?
var input = ???;
input.attr('type','text');
input.attr('id','someId');
input.attr('name','someName');
input.attr('val','someValue');
input.attr('type','text');
$('#form').append(input);
Yes there is a better way exist,
$('#form').append($("<input>",{
"type" : "text",
"id" : "someId",
"name" : "someName",
"value" : "someValue"
}));
Don't mix up pure jquery with javascript. That may results in error sometimes. like invoking jquery functions over node object. Ex : node.css(...)
When I need to add elements dynamically I do the following:
var $_form = $("#form");
$("<input/>",{ id : "input_1",type : "text", val : "value" }).addClass("class1 class2").appendTo($_form);
This form works for any element, not just form fields.
For brevity you can just do the following, all in one line:
$('#form').append("<input type='text' id='someId' name='someName' value='someValue'>");
but for more complex cases you can take the longer approach:
$("<input/>", {
attributes
});
You can do this using JavaScript's createElement() and setAttribute(), like so:
var input = document.createElement("input");
input.setAttribute("type", "text");
//and so on
As #scottmarcus has pointed out this is is not necessarily better but different. It's what you're looking for:
var input = document.createElement("INPUT");
Which is a DOM element; so you cannot use jQuery methods such as .attr(). You would have to use JavaScript methods such as setAttribute(). In order to use jQuery methods all you have to do is to wrap input in $ like so:
var input = $( document.createElement("INPUT") );
A better way to write this would be:
var input = $('<input/>');
Ref: https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement

Insert span in a dom element without overwrite child nodes?

I have an HTML article with some annotations that I retrieve with SPARQL queries. These annotations refer to some text in the document, and I have to highlight this text (wrapping it in a span).
I had already asked how to wrap text in a span, but now I have a more specific problem that I do not know how to solve.
The code I wrote was:
var currentText = $("#"+v[4]["element"]+"").text();
var newText = currentText.substring(0, v[5]["start"]) + "<span class=' annotation' >" + currentText.substring(v[5]["start"], v[6]["end"]) + "</span>" + currentText.substring(v[6]["end"], currentText.length);
$("#"+v[4]["element"]+"").html(newText);
Where:
v[4]["element"] is the id of the parent element of the annotation
v[5]["start"] is the position of the first character of the annotation
v[6]["end"] is the position of the last character of the annoation
Note that start and end don't consider html tags.
In fact my mistake consists in extracting data from the node with the text() method (to be able to go back to the correct position of the annotation) and put back with the html() method; but in this manner if parent node has children nodes, they will be lost and overwritten by simple text.
Example:
having an annotation on '2003'
<p class="metadata-entry" id="k673f4141ea127b">
<span class="generated" id="bcf5791f3bcca26">Publication date (<span class="data" id="caa7b9266191929">collection</span>): </span>
2003
</p>
It becomes:
<p class="metadata-entry" id="k673f4141ea127b">
Publication date (collection):
<span class="annotation">2003</span>
</p>
I think I should work with nodes instead of simply extract and rewrite the content, but I don't know how to identify the exact point where to insert the annotation without considering html tags and without eliminating child elements.
I read something about the jQuery .contents() method, but I didn't figure out how to use it in my code.
Can anyone help me with this issue? Thank you
EDIT: Added php code to extract body of the page.
function get_doc_body(){
if (isset ($_GET ["doc_url"])) {
$doc_url = $_GET ["doc_url"];
$doc_name = $_GET ["doc_name"];
$doc = new DOMDocument;
$mock_doc = new DOMDocument;
$doc->loadHTML(file_get_contents($doc_url.'/'.$doc_name));
$doc_body = $doc->getElementsByTagName('body')->item(0);
foreach ($doc_body->childNodes as $child){
$mock_doc->appendChild($mock_doc->importNode($child, true));
}
$doc_html = $mock_doc->saveHTML();
$doc_html = str_replace ('src="images','src="'.$doc_url.'/images',$doc_html);
echo($doc_html);
}
}
Instead of doing all these, you can either use $(el).append() or $(el).prepend() for inserting the <span> tag!
$("#k673f4141ea127b").append('<span class="annotation">2003</span>');
Or, If I understand correctly, you wanna wrap the final 2003 with a span.annotation right? If that's the case, you can do:
$("#k673f4141ea127b").contents().eq(1).wrap('<span class="annotation" />');
Fiddle:
$(document).ready(function() {
$("#k673f4141ea127b").contents().eq(1).wrap('<span class="annotation" />');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p class="metadata-entry" id="k673f4141ea127b">
<span class="generated" id="bcf5791f3bcca26">Publication date (<span class="data" id="caa7b9266191929">collection</span>): </span>
2003
</p>
At the end my solution is in this Fiddle.
Generalizing:
var element = document.getElementById(id);
var totalText = element.textContent;
var toFindText = totalText.substring(start,end);
var toReplaceText = "<span class='annotation'>"+toFindText+"</span>";
element.innerHTML = element.innerHTML.replace(toFindText, toReplaceText);
Hope it could help someone else.
Note: This don't check if two or more annotations refers to the same node, I'm working on it right now.

Categories

Resources