views:

40

answers:

2

Coming to jQuery from a functional background, I am fond (perhaps unreasonably so) of elegant chains of functions. I often find myself dealing with arrays of elements, such as those that may result from $.map, and my ability to manipulate these arrays in the DOM seems quite limited. Here's some sample code that runs through the results of a Google search, rendering the result titles:

var newResultsDiv = $('<div id="results" />');
$.each(searcher.results, function() {
  newResultsDiv.append('<p>' + this.title);
});
$("#searchresults").append(newResultsDiv);

I find this excessively verbose. Ideally, I would do something along these lines instead:

$.map(searcher.results, function(elem) {
  return $('<p>' + elem.title);
}).wrapAll('<div id="results" />').appendTo('#searchresults');

I've tried this out, along with several variants using different forms of append and wrap. They all appear to be incapable of handling the plain-old-Javascript array that jQuery.map spits out. They're designed to work with jQuery's own set collection. At least, that's my guess, as messing around with these functions in the Firebug console seems to confirm the problem.

I am hoping that someone here has the wisdom to show me an elegant way to do what I'm trying to do. Is there one?

A: 

Hm...not sure if this will work, but give it a try:

$("<div id='results'>" + $.map(searcher.results, function(elem) {
    return '<p>' + elem.title + '</p>';
}).join("") + "</div>").appendTo('#searchresults');

OK, just tested it here:

http://jsfiddle.net/xxxST/1/

And it seems to work. I should mention, however, that while this is perhaps not very "verbose" in terms of number of lines, it's a bit opaque with regard to its clarity. It's a jQuery function result, joined together to a string, inside a jQuery wrapper with an appendTo function running on it. I think it would be much more readable if you were to do something like this:

var resultString = $.map(searcher.results, function(elem) {
    return '<p>' + elem.title + '</p>';
}).join("");

$('#searchresults').append("<div id='results'>" + resultString + "</div>");

But that's just my opinion.

treeface
+2  A: 

Using the $.map method you presented, you could return the actual DOM element instead of a jQuery object. This is done by grabbing the [0] index item in the jQuery object. Then wrap the entire $.map with $().

This works because jQuery will accept an array of DOM elements.

Your <p> creation was a little off. I changed it to pass an object literal to set the text. Otherwise, you would need to concatenate the ending tag as well.

Finally, you would need to traverse up to the wrapper #results you created using .parent().

$($.map(searcher.results, function(elem) {
  return $('<p>',{text:elem.title})[0];
})).wrapAll('<div id="results" />').parent().appendTo('#searchresults');

EDIT: IF you don't mind the look of it, you could do this as well:

$('<div id="results" />').append(
    $.map(searcher.results, function(elem) {
        return $('<p>',{text:elem.title})[0];
    })).appendTo('#searchresults');
patrick dw
Excellent. I ended up using a variation of the second code sample, which I find cleaner. Because google search results are marked up (for example, the search text appears in bold), I had to replace the {text:elem.title} with string concatenation and a closing </p>, as you suggested.
Grynszpan
@Grynszpan - FYI, instead of `{text:elem.title}` you could do `{html:elem.title}` if there's markup. Either way will work, though.
patrick dw