¿Cómo obtengo solo el text visible con JQuery (o Javascript)?

Tengo un website que convierte kanji japonés en romaji (letras romanas) :

y el resultado muestra y oculta con CSS lo que el usuario necesita ver según sus criterios de input. Por ejemplo:

 <div id="output"> <span class="roman">watashi</span> <span class="english">I</span> </div> 

La interfaz permite al usuario cambiar entre watashi y I dependiendo de lo que quieran ver.

El CSS oculta uno u otro usando jQuery y un button para alternar. (el mecanismo de ocultamiento implica simplemente agregar una class al body y dejar que CSS haga su trabajo).

El problema es que cuando los usuarios copyn / pegan el text en Word lo copy todo. Así que decidí usar un sistema para copyr y pegar el text usando JavaScript y jQuery , pero el problema se repite:

 $('#output').text() 

watashi I incluso si I invisible en la página en vez de watashi . ¿Hay alguna manera de get solo el text visible?

Use el selector: visible de Jquery

En tu caso, creo que quieres hacer:

 $('#output').children(":visible").text() 

las otras soluciones no me dieron lo que necesitaba.

Respuesta corta

mi respuesta es :

 $('#output *:not(:has(*)):visible').text() 

Plunkr

TL; DR

El problema con la solución de marcgg

No debe preguntar el text de todos los elementos debajo de algún elemento raíz.

¿por qué? – repetirá la salida e ignorará la bandera oculta

veamos un ejemplo simple

 <div id="output" class="my-root"> <div class="some-div"> <span class="first" style="display:none"> hidden text </span> <span class="second" > visible text </span> </div> <div> 

ahora si hago $('#output').children(":visible").text()

.second .some-div y .second … cuando de hecho. .second .some-div no me concierne …

cuando pido text() sobre esos elementos, .some-div devolverá el text oculto también ..

por lo que técnicamente la solución de marcgg es incorrecta en mi humilde opinión …

El motivo de mi respuesta

Ahora, para responder correctamente a la pregunta, tenemos que hacer una suposition. Uno que, para mí, parece bastante razonable.

La suposition es que el text solo aparece en elementos de hoja.

Entonces no veremos algo como esto:

 <div id="output" class="my-root"> <div class="some-div"> <span class="first" style="display:none"> hidden text </span> <span class="second" > visible text </span> </div> some text here.. <div> 

¿Por qué esta suposition parece razonable para mí? dos razones:

  • Porque es difícil mantener una página que esté construida de esta manera, y con el time las personas con experiencia aprenden eso y lo evitan.
  • Es fácil convertir su html a dicha estructura. simplemente envuelva el text de los padres con tramos. Entonces, incluso si esta suposition no existe en este momento, es fácil llegar allí.

Con esa suposition, lo que quiere hacer es solicitar todos los elementos de hoja (elementos sin elementos secundarios), filtrar lo visible y solicitar su text.

 $('#output *:not(:has(*)):visible').text() 

Esto debería generar el resultado correcto.

¿Debo tener text fuera del elemento de la hoja?

los comentarios sugieren que a veces tienes que tener text fuera del elemento de la hoja

 <div> This is some <strong style="display:none"> text </strong> </div> 

Como puede ver, tiene <strong> como hoja y es común tener text fuera de este como en este ejemplo.

Podrías darle la vuelta con la solución que sugiero arriba … pero ¿y si no puedes?

Puedes clonar el dom y luego eliminar todos los elementos ocultos. El problema aquí es que para que el selector :visible o :hidden selectores :hidden funcionen, debo tener el elemento dom en el documento (lo que significa realmente visible para el usuario). Y entonces, este método viene con algunos efectos secundarios, así que ten cuidado.

Aquí hay un ejemplo

para este html

  <div id="output" class="my-root"> <span> some text <strong style="display:none">here.. </strong> </span> </div> 

Este javascript funciona

 $(function(){ var outputClone = $('#output').clone(); $('#output :hidden').remove(); console.log($('#output').text()); // only visible text $('#output').replaceWith(outputClone); console.log($('#output').text()); // show original state achieved. }) 

ver plunker aquí

como se mencionó, los efectos secundarios pueden aparecer como un parpadeo momentáneo o algún script de initialization que debería ejecutarse … algunos pueden evitarse con un pensamiento original (div con tamaño 1px / 1px para contener el clon junto con el contenido original) dependiendo de su escenario.

Guy tiene la respuesta correcta.

Sin embargo, estaba lidiando con un object "este", por lo que para que funcione su respuesta necesita usar la siguiente syntax …

 $('*:not(:has(*)):visible', this).text() 
 var lookup = function(element, text) { //DFS Recursive way of finding text on each level //Visible only works on elements that take up space(ie not fixed position elements) var results = element.children(':visible'); //Look at the text at each level with the children removed var newText = ''; results.each(function(index, value) { newText += $(value).clone() .children() .remove() .end() .text(); }); var moreResultText = ''; results.each(function(index, value) { moreResultText += lookup($(value), text); }) if (results.length > 0) { return text + newText + moreResultText; } else { return text; } }; lookup($('#output'), '')); 

La mayoría de las otras funciones se desmoronan cuando se ejecutan en grandes secciones de una página, esta debería ser una manera más precisa de determinar qué se muestra realmente al usuario, sin corromper la página y sin devolver el text que no sea visible para el usuario.

Tenga cuidado, por supuesto, esto no conserva ningún sentido de formateo, y el espaciado de la salida puede no ser correcto entre los elementos. Además, probablemente no ordere correctamente el text devuelto, en estos aspectos sus usos serán limitados. Otra consideración es que la definición real de visible es un poco difícil de definir, pero para este ejemplo acepto que "visible" funciona para la mayoría de los casos comunes.

Lo uso para verificar si una página contiene text visible (simplemente ejecútelo en el elemento del cuerpo), pero probablemente también funcione para este ejemplo.

En lugar de ocultar un tramo, elimine el elemento tramo y conserve una reference al mismo. Cuando el usuario click el button de alternar, elimine el otro e inserte el que tenía referencedo. El usuario ya no podrá seleccionar algo que ya no esté en DOM.

Pruebe esto en browseres modernos (aquí 'elemento' es un object DOM no JQuery):

 function getVisibleText(element) { window.getSelection().removeAllRanges(); var range = document.createRange(); range.selectNode(element); window.getSelection().addRange(range); var visibleText = window.getSelection().toString().trim(); window.getSelection().removeAllRanges(); return visibleText; } 

entonces:

 getVisibleText(document.getElementById('output'));