jQuery: how to change tag name? - javascript

jQuery: how to change tag name?
For example:
<tr>
$1
</tr>
I need
<div>
$1
</div>
Yes, I can
Create DOM element <div>
Copy tr content to div
Remove tr from dom
But can I make it directly?
PS:
$(tr).get(0).tagName = "div";
results in DOMException.

You can replace any HTML markup by using jQuery's .replaceWith() method.
example: http://jsfiddle.net/JHmaV/
Ref.: .replaceWith
If you want to keep the existing markup, you could use code like this:
$('#target').replaceWith('<newTag>' + $('#target').html() +'</newTag>')

No, it is not possible according to W3C specification: "tagName of type DOMString, readonly"
http://www.w3.org/TR/DOM-Level-2-Core/core.html

Where the DOM renameNode() Method?
Today (2014) no browser understand the new DOM3 renameNode method (see also W3C)
check if run at your bowser: http://jsfiddle.net/k2jSm/1/
So, a DOM solution is ugly and I not understand why (??) jQuery not implemented a workaround?
pure DOM algorithm
createElement(new_name)
copy all content to new element;
replace old to new by replaceChild()
is something like this,
function rename_element(node,name) {
var renamed = document.createElement(name);
foreach (node.attributes as a) {
renamed.setAttribute(a.nodeName, a.nodeValue);
}
while (node.firstChild) {
renamed.appendChild(node.firstChild);
}
return node.parentNode.replaceChild(renamed, node);
}
... wait review and jsfiddle ...
jQuery algorithm
The #ilpoldo algorithm is a good start point,
$from.replaceWith($('<'+newname+'/>').html($from.html()));
As others commented, it need a attribute copy ... wait generic ...
specific for class, preserving the attribute, see http://jsfiddle.net/cDgpS/
See also https://stackoverflow.com/a/9468280/287948

The above solutions wipe out the existing element and re-create it from scratch, destroying any event bindings on children in the process.
short answer: (loses <p/>'s attributes)
$("p").wrapInner("<div/>").children(0).unwrap();
longer answer: (copies <p/>'s attributes)
$("p").each(function (o, elt) {
var newElt = $("<div class='p'/>");
Array.prototype.slice.call(elt.attributes).forEach(function(a) {
newElt.attr(a.name, a.value);
});
$(elt).wrapInner(newElt).children(0).unwrap();
});
fiddle with nested bindings
It would be cool to copy any bindings from the at the same time, but getting current bindings didn't work for me.

To preserve the internal content of the tag you can use the accessor .html() in conjunction with .replaceWith()
forked example: http://jsfiddle.net/WVb2Q/1/

Inspired by ericP answer, formatted and converted to jQuery plugin:
$.fn.replaceWithTag = function(tagName) {
var result = [];
this.each(function() {
var newElem = $('<' + tagName + '>').get(0);
for (var i = 0; i < this.attributes.length; i++) {
newElem.setAttribute(
this.attributes[i].name, this.attributes[i].value
);
}
newElem = $(this).wrapInner(newElem).children(0).unwrap().get(0);
result.push(newElem);
});
return $(result);
};
Usage:
$('div').replaceWithTag('span')

Working pure DOM algorithm
function rename_element(node, name) {
let renamed = document.createElement(name);
Array.from(node.attributes).forEach(attr => {
renamed.setAttribute(attr.name, attr.value);
})
while (node.firstChild) {
renamed.appendChild(node.firstChild);
}
node.parentNode.replaceChild(renamed, node);
return renamed;
}

You could go a little basic. Works for me.
var oNode = document.getElementsByTagName('tr')[0];
var inHTML = oNode.innerHTML;
oNode.innerHTML = '';
var outHTML = oNode.outerHTML;
outHTML = outHTML.replace(/tr/g, 'div');
oNode.outerHTML = outHTML;
oNode.innerHTML = inHTML;

To replace the internal contents of multiple tags, each with their own original content, you have to use .replaceWith() and .html() differently:
http://jsfiddle.net/kcrca/VYxxG/

JS to change the tag name
/**
* This function replaces the DOM elements's tag name with you desire
* Example:
* replaceElem('header','ram');
* replaceElem('div.header-one','ram');
*/
function replaceElem(targetId, replaceWith){
$(targetId).each(function(){
var attributes = concatHashToString(this.attributes);
var replacingStartTag = '<' + replaceWith + attributes +'>';
var replacingEndTag = '</' + replaceWith + '>';
$(this).replaceWith(replacingStartTag + $(this).html() + replacingEndTag);
});
}
replaceElem('div','span');
/**
* This function concats the attributes of old elements
*/
function concatHashToString(hash){
var emptyStr = '';
$.each(hash, function(index){
emptyStr += ' ' + hash[index].name + '="' + hash[index].value + '"';
});
return emptyStr;
}
Related fiddle is in this link

Since replaceWith() didn't work for me on an element basis (maybe because I used it inside map()), I did it by creating a new element and copying the attributes as needed.
$items = $('select option').map(function(){
var
$source = $(this),
$copy = $('<li></li>'),
title = $source.text().replace( /this/, 'that' );
$copy
.data( 'additional_info' , $source.val() )
.text(title);
return $copy;
});
$('ul').append($items);

Take him by the word
Taken the Question by Word "how to change tag name?" I would suggest this solution:
If it makes sense or not has to be decided case by case.
My example will "rename" all a-Tags with hyperlinks for SMS with span tags. Maintaining all attributes and content:
$('a[href^="sms:"]').each(function(){
var $t=$(this);
var $new=$($t.wrap('<div>')
.parent()
.html()
.replace(/^\s*<\s*a/g,'<span')
.replace(/a\s*>\s*$/g,'span>')
).attr('href', null);
$t.unwrap().replaceWith($new);
});
As it does not make any sense to have a span tag with an href attribute I remove that too.
Doing it this way is bulletproof and compatible with all browsers that are supported by jquery.
There are other ways people try to copy all the Attributes to the new Element, but those are not compatible with all browsers.
Although I think it is quite expensive to do it this way.

Jquery plugin to make "tagName" editable :
(function($){
var $newTag = null;
$.fn.tagName = function(newTag){
this.each(function(i, el){
var $el = $(el);
$newTag = $("<" + newTag + ">");
// attributes
$.each(el.attributes, function(i, attribute){
$newTag.attr(attribute.nodeName, attribute.nodeValue);
});
// content
$newTag.html($el.html());
$el.replaceWith($newTag);
});
return $newTag;
};
})(jQuery);
See : http://jsfiddle.net/03gcnx9v/3/

Yet another script to change the node name
function switchElement() {
$element.each(function (index, oldElement) {
let $newElement = $('<' + nodeName + '/>');
_.each($element[0].attributes, function(attribute) {
$newElement.attr(attribute.name, attribute.value);
});
$element.wrapInner($newElement).children().first().unwrap();
});
}
http://jsfiddle.net/rc296owo/5/
It will copy over the attributes and inner html into a new element and then replace the old one.

$(function(){
$('#switch').bind('click', function(){
$('p').each(function(){
$(this).replaceWith($('<div/>').html($(this).html()));
});
});
});
p {
background-color: red;
}
div {
background-color: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Hello</p>
<p>Hello2</p>
<p>Hello3</p>
<button id="switch">replace</button>

You can use this function
var renameTag = function renameTag($obj, new_tag) {
var obj = $obj.get(0);
var tag = obj.tagName.toLowerCase();
var tag_start = new RegExp('^<' + tag);
var tag_end = new RegExp('<\\/' + tag + '>$');
var new_html = obj.outerHTML.replace(tag_start, "<" + new_tag).replace(tag_end, '</' + new_tag + '>');
$obj.replaceWith(new_html);
};
ES6
const renameTag = function ($obj, new_tag) {
let obj = $obj.get(0);
let tag = obj.tagName.toLowerCase();
let tag_start = new RegExp('^<' + tag);
let tag_end = new RegExp('<\\/' + tag + '>$');
let new_html = obj.outerHTML.replace(tag_start, "<" + new_tag).replace(tag_end, '</' + new_tag + '>');
$obj.replaceWith(new_html);
};
Sample code
renameTag($(tr),'div');

Try this one also. in this example we can also have attributes of the old tag in new tag
var newName = document.querySelector('.test').outerHTML.replaceAll('h1', 'h2');
document.querySelector('.test').outerHTML = newName;
<h1 class="test">Replace H1 to H2</h1>

Related

extract div as HTML including CSS classes

I am trying to create a general function that will extract a div content (with nested elements) and save it locally in an HTML file.
Basically I get the div innerHTML, wrap it in html/head/body tags and then save it:
function div2html() {
var inner=document.getElementById("div2save").innerHTML;
var html="<html><head></head><body>"+inner+"</body></html>";
saveTextAsFile("div2html.html", html);
}
See a working version here: jsfiddle
However I am not sure how to handle classes. As you can see the class in the sample (bigbold) is not embedded in the new HTML. I need some way to get all the classes used in the div and then add them (or the computed styles ?) to the html I generate .. is this possible ? is there any other way around it ?
Try including style element .outerHTML within saved html
function div2html() {
var inner=document.getElementById("div2save").innerHTML;
var style = document.getElementsByTagName("style")[0].outerHTML;
var html="<html><head>"+style+"</head><body>"+inner+"</body></html>";
saveTextAsFile("div2html.html", html);
}
jsfiddle http://jsfiddle.net/fb6s763w/1/
Alternatively, using window.getComputedStyle() to select only css of #div2save child node
function div2html() {
var inner = document.getElementById("div2save");
var style = window.getComputedStyle(inner.children[0]).cssText;
var html = "<html><head><style>"
+ "." + inner.children[0].className
+ "{" + style + "}"
+ "</style></head><body>"
+ inner.innerHTML + "</body></html>";
saveTextAsFile("div2html.html", html);
}
jsfiddle http://jsfiddle.net/fb6s763w/2/
Looks like this might be able to help you out:
https://github.com/Automattic/juice
If the CSS of the page is not big, a simple solution is to include it all in the saved html as suggested by guest271314 above with
var style = document.getElementsByTagName("style")[0].outerHTML;
see jsfiddle
A more comprehensive solution extracts the classes from the div and then adds only the rules of those classes to the div (Using code from How do you read CSS rule values with JavaScript?)
function div2html(divId) {
var html = document.getElementById(divId).innerHTML;
// get all css classes in html
var cssClasses = [];
var classRegexp = /class=['"](.*?)['"]/g;
var m;
while ((m = classRegexp.exec(html))) cssClasses = cssClasses.concat(cssClasses, m[1].split(" "));
// filter non unique or empty cssClasses
cssClasses = cssClasses.filter(function (item, pos, self) {
return item && self.indexOf(item) == pos;
});
// get html of classes
var cssHtml = '';
for (var i = 0; i < cssClasses.length; i++) cssHtml += getRule('.' + cssClasses[i]);
// assemble html
var html = "<html><head><style>" + cssHtml + "</style></head><body>" + html + "</body></html>";
console.log(html);
saveTextAsFile("div2html.html", html);
}
see jsfiddle

Write text over div without using .text()

I've got again a rather simple question, that I couldn't find an answer to.
I was using sofar the Jquery function .text() to write text on mouseenter on a dynamically created div. I came to realise that this only worked on my Iceweasel, but not in Chrome for instance. Instead ot .text() everywhere people advised of using the .val(), but I can't seem to figure out exactly how to use it in my implementation, since the divs had no previous text value.
Please find below a simple code, with .text() to understnad the question.
(function(){
for (var i = 0; i < 3; i++) {
var span = document.createElement("span");
span.innerHTML = "<img width=\"" + data.size[i][0] + "\" height=\"" + data.size[i][1] + "\" id=\"" + i + "\">";
span.style.position = "absolute";
span.style.left = data.coords[i][0] + "px";
span.style.top = data.coords[i][1] + "px";
document.body.appendChild(span);
}
}());
for (var i=0; i<3; i++) {
$('#' + i).mouseenter(function() {
$(this).text("text");
});
$('#' + i).mouseleave(function() {
$(this).text("")
});
}
http://jsfiddle.net/ckpx6esj/1/
I hope someone can give me an idea, of how to apply .val() or use something else entirely to make this work for chrome also.
Best Regards and Thanks in advance!
The problem is that you put text in an image tag!
<img>Some text</img>
This is invalid HTML, see this answer.
If you want text over an image, I suggest using a div with background: url(...) instead.
Updated fiddle.
The cleverest I could think to don't screw up your for loop is appending a <p> tag containing your text and removing it on mouseleave:
for (var i=0; i<3; i++){
$('#' + i).on("mouseenter",function() {
$(this).parent().append("<p>text</p>");
});
$('#' + i).on("mouseleave",function() {
$(this).parent().find("p").remove();
});
}
Fiddle:
http://jsfiddle.net/ckpx6esj/2/
Besides, text was not working because you are listening to the image (<img>) instead of the span. Images has no .text() prototype, hence you should access its parent() (which is a <span> in that case) if you want to use the .text() prototype, but using .text() on the parent will remove the image, hence the idea of appending the text and removing it later.
According to specification, val() function is to set value attribute and it only matters for input fields on your page. text() function is to change content of your element.
The .val() method is primarily used to get the values of form elements
such as input, select and textarea.
So you should use text() function in your code.
Also according to your code you change text property of <img> element. This is not good. You should change text of your <span>. So just move your id to span element.
If you want the jQuery equivalent of Javascript's native innerHtml, go for $(this).html('text');.
Take a look at these functions:
http://api.jquery.com/html/
$(this).html('text');
http://api.jquery.com/append/
$(this).append('text'); // Note that this appends instead of replaces
http://api.jquery.com/val/
$(this).val('text');
Or if you're feeling adventurous:
http://api.jquery.com/appendto/
$('text').appendTo($(this)); // Performance penalty for creating an object out of 'text'
First I will use class instead id, it will save using the second loop,
also if you want to have also text and also image you can do it but it will be littel complicated I would recommand add some child element to the span that will contain the text, I didnt do it just for the challenge
http://jsfiddle.net/ckpx6esj/5/
simple plugin to change the text without changing the html elements
$.fn.selectorText = function(text) {
var str = '';
this.contents().each(function() {
if (this.nodeType === 3) {
if(typeof(text) === 'string'){
this.textContent = text;
return false;
}else{
str += this.textContent || this.innerText || '';
}
}
});
return str;
};
var thisData = [{
'coords' : [[100,100], [300, 300], [200, 200]],
'size' : [[30, 30], [30, 30], [30, 30]]
}];
var data = thisData[0];
(function(){
for (var i = 0; i < 3; i ++){
var span = document.createElement("span");
span.setAttribute('class','spanImage');
span.style.position = "absolute";
span.style.left = data.coords[i][0] + "px";
span.style.top = data.coords[i][1] + "px";
span.innerHTML = "\n<img width=\"" + data.size[i][0] + "\" height=\"" + data.size[i][1] + "\" id=\"" + i + "\">";
document.body.appendChild(span);
}
$('.spanImage')
.on( 'mouseenter', function() {
$(this).selectorText('text');
})
.on( 'mouseleave', function() {
$(this).selectorText('');
});
}());

Change existing DIV from a CLASS to an ID

Is this possible? Or is there a way to tack on and ID to an existing div?
This is my code. I can't get the code to work using classes, but I found when I used getElementById and changed the div to an ID, that it did. But I have a ton of already posted stuff so it would take forever to go through all those posts and change it manually to an ID.
Can I incorperate JQuery in this and still have it work? I tried that with something I stumbled across but it didn't work so I removed it. I don't remember what it is now though. :S
<div id="imdb" class="imdb">tt2382396</div>
<script>
function imdbdiv() {
var imdbmain = "http://www.imdb.com/title/";
var end = "/#overview-top";
var idnum = document.getElementsByClassName("imdb");
var newdiv = document.createElement("div");
var done = "<a href='" + imdbmain + idnum + end + "'>IMDB</a>";
newdiv.innerHTML = done;
document.body.appendChild(newdiv);
}
window.onload = imdbdiv();
</script>
Can anyone help. I cannot for the life of me figure this out.
JsFiddle
Your problem was, you were appending the collection returned by document.getElementsByClassName instead of looping through the elements in the collection. You can verify this by looking at the href property of the link in your jsFiddle. You must loop through the values, then access the data in their innerHTML property.
You can use document.querySelectorAll to get a list of all elements matching a certain CSS selector, in your case .imdb. This is more flexible, in case you want to select elements with more than one class. I've pasted the code from the updated jsFiddle below.
function imdbdiv() {
var imdbMain = "http://www.imdb.com/title/",
end = "/#overview-top",
imdbValueDivs = document.querySelectorAll('.imdb'),
length = imdbValueDivs.length,
// Iterator values
i,
newDiv,
newLink;
// Loop over all of your link value containers
for (i = 0; i < length; i++) {
// Create the container
newDiv = document.createElement('div');
// Create the new link
newLink = document.createElement('a');
newLink.href = imdbMain + imdbValueDivs[i].innerHTML + end;
newLink.innerHTML = "My favorite film";
// Add the link to the container,
// and add the container to the body
newDiv.appendChild(newLink);
document.body.appendChild(newDiv);
}
}
window.onload = imdbdiv();
If you have many such divs on your page, then it could be like this:
<div class="imdb">tt2382396</div>
<div class="imdb">tt2382396</div>
<div class="imdb">tt2382396</div>
<script>
function imdbdiv() {
var imdbmain = "http://www.imdb.com/title/";
var end = "/#overview-top";
var idnums = document.getElementsByClassName("imdb");
for (var i =0; i < idnums.length; i++) {
var newdiv = document.createElement("div");
var done = "<a href='" + imdbmain + idnums[i].innerText + end + "'>IMDB</a>";
newdiv.innerHTML = done;
document.body.appendChild(newdiv);
}
}
window.onload = imdbdiv();
</script>
See jsfiddle
UPDATE:
The following string was incorrect:
window.onload = imdbdiv;
Okay, so your question is a little bit unclear.
The way I understood your question is that you have a whole bunch of div elements with class attribute and what you want is to simply copy the class value to the id attribute of the div elements.
If that's correct then try something like this with jquery:
<script>
$(document).ready(function(){
$(".imdb").each(function(imdbDiv){
var classValue = imdbDiv.attr("class");
imdbDiv.attr("id", classValue);
});
});
</script>

Accessing HTML Elements that have been created by setting innerHTML

I have a Function which makes an ajax call and then return a piece of html that is inserted into a table's td element. using element.innerHTML=ajaxResult; Now I want to access the elements in the ajaxResult that have the name attribute as special. So I do a document.getElementsByName('special') and expect to get 4-5 elements. But I actually get none.
This makes it impossible for me to access those elements and I am stuck. Please help me resolve this. Thanks in Advance!
I think this is related to the dom not reloading after I set innerHTML. But not sure how to reload it.
I am using IE8 in IE8 compatibility view and IE7 standards :(
EDIT
This is my function
function handleStateChange()
{
if(ajaxRequest.readyState==4 && ajaxRequest.status==200) {
var responseStr = ajaxRequest.responseText;
var splitResult = responseStr.split("$$$$$$$$$$$$$$$$$$$$");
var leftHtml= splitResult [0];
var rightHtml= splitResult [1];
document.getElementById("div1").innerHTML=leftHtml;
if(rightHtml !="") {
document.getElementById("div2").innerHTML=rightHtml;
}
if(splitResult.length >=3 ){
var appActionflag = splitResult [2];
document.getElementById("userAction").innerHTML=appActionflag;
}
if(splitResult.length >= 4 ){
var userId = splitResult [3];
document.getElementById("userId").innerHTML=userId;
}
reverseDNASwitch();
var grpList = document.getElementsByName('parmGrpId');
alert('javascript is working! Found:'+grpList.length);
for(var i=0;i<grpList.length;i++){
alert('Got GroupId: '+ (i));
var grpTd = grpList[i];
grpTd.innnerHTML='Hi';
}
}
}
If the table cell you're adding to is, itself, in the DOM, that should work. (And so I may have to delete this answer; originally I thought document.getElementsByName had been deprecated, but I was mistaken). Here's an example using getElementsByName:
Live Copy | Live Source
(function() {
// Get the target
var target = document.getElementById("target");
// Dynamically add content
target.innerHTML =
'<div name="special">special 1</div>' +
'<div name="special">special 2</div>' +
'<div name="special">special 3</div>';
// Get those elements
var list = document.getElementsByName("special");
// Prove we got them
var p = document.createElement('p');
p.innerHTML = "Found " + list.length + " 'special' elements";
document.body.appendChild(p);
})();
Of course, because it's a function of document, that will find all of the elements with name="special", not just the ones you added to the table cell.
The above does not work on IE if the elements aren't allowed to have the name attribute. So for instance, if you look for getElementsByName("special"). it will ignore <div name="special"> but find <input name="special">, because name is not a valid attribute for div elements. Details in this MSDN article. Worse, IE will include elements whose id matches, even though of course that has nothing to do with name. sigh
Unless you need to support IE7 and earlier (e.g., unless you're developing for China), you can use Element#querySelectorAll with the selector '[name="special"]'. That will look only within the element for elements that use that name attribute.
Example: Live Copy | Live Source
(function() {
// Get the target
var target = document.getElementById("target");
// Dynamically add content
target.innerHTML =
'<div name="special">special 1</div>' +
'<div name="special">special 2</div>' +
'<div name="special">special 3</div>';
// Get those elements
var list = target.querySelectorAll('[name="special"]');
// Prove we got them
var p = document.createElement('p');
p.innerHTML = "Found " + list.length + " 'special' elements";
document.body.appendChild(p);
})();
If you need to support IE7 or earlier, you might look at this other Stack Overflow question and my answer to it. The question points to this article about adding querySelectorAll to document, and my answer to the question talks about how to emulate that at an element-specific level.
So combining the code from that article with my answer to the other question and the examples above, we get:
Live Copy | Live Source
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body>
<div id="target"></div>
<script>
(function() {
// IE7 support for querySelectorAll. Supports multiple / grouped selectors and the attribute selector with a "for" attribute. http://www.codecouch.com/
if (!document.querySelectorAll) {
(function(d, s) {
d=document, s=d.createStyleSheet();
d.querySelectorAll = function(r, c, i, j, a) {
a=d.all, c=[], r = r.replace(/\[for\b/gi, '[htmlFor').split(',');
for (i=r.length; i--;) {
s.addRule(r[i], 'k:v');
for (j=a.length; j--;) a[j].currentStyle.k && c.push(a[j]);
s.removeRule(0);
}
return c;
}
})();
}
var qsaWorker = (function() {
var idAllocator = 10000;
function qsaWorkerShim(element, selector) {
var needsID = element.id === "";
if (needsID) {
++idAllocator;
element.id = "__qsa" + idAllocator;
}
try {
return document.querySelectorAll("#" + element.id + " " + selector);
}
finally {
if (needsID) {
element.id = "";
}
}
}
function qsaWorkerWrap(element, selector) {
return element.querySelectorAll(selector);
}
// Return the one this browser wants to use
return document.createElement('div').querySelectorAll ? qsaWorkerWrap : qsaWorkerShim;
})();
// Get the target
var target = document.getElementById("target");
// Dynamically add content
target.innerHTML =
'<div name="special">special 1</div>' +
'<div name="special">special 2</div>' +
'<div name="special">special 3</div>';
// Get those elements
var list = qsaWorker(target, '[name="special"]');
// Prove we got them
var p = document.createElement('p');
p.innerHTML = "Found " + list.length + " 'special' elements";
document.body.appendChild(p);
})();
</script>
</body>
</html>
Which works in IE7.
As an option for your specific scenario, where you want to get elements with a specific attribute (and value), you can use a function like this:
function getElementsByAttribute(options) {
/*if (container.querySelectorAll) {
var selector = '';
if (options.tagFilter) {
selector += options.tagFilter;
}
selector += '[' + options.attr;
if (options.val) {
selector += '="' + options.val.replace(/"/g, '\\"') + '"';
}
selector += ']';
return Array.prototype.slice.call(options.container.querySelectorAll(selector));
}*/
var elements = options.container.getElementsByTagName(options.tagFilter || "*"),
ret = [],
i, cur,
matches = (function () {
if (options.val) {
return function (el) {
return el.getAttribute(options.attr) === options.val;
};
} else {
return function (el) {
return el.hasAttribute(options.attr);
};
}
})();
for (i = 0; i < elements.length; i++) {
cur = elements[i];
if (matches(cur)) {
ret.push(cur);
}
}
return ret;
}
And you call it like:
window.onload = function () {
var contain = document.getElementById("container"),
els = getElementsByAttribute({
container: contain,
attr: "name",
val: "special",
tagFilter: ""
});
console.log("els", els);
};
DEMO: http://jsfiddle.net/cxq6t/1/
(I kept in my original logic for supporting querySelectorAll, but proved not to work in old IE with invalid attributes, per the comments)
The object you pass to the function accepts:
container: containing element to look through
attr: the attribute to look for
val: optional value to match against
tagFilter: optional filter for the tagName of matched elements
I tested in IE7/8 to see if the <div name="special"> would match, and it does. As well as the other <input />s.
If you ever wanted to expand the selector to be more complicated things like what querySelectorAll supports, you could use a polyfill like T.J.Crowder's. But this seems to get the job done.

Get unique selector jQuery

I need to be able to get an unqiue selector for each element on a page.
For example, when I click on an element I want to do something like this:
$(document).click(function(){
var sel = getUniqueSel(this);
});
So, after storing the sel value in a DB I can get that value and simply access the element by
var el = $(sel);
I can't change and don't know anything about the HTML structure of the page and I can't simply add unique ID's (using JS) to every element as this would be inefficient.
Another approach might be to wander up the dom tree and create a path to the element, which you can save and use it later as a selector again, although that might not be bulletproof, but maybe its a point where you can start off.
Edit: Updated the Answer with your suggestion in the comment, now it returns the id if available
Just visit the example on JSBin And click the document twice.
but notice what gets highlighted..
jQuery.fn.getPath = function () {
if (this.length != 1) throw 'Requires one element.';
var path, node = this;
if (node[0].id) return "#" + node[0].id;
while (node.length) {
var realNode = node[0],
name = realNode.localName;
if (!name) break;
name = name.toLowerCase();
var parent = node.parent();
var siblings = parent.children(name);
if (siblings.length > 1) {
name += ':eq(' + siblings.index(realNode) + ')';
}
path = name + (path ? '>' + path : '');
node = parent;
}
return path;
};
var sel;
$(document)
.click(function (e, a) {
if (!sel) {
sel = $("#comment-21702402")
.getPath();
alert("Path is: " + sel + ", hiding the Element -> Click again to highlight");
} else {
$(sel)
.css("background-color", "yellow");
}
});
One way to do this is to get all the information you can get on the element that was clicked.
So when you save it to the database you can save it as a text for example:
If the element you clicked on is: <div> I'm a div </div>
$(document).click(function(){
var tagName = $(this).prev().prop('tagName');
var attributes = {};
if( this.length ) {
$.each( this[0].attributes, function( index, attr ) {
attributes[ attr.name ] = attr.value;
} );
}
var elText=$(this).html();
saveToDB(tagName,attributes,elText);
});
You can later find the element using the attributes you have or simply use
$(tagName+'['+attribute+'="'+value+'"]:contains("'+elText+'")')
I think this should help

Categories