0

私は単純な天気予報アプリケーションを書いていますが、for ループに悩まされています。アイデアは、場所の小さな配列の天気を取得し、それぞれの予測を独自の div にロードすることです。これまでのコードは次のとおりです。

$(function () {
    var z = ["location ID1", "location ID2", etc.];
    var n = ["location name1", "location name2", etc];
    for (j = 0; j<z.length; j++) {
        weatherQuery = 'SELECT * FROM rss WHERE url="http://xml.weather.yahoo.com/forecastrss/' + z[j] + '_c.xml"';
        $('#weatherData').append($('<div/>', {id: 'c' + j}));
        $('#c' + j + '').append('' + n[j] + '');
        var weatherUrl = 'http://query.yahooapis.com/v1/public/yql?q=' + encodeURIComponent(weatherQuery) + '&format=json';
        $.getJSON(weatherUrl + '&callback=?', function (data) {
            var weatherForecasts = data.query.results.item.forecast;
            for (i=0; i<weatherForecasts.length; i++) {
                var code = weatherForecasts[i].code;
                $('#c' + j + '').append('<table style = border:none><tr><td class = weather-date>' + weatherForecasts[i].day + '</td><td class = weather-icon style = background-position:-' + (61 * code ) + 'px 0px></td><td class = weather-min>' + weatherForecasts[i].low + '°</td><td class = weather-max>' + weatherForecasts[i].high + '°</td></tr></table>');              
            } 
        });
    }
});

Firebug からわかる限り、最初の部分は機能します。正しい気象データが取得され、新しい div が親 div に作成されています。正しい場所名も新しい div のそれぞれに追加されています。それが落ちたのは、各divに天気データを追加することです。2 番目 (最後) の追加ステートメントで子 div を識別して、各 div の気象データ解析ループを 1 回反復する正しい方法は何ですか? ありがとう。

4

1 に答える 1

1
for (j = 0; j<z.length; j++) {
    $.getJSON(..., function (data) {
        $('#c' + j + '').append(...

ループはz.length時間を実行し、 z.lengthAJAX 要求を開始し、ループjを終了するのと同じで完了しz.lengthます。

その後、かなり後で (AJAX の「A」は「非同期」を意味するため)、要求が完了し、各コールバック関数が呼び出されます。j結果を追加する div を選択するために使用します。しかし、ループが終了したときの状態のjままです。z.length変数に対するクロージャーを使用して関数を作成しても、関数が作成された時点から変数の値が記憶されているわけではありません。各関数には同じ への参照がありjます。

(実際には、ループ変数ji宣言されていないため、クロージャーはまったくありませんvar。したがって、それらは偶発的なグローバル変数です。)

関数を作成するときに、その変数を使用して別の関数にラップすることにより (またはfunction.bindECMAScript 5 で使用することにより)、ローカル変数の値を記憶できます。これを行う簡単な方法は、次の代わりにコールバック関数ベースのループを使用することですfor

var places = [
    {id: 'location1', name: 'Location One'},
    ...
];

$.each(places, function(place_i, place) {
    var yql = 'SELECT * FROM rss WHERE url="http://xml.weather.yahoo.com/forecastrss/' + encodeURIComponent(place.id) + '_c.xml"';
    var div = $('<div/>').attr('id', 'c'+place_i)).text(place.name);
    $('#weatherData').append(div);

    $.ajax({
        url: 'http://query.yahooapis.com/v1/public/yql',
        data: {q: yql, format: 'json'},
        dataType: 'jsonp'
    ).done(function(json) {
        var forecasts = data.query.results.item.forecast;
        var table = $('<table style="border: none">'); // prefer class/stylesheet
        $.each(forecasts, function(forecast_i, forecast) {
            table.append($('<tr>').append(
                $('<td class="weather-date">').text(forecast.day)
            ).append(
                $('<td class="weather-icon">').css('background-position', forecast.code*-61 + 'px 0')
            ).append(
                $('<td class="weather-min">').text(forecast.low+'°')
            ).append(
                $('<td class="weather-max">').text(forecast.high+'°')
            ));
        });
        div.html(table);
    });
});

ノート:

  • jQuery のデータ パラメータの自動変換を使用encodeURIComponentして、JSON URL で手動で呼び出す必要がないようにします。

  • 文字列変数から HTML コンテンツをくっつけないようにします。HTML 特殊文字では失敗し、セキュリティの問題 (XSS) が発生する可能性があります。テキスト コンテンツの設定には、 jQuery を使用しますtext()

  • idこのように JS 変数で要素への参照を保持している場合は、おそらく要素に を与える必要はまったくありません。

于 2013-10-03T23:38:44.060 に答える