web-dev-qa-db-ja.com

要素にIDがない場合でも、一意の要素ID

一連の要素を繰り返し処理するGreaseMonkeyスクリプトを書いています。要素ごとに、後でその要素を参照するために使用できる文字列IDが必要です。要素自体にはid属性がなく、元のドキュメントを変更してそれを与えることはできません(ただし、スクリプトでDOMを変更することはできます)。参照を必要なときに、GreaseMonkeyスクリプト自体がスコープ外になったため、参照をスクリプトに保存できません。たとえば、ブラウザが使用する「内部」IDを取得する方法はありますか? Firefoxのみのソリューションで問題ありません。他のシナリオに適用できるクロスブラウザソリューションは素晴らしいでしょう。

編集:

  • GreaseMonkeyスクリプトが範囲外の場合、後で要素をどのように参照しますか?彼らはGreaseMonkeyスクリプトがDOMオブジェクトにイベントを追加しています。イベントが発生すると、GreaseMonkeyスクリプトがスコープ外になったため、配列が失われるため、参照を配列または他の同様のメカニズムに格納できません。したがって、イベントには、イベントがアタッチされたときにスクリプトが持っていた要素参照について知るための何らかの方法が必要です。そして、問題の要素は、それがアタッチされている要素ではありません。

  • 要素でカスタムプロパティを使用できないのですか?はい、問題はルックアップにあります。カスタムプロパティを目的のIDに設定した要素を探すために、すべての要素を反復処理する必要があります。それは確かに機能しますが、大きなドキュメントでは非常に時間がかかる可能性があります。私はブラウザがルックアップの大げさな仕事をすることができる何かを探しています。

  • ドキュメントを変更できますか?ソースドキュメントを変更できませんが、スクリプトでDOMを変更できます。質問で明らかにします。

  • クロージャを使用できませんか?クロージャは最初は機能しないと思っていましたが、機能することがわかりました。私の後の投稿を参照してください。

「使用できる内部ブラウ​​ザIDはありますか?」という質問の答えのように聞こえます。 「いいえ」です

31

PDATE:クロージャーは確かに答えです。それで、もう少しいじってみて、クロージャーが最初に問題があった理由とそれを修正する方法を理解しました。クロージャーのトリッキーなことは、すべてのクロージャーが同じエレメントを参照することにならないように、エレメントを反復処理するときに注意する必要があることです。たとえば、これは機能しません:

for (var i = 0; i < elements.length; i++) {
    var element = elements[i];
    var button = document.createElement("button");
    button.addEventListener("click", function(ev) {
        // do something with element here
    }, false)
}

しかし、これは:

var buildListener = function(element) {
    return function(ev) {
        // do something with event here
    };
};

for (var i = 0; i < elements.length; i++) {
    var element = elements[i];
    var button = document.createElement("button");
    button.addEventListener("click", buildListener(element), false)
}

とにかく、質問には2つの回答があったため、1つの回答を選択しないことにしました。1)いいえ、使用できる内部IDはありません。 2)これにはクロージャーを使用する必要があります。そのため、内部IDがあるかどうか、またはIDの生成を推奨した人に加えて、クロージャーについて言及している人がいるかどうかを最初の人に伝えただけです。助けてくれてありがとう!

5

答えは「いいえ」です。アクセスできる内部IDはありません。 Opera and IE(たぶんSafari?)は_.sourceIndex_(DOMが変更すると変更されます)をサポートしますが、Firefoxにはこの種の機能はありません。

特定のノードへのXpathを生成するか、ノードのインデックスをdocument.getElementsByTagName('*')から検索して、常にソース順に要素を返すことで、ソースインデックスをシミュレートできます。

もちろん、これにはすべて完全に静的なファイルが必要です。 DOMを変更すると、ルックアップが壊れます。

私が理解していないのは、ノードへの参照を失うことができるが、(理論的な)内部IDへの参照を失うことができない方法です。クロージャーと割り当ては機能するか機能しないかのどちらかです。それとも何か不足していますか?

7
Borgar

質問の文言に少し戸惑いました-「後でその要素を参照するために[あなた]が使用できる文字列IDが必要である」と言いましたが、 "[自分の]スクリプトに参照を保存できないため、[あなたは]それらを必要とする、GreaseMonkeyスクリプト自体は範囲外になります。」

スクリプトが範囲外になった場合、後でそれらをどのように参照しますか?

私はあなたが得ていることに混乱しているという事実を無視し、Greasemonkeyスクリプトを頻繁に記述し、DOMをcan変更できることを伝えますIDプロパティを与えるためにアクセスする要素。これは、一時的に使用するための疑似一意の値を取得するために使用できるコードです。

var PseudoGuid = new (function() {
    this.empty = "00000000-0000-0000-0000-000000000000";
    this.GetNew = function() {
        var fourChars = function() {
            return (((1 + Math.random()) * 0x10000)|0).toString(16).substring(1).toUpperCase();
        }
        return (fourChars() + fourChars() + "-" + fourChars() + "-" + fourChars() + "-" + fourChars() + "-" + fourChars() + fourChars() + fourChars());
    };
})();

// usage example:
var tempId = PseudoGuid.GetNew();
someDomElement.id = tempId;

それは私にとってはうまくいきます、私は自分でGreasemonkeyスクリプトでそれをテストしました。


PDATE:クロージャは進むべき道です-個人的には、ハードコアJavaScript開発者として、私はあなたがどのようにしているかわかりませんできなかったすぐに考えてください。 :)

myDomElement; // some DOM element we want later reference to

someOtherDomElement.addEventListener("click", function(e) {
   // because of the closure, here we have a reference to myDomElement
   doSomething(myDomElement);
}, false);

さて、myDomElementは、説明からすでに明らかに持っている要素の1つです(IDを追加することなどを考えていたため)。

たぶん、あなたがやろうとしていることの例を投稿すれば、そうではないと仮定して、あなたを助けるほうが簡単でしょう。

6
Jason Bunting

クロージャーは進むべき道です。このようにして、DOMのシャッフルにも耐えられる要素を正確に参照できます。

閉鎖を知らない人のための例:

var saved_element = findThatDOMNode();

document.body.onclick = function() 
{
   alert(saved_element); // it's still there!
}

Cookieに保存する必要がある場合、XPathを計算することをお勧めします(たとえば、IDを持つ要素を見つけて終了するまで、前の兄弟をカウントするDOMを調べます) [@id=foo]/div[4]/p[2]/a)。

XPointer は、その問題に対するW3Cのソリューションです。

6
Kornel

can DOMに書き込む場合(できると思います)。私はこれを次のように解決します:

関数がIDを返すか生成するようにします。

//(function () {

  var idCounter = new Date().getTime();
  function getId( node ) {
    return (node.id) ? node.id : (node.id = 'tempIdPrefix_' + idCounter++ );
  }

//})();

これを使用して、必要に応じてIDを取得します。

var n = document.getElementById('someid');
getId(n);  // returns "someid"

var n = document.getElementsByTagName('div')[1];
getId(n);  // returns "tempIdPrefix_1224697942198"

この方法では、サーバーがHTMLを渡したときにHTMLがどのように見えるかについて心配する必要はありません。

4
Borgar

DOMを変更しない場合は、インデックス付きの順序ですべて取得できます。

プロトタイプ 例)

myNodes = document.body.descendants()
alert(document.body.descendants()[1].innerHTML)

すべてのノードをループして、後で簡単に選択できる一意のclassNameを指定できます。

次の関数を使用して、DOM内の任意のノードの安定した一意の識別子を生成できます。

_function getUniqueKeyForNode (targetNode) {
    const pieces = ['doc'];
    let node = targetNode;

    while (node && node.parentNode) {
        pieces.Push(Array.prototype.indexOf.call(node.parentNode.childNodes, node));
        node = node.parentNode
    }

    return pieces.reverse().join('/');
}
_

これにより、次のような構造の_doc/0_、_doc/0/0_、_doc/0/1_、_doc/0/1/0_、_doc/0/1/1_などの識別子が作成されます。

_<div>
    <div />
    <div>
        <div />
        <div />
    </div>
</div>
_

また、いくつかの最適化と変更が可能です。たとえば、次のとおりです。

  • whileループでは、breakは、そのnodeが一意であることがわかっている属性を持っている場合、たとえば_@id_

  • reverse()piecesではありません。現在、IDの生成元であるDOM構造のように見えるだけです。

  • ドキュメントノードの識別子が必要ない場合は、最初のpiecedocを含めないでください

  • なんらかの方法でノードに識別子を保存し、その値を子ノードに再利用して、ツリー全体を再びたどる必要がないようにします。

  • これらの識別子をXMLに書き戻す場合は、書き込む属性が制限されている場合は、別の連結文字を使用してください。

2
wybe

Id属性を計算値に設定できます。これを行うことができる関数がプロトタイプライブラリにあります。

http://www.prototypejs.org/api/element/identify

私のお気に入りのJavaScriptライブラリはjQueryです。残念ながら、jQueryには、identifyなどの関数はありません。ただし、id属性を独自に生成した値に設定することはできます。

http://docs.jquery.com/Attributes/attr#keyfn

以下は、ページ内の位置に基づいてdivのIDを設定するjQueryドキュメントからの部分的なスニペットです。

  $(document).ready(function(){

    $("div").attr("id", function (arr) {
          return "div-id" + arr;
        });
  });
2
Michael

一意の識別子を生成するためにpguid(ページ固有の識別子)を使用することもできます。

 pguid = b9j.pguid.next() // A unique id (suitable for a DOM element)
                          // is generated
                          // Something like "b9j-pguid-20a9ff-0"
 ...
 pguid = b9j.pguid.next() // Another unique one... "b9j-pguid-20a9ff-1"

 // Build a custom generator
 var sequence = new b9j.pguid.Sequence({ namespace: "frobozz" })
 pguid = sequence.next() "frobozz-c861e1-0"

http://appengine.bravo9.com/b9j/documentation/pguid.html

1
Robert Krimen

要素のマウスや位置のプロパティを使用して、一意のIDを生成します。

1
Paul Tierney

私はこれと同じような問題を解決したと思います。ただし、ブラウザーのDOM環境でjQueryを使用しています。

var objA = $( "selector to some dom element"); var objB = $( "selector to some other dom element");

if(objA [0] === objB [0]){//素晴らしい! 2つのオブジェクトがまったく同じdomノードを指す}

0
Tom Carnell

JavaScriptでは、カスタムIDフィールドをノードにアタッチできます

_if(node.id) {
  node.myId = node.id;
} else {
  node.myId = createId();
}

// store myId
_

少しハックですが、すべてのノードに使用できるIDを提供します。もちろん、document.getElementById()はそれに注意を払いません。

0
sblundy

OK、DOM要素に自動的に関連付けられたIDはありません。 DOMは、主要な情報である要素の階層構造を持っています。この観点から、jQueryまたはjQLiteでデータをDOM要素に関連付けるを行うことができます。カスタムデータを要素にバインドする必要がある場合、いくつかの問題を解決できます。

0
kisp