可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I need to generate unique css selector for elements.
Particularly, I have onclick event handler that should remember what target element
was clicked and send this info to my server. Is there a way to do it without doing DOM modifications?
P.S. my javascript code supposed to be run on different
3-rd party websites so I can't make any assumptions about html.
回答1:
let say you have a list of links for the sake of simplicity: you can simply pass the index of the triggering element in the collection of all elements
<a href="#">...</a>
<a href="#">...</a>
<a href="#">...</a>
the js (jQuery 1.7+, I used .on()
otherwise use bind()
) function can be
var triggers = $('a');
triggers.on('click', function(e) {
e.preventDefault();
var index = triggers.index($(this));
/* ajax call passing index value */
});
so that if you click on third element index value passed will be 2. (0-based index);
of course this is valid as long as the code (the DOM) doesn't change. Later you can use that index to create a css rule to that element e.g. using :nth-child
Otherwise if each one of your elements have a different attribute (like an id) you can pass that attribute
example on JsFiddle : http://jsfiddle.net/t7J8T/
回答2:
Yes, you could do this. But with a few caveats. In order to be able to guarantee that selectors are unique, you'd need to use :nth-child()
which isn't universally supported. If you're then wanting to put these CSS selectors into CSS files, it won't work in all browsers.
I'd do it with something like this:
function () {
if (this.id) {
return sendToServer('#' + this.id);
}
var parent = this.parentNode;
var selector = '>' + this.nodeName + ':nth-child(' + getChildNumber(this) ')';
while (!parent.id && parent.nodeName.toLowerCase() !== 'body') {
selector = '>' + this.nodeName + ':nth-child(' + getChildNumber(parent) + ')' + selector;
parent = parent.parentNode;
}
if (parent.nodeName === 'body') {
selector = 'body' + selector;
} else {
selector = '#' + parent.id + selector;
}
return sendToServer(selector);
}
Then add that to your click handler for each element you want to model. I'll leave you to implement getChildNumber()
.
Edit: Just seen your comment about it being 3rd party code... so you could add an event
argument, replace all instances of this
with event.target
and then just attach the function to window
's click event if that's easier.
回答3:
This function creates a long, but quite practical unique selector, works quickly.
const getCssSelector = (el) => {
let path = [], parent;
while (parent = el.parentNode) {
path.unshift(`${el.tagName}:nth-child(${[].indexOf.call(parent.children, el)+1})`);
el = parent;
}
return `${path.join(' > ')}`.toLowerCase();
};
Example result:
html:nth-child(1) > body:nth-child(2) > div:nth-child(1) > div:nth-child(1) > main:nth-child(3) > div:nth-child(2) > p:nth-child(2)
The following code creates a slightly more beautiful and short selector
const getCssSelectorShort = (el) => {
let path = [], parent;
while (parent = el.parentNode) {
let tag = el.tagName, siblings;
path.unshift(
el.id ? `#${el.id}` : (
siblings = parent.children,
[].filter.call(siblings, sibling => sibling.tagName === tag).length === 1 ? tag :
`${tag}:nth-child(${1+[].indexOf.call(siblings, el)})`
)
);
el = parent;
};
return `${path.join(' > ')}`.toLowerCase();
};
Example result:
html > body > div > div > main > div:nth-child(2) > p:nth-child(2)
回答4:
You could probably traverse the DOM tree from the node back to the body element to generate a selector.
Firebug has a feature for this, both using XPath and CSS selectors.
See this answer
回答5:
"use strict";
function getSelector(_context){
var index, localName,pathSelector, that = _context, node;
if(that =='null') throw 'not an dom reference';
index = getIndex(that);
while(that.tagName){
pathSelector = that.localName+(pathSelector?'>'+pathSelector :'');
that = that.parentNode;
}
pathSelector = pathSelector+':nth-of-type('+index+')';
return pathSelector;
}
function getIndex(node){
var i=1;
var tagName = node.tagName;
while(node.previousSibling){
node = node.previousSibling;
if(node.nodeType === 1 && (tagName.toLowerCase() == node.tagName.toLowerCase())){
i++;
}
}
return i;
}
document.addEventListener('DOMContentLoaded', function(){
document.body.addEventListener('mouseover', function(e){
var c = getSelector(e.target);
var element = document.querySelector(c);
//console.log(element);
console.log(c);
//element.style.color = "red"
});
});
you can try this one. without using jquery.
回答6:
Check this CSS selector generator library @medv/finder
- Generates shortest selectors
- Unique selectors per page
- Stable and robust selectors
- 2.9 kB gzip and minify size
Example of generated selector:
.blog > article:nth-child(3) .add-comment