Question

Je suis en train de faire tous les noeuds DOM qui sont dans un objet de plage, quelle est la meilleure façon de le faire?

var selection = window.getSelection(); //what the user has selected
var range = selection.getRangeAt(0); //the first range of the selection
var startNode = range.startContainer;
var endNode = range.endContainer;
var allNodes = /*insert magic*/;

J'ai pensé à un moyen pour les dernières heures et est venu avec ceci:

var getNextNode = function(node, skipChildren){
    //if there are child nodes and we didn't come from a child node
    if (node.firstChild && !skipChildren) {
        return node.firstChild;
    }
    if (!node.parentNode){
        return null;
    }
    return node.nextSibling 
        || getNextNode(node.parentNode, true);
};

var getNodesInRange = function(range){
    var startNode = range.startContainer.childNodes[range.startOffset]
            || range.startContainer;//it's a text node
    var endNode = range.endContainer.childNodes[range.endOffset]
            || range.endContainer;

    if (startNode == endNode && startNode.childNodes.length === 0) {
        return [startNode];
    };

    var nodes = [];
    do {
        nodes.push(startNode);
    }
    while ((startNode = getNextNode(startNode)) 
            && (startNode != endNode));
    return nodes;
};

Toutefois, lorsque le noeud final est le parent du nœud de départ, il retourne tout sur la page. Je suis sûr que je suis sur quelque chose évidente? Ou peut-être aller à ce sujet dans le mauvais sens tout à fait.

MDC / DOM / intervalle

Était-ce utile?

La solution

Le getNextNode sautera votre récursive si désiré nœud terminal est un nœud parent.

Effectuer le contrôle de rupture conditionnelle à l'intérieur de la place getNextNode:

var getNextNode = function(node, skipChildren, endNode){
  //if there are child nodes and we didn't come from a child node
  if (endNode == node) {
    return null;
  }
  if (node.firstChild && !skipChildren) {
    return node.firstChild;
  }
  if (!node.parentNode){
    return null;
  }
  return node.nextSibling 
         || getNextNode(node.parentNode, true, endNode); 
};

et while:

while (startNode = getNextNode(startNode, false , endNode));

Autres conseils

Voici une implémentation je suis venu avec pour résoudre ceci:

function getNextNode(node)
{
    if (node.firstChild)
        return node.firstChild;
    while (node)
    {
        if (node.nextSibling)
            return node.nextSibling;
        node = node.parentNode;
    }
}

function getNodesInRange(range)
{
    var start = range.startContainer;
    var end = range.endContainer;
    var commonAncestor = range.commonAncestorContainer;
    var nodes = [];
    var node;

    // walk parent nodes from start to common ancestor
    for (node = start.parentNode; node; node = node.parentNode)
    {
        nodes.push(node);
        if (node == commonAncestor)
            break;
    }
    nodes.reverse();

    // walk children and siblings from start until end is found
    for (node = start; node; node = getNextNode(node))
    {
        nodes.push(node);
        if (node == end)
            break;
    }

    return nodes;
}

ci-dessous le code résoudre votre problème

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>payam jabbari</title>
<script src="http://code.jquery.com/jquery-2.0.2.min.js" type="text/javascript"></script>
<script type="text/javascript">

$(document).ready(function(){
    var startNode = $('p.first').contents().get(0);
var endNode = $('span.second').contents().get(0);
var range = document.createRange();
range.setStart(startNode, 0);
range.setEnd(endNode, 5);
var selection = document.getSelection();
selection.addRange(range);
// below code return all nodes in selection range. this code work in all browser
var nodes = range.cloneContents().querySelectorAll("*");
for(var i=0;i<nodes.length;i++)
{
   alert(nodes[i].innerHTML);
}
});
</script>
</head>

<body>
<div>

<p class="first">Even a week ago, the idea of a Russian military intervention in Ukraine seemed far-fetched if not totally alarmist. But the arrival of Russian troops in Crimea over the weekend has shown that he is not averse to reckless adventures, even ones that offer little gain. In the coming days and weeks</p>

<ol>
    <li>China says military will respond to provocations.</li>
    <li >This Man Has Served 20 <span class="second"> Years—and May Die—in </span> Prison for Marijuana.</li>
    <li>At White House, Israel's Netanyahu pushes back against Obama diplomacy.</li>
</ol>
</div>
</body>
</html>

J'ai fait 2 corrections supplémentaires en fonction de la réponse de MikeB pour améliorer la précision des noeuds sélectionnés.

Je teste en particulier ce sur une sélection de toutes les opérations, autres que la sélection de plage fait en faisant glisser le curseur le long texte enjambant plusieurs éléments.

Dans Firefox, frappant tout sélectionner (CMD + A) retourne une plage où il est startContainer & endContainer est le contenteditable div, la différence est dans le startOffset et endOffset où il est respectivement l'indice du premier et le dernier nœud enfant.

Dans Chrome, frapper tout sélectionner (CMD + A) retourne une plage où il est startContainer est le premier nœud enfant du contenteditable div et endContainer est le dernier nœud enfant du contenteditable div.

Les modifications que j'ai travail supplémentaire autour des écarts entre les deux. Vous pouvez voir les commentaires dans le code pour des explications supplémentaires.

function getNextNode(node) {
    if (node.firstChild)
        return node.firstChild;

    while (node) {
        if (node.nextSibling) return node.nextSibling;
        node = node.parentNode;
    }
}

function getNodesInRange(range) {

    // MOD #1
    // When the startContainer/endContainer is an element, its
    // startOffset/endOffset basically points to the nth child node
    // where the range starts/ends.
    var start = range.startContainer.childNodes[range.startOffset] || range.startContainer;
    var end = range.endContainer.childNodes[range.endOffset] || range.endContainer;
    var commonAncestor = range.commonAncestorContainer;
    var nodes = [];
    var node;

    // walk parent nodes from start to common ancestor
    for (node = start.parentNode; node; node = node.parentNode)
    {
        nodes.push(node);
        if (node == commonAncestor)
            break;
    }
    nodes.reverse();

    // walk children and siblings from start until end is found
    for (node = start; node; node = getNextNode(node))
    {
        // MOD #2
        // getNextNode might go outside of the range
        // For a quick fix, I'm using jQuery's closest to determine
        // when it goes out of range and exit the loop.
        if (!$(node.parentNode).closest(commonAncestor)[0]) break;

        nodes.push(node);
        if (node == end)
            break;
    }

    return nodes;
};

Annon, excellent travail. J'ai modifié le original plus inclus les modifications de Stefan dans ce qui suit.

En outre, j'ai supprimé le recours à la plage, qui convertit la fonction en un algorithme générique pour marcher entre deux nœuds. De plus, je l'ai enveloppé tout en une seule fonction.

Réflexions sur d'autres solutions:

  • Pas intéressé en se fondant sur jquery
  • Utilisation cloneNode lève les résultats à un fragment, ce qui empêche de nombreuses opérations on pourrait vouloir effectuer pendant le filtrage.
  • Utilisation querySelectAll sur un fragment cloné est bancale parce que les noeuds de début ou de fin peuvent être dans un nœud d'emballage, d'où l'analyseur peut ne pas avoir la balise de fermeture?

Exemple:

<div>
    <p>A</p>
    <div>
        <p>B</p>
        <div>
            <p>C</p>
        </div>
    </div>
</div>

Supposons noeud départ est le point « A », et le noeud d'extrémité est le point « C » . Le fragment cloné résultant serait:

<p>A</p>
    <div>
        <p>B</p>
        <div>
            <p>C</p>

et nous manque des balises de fermeture? résultant de la structure DOM froussard?

Quoi qu'il en soit, voici la fonction, qui comprend une option de filtre, qui doit retourner VRAI ou FAUX pour inclure / exclure des résultats.

var getNodesBetween = function(startNode, endNode, includeStartAndEnd, filter){
    if (startNode == endNode && startNode.childNodes.length === 0) {
        return [startNode];
    };

    var getNextNode = function(node, finalNode, skipChildren){
        //if there are child nodes and we didn't come from a child node
        if (finalNode == node) {
            return null;
        }
        if (node.firstChild && !skipChildren) {
            return node.firstChild;
        }
        if (!node.parentNode){
            return null;
        }
        return node.nextSibling || getNextNode(node.parentNode, endNode, true);
    };

    var nodes = [];

    if(includeStartAndEnd){
        nodes.push(startNode);
    }

    while ((startNode = getNextNode(startNode, endNode)) && (startNode != endNode)){
        if(filter){
            if(filter(startNode)){
                nodes.push(startNode);
            }
        } else {
            nodes.push(startNode);
        }
    }

    if(includeStartAndEnd){
        nodes.push(endNode);
    }

    return nodes;
};

bob. la fonction ne retourne que le startNode et nœud terminal. les noeuds entre les deux ne sont pas poussés au tableau.

semble la boucle while renvoie NULL getNextNode () où ce bloc ne sera exécuté.

est fonction retour ici éventail de gammes de sous-

function getSafeRanges(range) {

var doc = document;

var commonAncestorContainer = range.commonAncestorContainer;
var startContainer = range.startContainer;
var endContainer = range.endContainer;
var startArray = new Array(0),
    startRange = new Array(0);
var endArray = new Array(0),
    endRange = new Array(0);
// @@@@@ If start container and end container is same
if (startContainer == endContainer) {
    return [range];
} else {
    for (var i = startContainer; i != commonAncestorContainer; i = i.parentNode) {
        startArray.push(i);
    }
    for (var i = endContainer; i != commonAncestorContainer; i = i.parentNode) {
        endArray.push(i);
    }
}
if (0 < startArray.length) {
    for (var i = 0; i < startArray.length; i++) {
        if (i) {
            var node = startArray[i - 1];
            while ((node = node.nextSibling) != null) {
                startRange = startRange.concat(getRangeOfChildNodes(node));
            }
        } else {
            var xs = doc.createRange();
            var s = startArray[i];
            var offset = range.startOffset;
            var ea = (startArray[i].nodeType == Node.TEXT_NODE) ? startArray[i] : startArray[i].lastChild;
            xs.setStart(s, offset);
            xs.setEndAfter(ea);
            startRange.push(xs);
        }
    }
}
if (0 < endArray.length) {
    for (var i = 0; i < endArray.length; i++) {
        if (i) {
            var node = endArray[i - 1];
            while ((node = node.previousSibling) != null) {
                endRange = endRange.concat(getRangeOfChildNodes(node));
            }
        } else {
            var xe = doc.createRange();
            var sb = (endArray[i].nodeType == Node.TEXT_NODE) ? endArray[i] : endArray[i].firstChild;
            var end = endArray[i];
            var offset = range.endOffset;
            xe.setStartBefore(sb);
            xe.setEnd(end, offset);
            endRange.unshift(xe);
        }
    }
}
var topStartNode = startArray[startArray.length - 1];
var topEndNode = endArray[endArray.length - 1];
var middleRange = getRangeOfMiddleElements(topStartNode, topEndNode);
startRange = startRange.concat(middleRange);
response = startRange.concat(endRange);
return response;

}

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top