No estoy seguro de cuán flexible o cuántos casos necesita para cubrir, pero para su ejemplo, si el texto siempre viene antes de las primeras etiquetas HTML, ¿por qué no simplemente dividir el html interno en la primera etiqueta y tomar la primera:
$('#listItem').html().split('<span')[0];
y si lo necesitas más ancho tal vez solo
$('#listItem').html().split('<')[0];
y si necesita el texto entre dos marcadores, como después de una cosa pero antes de otra, puede hacer algo como (no probado) y usar declaraciones if para que sea lo suficientemente flexible como para tener un marcador de inicio o fin o ambos, evitando al mismo tiempo los errores de referencia nula :
var startMarker = '';// put any starting marker here
var endMarker = '<';// put the end marker here
var myText = String( $('#listItem').html() );
// if the start marker is found, take the string after it
myText = myText.split(startMarker)[1];
// if the end marker is found, take the string before it
myText = myText.split(endMarker)[0];
console.log(myText); // output text between the first occurrence of the markers, assuming both markers exist. If they don't this will throw an error, so some if statements to check params is probably in order...
Generalmente hago funciones de utilidad para cosas útiles como esta, las hago libres de errores y luego confío en ellas con frecuencia una vez que son sólidas, en lugar de reescribir siempre este tipo de manipulación de cadenas y arriesgar referencias nulas, etc. De esa manera, puede reutilizar la función en muchos proyectos y nunca más tendrá que perder el tiempo depurando por qué una referencia de cadena tiene un error de referencia indefinido. Puede que no sea el código de 1 línea más corto de la historia, pero después de tener la función de utilidad, es una línea a partir de ese momento. Tenga en cuenta que la mayor parte del código solo maneja los parámetros que existen o no para evitar errores :)
Por ejemplo:
/**
* Get the text between two string markers.
**/
function textBetween(__string,__startMark,__endMark){
var hasText = typeof __string !== 'undefined' && __string.length > 0;
if(!hasText) return __string;
var myText = String( __string );
var hasStartMarker = typeof __startMark !== 'undefined' && __startMark.length > 0 && __string.indexOf(__startMark)>=0;
var hasEndMarker = typeof __endMark !== 'undefined' && __endMark.length > 0 && __string.indexOf(__endMark) > 0;
if( hasStartMarker ) myText = myText.split(__startMark)[1];
if( hasEndMarker ) myText = myText.split(__endMark)[0];
return myText;
}
// now with 1 line from now on, and no jquery needed really, but to use your example:
var textWithNoHTML = textBetween( $('#listItem').html(), '', '<'); // should return text before first child HTML tag if the text is on page (use document ready etc)