ユーザーのすべてのアクションを追跡するレコーダーのようなものを作成したい。そのため、後のセッションでこれらの要素を参照できるように、ユーザーが対話する要素を識別する必要があります。
擬似コードで話されて、次のようなことができるようになりたい
サンプルHTML(複雑なものも可能):
<html>
<body>
<div class="example">
<p>foo</p>
<span><a href="bar">bar</a></span>
</div>
</body>
</html>
ユーザーがリンクなどの何かをクリックします。ここで、クリックした要素を特定し、後で使用するためにその場所をDOMツリーに保存する必要があります。
(any element).onclick(function() {
uniqueSelector = $(this).getUniqueSelector();
})
これで、uniqueSelectorは次のようになります(xpathまたはcssセレクタースタイルの場合は気にしません)
html > body > div.example > span > a
これにより、そのセレクター文字列を保存し、後で使用して、ユーザーが行ったアクションを再生することができます。
そんなことがあるものか?
私の答えを得ました: 要素のjQueryセレクターを取得する
私が修正しなければならない解決策を見つけたので、私は自分でこれに答えます。次のスクリプトは動作しており、 Blixtのスクリプト に基づいています。
jQuery.fn.extend({
getPath: function () {
var path, node = this;
while (node.length) {
var realNode = node[0], name = realNode.localName;
if (!name) break;
name = name.toLowerCase();
var parent = node.parent();
var sameTagSiblings = parent.children(name);
if (sameTagSiblings.length > 1) {
var allSiblings = parent.children();
var index = allSiblings.index(realNode) + 1;
if (index > 1) {
name += ':nth-child(' + index + ')';
}
}
path = name + (path ? '>' + path : '');
node = parent;
}
return path;
}
});
@Alpのものと同じソリューションですが、複数のjQuery要素と互換性があります。
jQuery('.some-selector')
は、1つまたは複数のDOM要素になります。 @Alpのソリューションは、残念ながら最初のソリューションでのみ機能します。私のソリューションは、それらすべてを,
で連結します。
最初の要素のみを処理する場合は、次のようにします。
jQuery('.some-selector').first().getPath();
// or
jQuery('.some-selector:first').getPath();
改善されたバージョン
jQuery.fn.extend({
getPath: function() {
var pathes = [];
this.each(function(index, element) {
var path, $node = jQuery(element);
while ($node.length) {
var realNode = $node.get(0), name = realNode.localName;
if (!name) { break; }
name = name.toLowerCase();
var parent = $node.parent();
var sameTagSiblings = parent.children(name);
if (sameTagSiblings.length > 1)
{
var allSiblings = parent.children();
var index = allSiblings.index(realNode) + 1;
if (index > 0) {
name += ':nth-child(' + index + ')';
}
}
path = name + (path ? ' > ' + path : '');
$node = parent;
}
pathes.Push(path);
});
return pathes.join(',');
}
});
私はより良い解決策はランダムなIDを生成し、そのIDに基づいて要素にアクセスすることだと思います:
一意のIDの割り当て:
// or some other id-generating algorithm
$(this).attr('id', new Date().getTime());
一意のIDに基づいて選択する:
// getting unique id
var uniqueId = $(this).getUniqueId();
// or you could just get the id:
var uniqueId = $(this).attr('id');
// selecting by id:
var element = $('#' + uniqueId);
// if you decide to use another attribute other than id:
var element = $('[data-unique-id="' + uniqueId + '"]');
_(any element).onclick(function() {
uniqueSelector = $(this).getUniqueSelector();
})
_
this
[〜#〜]は[〜#〜]クリックされた要素への一意のセレクターとパスです。なぜそれを使用しないのですか? jqueryの$.data()
メソッドを使用して、jqueryセレクターを設定できます。または、将来使用する必要がある要素をプッシュするだけです。
_var elements = [];
(any element).onclick(function() {
elements.Push(this);
})
_
本当にxpathが必要な場合は、次のコードを使用して計算できます。
_ function getXPath(node, path) {
path = path || [];
if(node.parentNode) {
path = getXPath(node.parentNode, path);
}
if(node.previousSibling) {
var count = 1;
var sibling = node.previousSibling
do {
if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {count++;}
sibling = sibling.previousSibling;
} while(sibling);
if(count == 1) {count = null;}
} else if(node.nextSibling) {
var sibling = node.nextSibling;
do {
if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {
var count = 1;
sibling = null;
} else {
var count = null;
sibling = sibling.previousSibling;
}
} while(sibling);
}
if(node.nodeType == 1) {
path.Push(node.nodeName.toLowerCase() + (node.id ? "[@id='"+node.id+"']" : count > 0 ? "["+count+"]" : ''));
}
return path;
};
_
ES6での質問はjQueryに対するものでしたが、Vanilla JavaScriptの@Alpに似たものを入手するのは非常に簡単です(nameCount
を追跡して、nth-child
):
function getSelectorForElement (elem) {
let path;
while (elem) {
let subSelector = elem.localName;
if (!subSelector) {
break;
}
subSelector = subSelector.toLowerCase();
const parent = elem.parentElement;
if (parent) {
const sameTagSiblings = parent.children;
if (sameTagSiblings.length > 1) {
let nameCount = 0;
const index = [...sameTagSiblings].findIndex((child) => {
if (elem.localName === child.localName) {
nameCount++;
}
return child === elem;
}) + 1;
if (index > 1 && nameCount > 1) {
subSelector += ':nth-child(' + index + ')';
}
}
}
path = subSelector + (path ? '>' + path : '');
elem = parent;
}
return path;
}
注:これは Array.from および Array.prototype.filter を使用します。どちらも必要ですIE11でポリフィルされます。
function getUniqueSelector(node) {
let selector = "";
while (node.parentElement) {
const siblings = Array.from(node.parentElement.children).filter(
e => e.tagName === node.tagName
);
selector =
(siblings.indexOf(node)
? `${node.tagName}:nth-of-type(${siblings.indexOf(node) + 1})`
: `${node.tagName}`) + `${selector ? " > " : ""}${selector}`;
node = node.parentElement;
}
return `html > ${selector.toLowerCase()}`;
}
使用法
getUniqueSelector(document.getElementsByClassName('SectionFour')[0]);
getUniqueSelector(document.getElementById('content'));
この回答は元の質問の説明を満たしていませんが、タイトルの質問には答えています。要素の一意のセレクターを取得する方法を探してこの質問に来ましたが、ページロード間でセレクターを有効にする必要はありませんでした。そのため、ページロード間で私の答えは機能しません。
DOMの変更はidelではないように感じますが、コードの調整なしでユニークなセレクターを構築する良い方法です。 @Eliの答えを読んだ後、私はこのアイデアを得ました:
一意の値を持つカスタム属性を割り当てます。
$(element).attr('secondary_id', new Date().getTime())
var secondary_id = $(element).attr('secondary_id');
次に、その一意のIDを使用してCSSセレクターを作成します。
var selector = '[secondary_id='+secondary_id+']';
次に、要素を選択するセレクターがあります。
var found_again = $(selector);
そして、あなたは多くの人がすでにsecondary_id
属性の要素。
if ($(element).attr('secondary_id')) {
$(element).attr('secondary_id', (new Date()).getTime());
}
var secondary_id = $(element).attr('secondary_id');
すべてを一緒に入れて
$.fn.getSelector = function(){
var e = $(this);
// the `id` attribute *should* be unique.
if (e.attr('id')) { return '#'+e.attr('id') }
if (e.attr('secondary_id')) {
return '[secondary_id='+e.attr('secondary_id')+']'
}
$(element).attr('secondary_id', (new Date()).getTime());
return '[secondary_id='+e.attr('secondary_id')+']'
};
var selector = $('*').first().getSelector();
ID属性(たとえばid = "something")がある場合、次のような値を取得する必要があります。
var selector = "[id='" + $(yourObject).attr("id") + "']";
console.log(selector); //=> [id='something']
console.log($(selector).length); //=> 1
アイデンティティー属性がなく、セレクターを取得したい場合は、アイデンティティー属性を作成できます。上記のようなもの、
var uuid = guid();
$(yourObject).attr("id", uuid); // Set the uuid as id of your object.
独自のGUIDメソッドを使用するか、 this にあるソースコードを使用できます。
function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
}
findCssSelector もご覧ください。コードは私の その他の答え にあります。
次のようなことができます:
_$(".track").click(function() {
recordEvent($(this).attr("id"));
});
_
onclick
クラスを持つすべてのオブジェクトにtrack
イベントハンドラーをアタッチします。オブジェクトがクリックされるたびに、そのIDがrecordEvent()
関数に渡されます。この関数に、各オブジェクトの時間とID、または必要なものを記録させることができます。
$(document).ready(function() {
$("*").click(function(e) {
var path = [];
$.each($(this).parents(), function(index, value) {
var id = $(value).attr("id");
var class = $(value).attr("class");
var element = $(value).get(0).tagName
path.Push(element + (id.length > 0 ? " #" + id : (class.length > 0 ? " .": "") + class));
});
console.log(path.reverse().join(">"));
return false;
});
});
*セレクター(非常に遅い)を使用し、イベントのバブリングを停止するときに問題が発生する可能性がありますが、HTMLコードがないと実際には解決できません。