2010-01-11 12 views
17

Aunque nunca he oído hablar de esto, ¿es posible recuperar un nodo del DOM utilizando JS y luego averiguar en qué línea del archivo se produjo ese nodo?Averiguar en qué número de línea se encuentra un elemento en dom en Javascript?

estoy abierto a cualquier cosa, navegadores alternativos plugins/add-ons, etc ... No tiene por qué ser multi-navegador por la opinión.

quiero suponer que esto sería posible alguna manera teniendo en cuenta que algunos depuradores JS son capaces de encontrar el número de línea dentro de una etiqueta de script, pero no estoy del todo seguro.

+0

he añadido enlaces en mi [respuesta] (http://stackoverflow.com/questions/2044642/finding-out-what-line-number-an-element-in-the-dom-occurs-on -in-javascript/2046930 # 2046930) a una versión de complemento de mi código que pueda interesarle. –

Respuesta

7

Ok, perdóname por lo grande que es esto. Pensé que esta era una pregunta muy interesante, pero mientras jugaba con ella, rápidamente me di cuenta de que innerHTML y su tipo son bastante poco confiables, manteniendo el espacio en blanco, los comentarios, etc. Con eso en mente, me volví a retirar una copia completa del fuente para poder estar absolutamente seguro de que obtuve la fuente completa. Luego usé jquery y unas pocas expresiones regulares (relativamente pequeñas) para encontrar la ubicación de cada nodo. Parece funcionar bien, aunque estoy seguro de que me he perdido algunos casos extremos. Y, sí, sí, expresiones regulares y dos problemas, bla, bla, bla.

Editar: Como un ejercicio de construcción de plugins jQuery, he modificado mi código para funcionar razonablemente bien como un independiente plugin con un example similar al HTML que se encuentra debajo (que voy a salir de aquí para la posteridad). He intentado hacer que el código sea un poco más robusto (como ahora manejar etiquetas dentro de cadenas entrecomilladas, como onclick), pero el error más grande que queda es que no puede dar cuenta de ninguna modificación a la página, como agregar elementos. Probablemente necesitaría usar un iframe en lugar de una llamada ajax para manejar ese caso.

<html> 
    <head id="node0"> 
    <!-- first comment --> 
     <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script> 
     <style id="node1"> 
/*   div { border: 1px solid black; } */ 
      pre { border: 1px solid black; } 
     </style> 
    <!-- second comment --> 
     <script> 
      $(function() { 

       // fetch and display source 
       var source; 
       $.ajax({ 
        url: location.href, 
        type: 'get', 
        dataType: 'text', 
        success: function(data) { 
         source = data; 


         var lines = data.split(/\r?\n/); 
         var html = $.map(lines, function(line, i) { 
          return ['<span id="line_number_', i, '"><strong>', i, ':</strong> ', line.replace(/</g, '&lt;').replace(/>/g, '&gt;'), '</span>'].join(''); 
         }).join('\n'); 

         // now sanitize the raw html so you don't get false hits in code or comments 
         var inside = false; 
         var tag = ''; 
         var closing = { 
          xmp: '<\\/\\s*xmp\\s*>', 
          script: '<\\/\\s*script\\s*>', 
          '!--': '-->' 
         }; 
         var clean_source = $.map(lines, function(line) { 
          if (inside && line.match(closing[tag])) { 
           var re = new RegExp('.*(' + closing[tag] + ')', 'i'); 
           line = line.replace(re, "$1"); 
           inside = false; 
          } else if (inside) { 
           line = ''; 
          } 

          if (line.match(/<(script|!--)/)) { 
           tag = RegExp.$1; 
           line = line.replace(/<(script|xmp|!--)[^>]*.*(<(\/(script|xmp)|--)?>)/i, "<$1>$2"); 
           var re = new RegExp(closing[tag], 'i'); 
           inside = ! (re).test(line); 
          } 
          return line; 
         }); 

         // nodes we're looking for 
         var nodes = $.map([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(num) { return $('#node' + num) }); 

         // now find each desired node in both the DOM and the source 
         var line_numbers = $.map(nodes, function(node) { 
          var tag = node.attr('tagName'); 
          var tags = $(tag); 
          var index = tags.index(node) + 1; 

          var count = 0; 
          for (var i = 0; i < clean_source.length; i++) { 
           var re = new RegExp('<' + tag, 'gi'); 
           var matches = clean_source[i].match(re); 
           if (matches && matches.length) { 
            count += matches.length; 
            if (count >= index) { 
             console.debug(node, tag, index, count, i); 
             return i; 
            } 
           } 
          } 


          return count; 
         }); 

         // saved till end to avoid affecting source html 
         $('#source_pretty').html(html); 
         $('#source_raw').text(source); 
         $('#source_clean').text(clean_source.join('\n')); 

         $.each(line_numbers, function() { $('#line_number_' + this).css('background-color', 'orange'); }); 
        }, 
       }); 

       var false_matches = [ 
        "<div>", 
        "<div>", 
        "</div>", 
        "</div>" 
       ].join(''); 

      }); 
     </script> 
    </head> 
    <!-- third comment --> 
    <body id="node2"> 
     <div> 
      <pre id="source_pretty"> 
      </pre> 
      <pre id="source_raw"> 
      </pre> 
      <pre id="source_clean"> 
      </pre> 
     </div> 

     <div id="node3"> 
      <xmp> 
       <code> 
       // <xmp> is deprecated, you should put it in <code> instead 
       </code> 
      </xmp> 
     </div> 

    <!-- fourth comment --> 
<div><div><div><div><div><div><span><div id="node4"><span><span><b><em> 
<i><strong><pre></pre></strong></i><div><div id="node5"><div></div></div></div></em> 
</b></span><span><span id="node6"></span></span></span></div></span></div></div></div></div></div></div> 


     <div> 
      <div> 
       <div id="node7"> 
        <div> 
         <div> 
          <div id="node8"> 
           <span> 
    <!-- fifth comment --> 
            <div> 
             <span> 
              <span> 
               <b> 
                <em id="node9"> 
                 <i> 
                  <strong> 
                   <pre> 
                   </pre> 
                  </strong> 
                 </i> 
                 <div> 
                  <div> 
                   <div> 
                   </div> 
                  </div> 
                 </div> 
                </em> 
               </b> 
              </span> 
              <span> 
               <span id="node10"> 
               </span> 
              </span> 
             </span> 
            </div> 
           </span> 
          </div> 
         </div> 
        </div> 
       </div> 
      </div> 
     </div> 
    </body> 
</html> 
+0

Maybe usted y yo deberíamos seguir probando este código y ajustarlo hasta que lo hagamos bien. ¿En qué navegador probaste esto? – leeand00

+0

(ya que no pedí una solución de navegador cruzado sino algo que funcionó). – leeand00

+0

+1 por esfuerzo! :) – leeand00

1

Esto se puede hacer. Empezar por conseguir el nodo más alto en el documento de la siguiente manera:

var htmlNode = document.getElementsByTagName('html')[0]; 
var node = htmlNode; 
while (node.previousSibling !== null) { 
    node = node.previousSibling; 
} 
var firstNode = node; 

(este código se probó y se recupera tanto el nodo de tipo de documento, así como los comentarios anteriores el nodo html)

A continuación, recorrer todos los nodos (ambos hermanos y niños). En IE, solo verá los elementos y comentarios (no los nodos de texto), por lo que será mejor utilizar FF o Chrome o algo así (usted dijo que no tendría que ser un navegador cruzado).

Al llegar a cada text node, analizarlo en busca de retornos de carro.

+4

¿Qué sucede si hay una nueva línea dentro de una etiqueta de elemento, como entre atributos? ¿No te perderás esas nuevas líneas? – thejoshwolfe

1

Algo como esto?

var wholeDocument = document.getElementsByTagName('html')[0] 
var findNode = document.getElementById('whatever') 
var documentUpToFindNode = wholeDocument.substr(0, wholeDocument.indexOf(findNode.outerHTML)) 
var nlsUpToFindNode = documentUpToFindNode.match(/\n/g).length 
+2

'findNode.outerHTML' no es necesariamente exclusivas de este elemento ... – James

+0

valor + 1 + x ... o si la página dictada en modo estándar con un DOCTYPE y o comentarios adicionales antes de la etiqueta HTML abierta. – scunliffe

+1

'outerHTML' es una propiedad no estándar, solo de IE, por lo que no funcionará en otros navegadores, y como se señala por @ J-P, no se garantiza que sea exclusivo del elemento. –

-2

Usted podría intentar: -

- start at the 'whatever' node, 
- traverse to each previous node back to the doc begining while concatenating the html of each node, 
- then count the new lines in your collected HTML. 

enviar el código una vez que Nut fuera coz eso es una buena pregunta :)

+0

Esto asume exactamente un nodo HTML por línea, no estoy seguro de que esto funcione –

+0

No, no es así. Sin embargo, es más fácil de lo que dije originalmente: simplemente grabe el cuerpo html como una cadena, divídalo en el elemento que desea el número de línea usando la ID de elem, luego cuente los caracteres de nueva línea en el 1er miembro de la división;) – ekerner

Cuestiones relacionadas