2017-06-23 30 views
0

我有下面的代碼,它顯示並隱藏使用基本選項鍊父/子關係的選擇菜單。一旦有變化,我需要調用一個函數。如何在多個更改事件中使用jQuery承諾

目前我正在按照下面的說明進行操作,每觸發一個事件<select>,然後調用該函數。在現實生活中,這是一個阿賈克斯呼籲,但這與問題無關。

我下面的代碼工程。我想要解決的問題是,如何在不依賴更改事件的情況下完成此操作,因爲它會不必要地多次觸發該功能。我只想簡單地在$.each()循環的末尾放置一個函數調用,當父對象被更改時,但由於涉及動畫,它將失敗,因爲有問題的元素有時仍可見/隱藏。我通過在每個slideUp()slideDown()之間放置一個回調來解決這個問題,但這是我最終觸發事件多次的確切原因。

我有我要找的答案用了jQuery promise()其中規定一個強烈的感覺:

的.promise()方法返回一個動態生成的承諾,解決一旦某種類型綁定的所有行動收集,排隊或不,已經結束。

這正是我想要做的,但我完全被promise()搞糊塗了,它是正確的用法。我明白了,但我可以利用一些幫助理解如何將所有這些更改事件排入一個promise(),然後在全部完成時調用我的函數。

$('select[data-parent="true"]').change(function() { 
 
    var parentValue = +this.value; 
 
    var parentId = $(this).data('parent_id'); 
 
    $('div[data-parent_id="' + parentId + '"]').each(function() { 
 
    var parentValues = $(this).data('parent_values'); 
 
    if (parentValues.indexOf(parentValue) !== -1) { 
 
     $(this).slideDown(1000, function() { 
 
     $(this).find('select').change(); 
 
     }); 
 
    } else { 
 
     $(this).slideUp(1000, function() { 
 
     $(this).find('select').change(); 
 
     }); 
 
    } 
 
    }); 
 
}); 
 
$('select').on('change', function() { 
 
    var results = ''; 
 
    $('div.option').each(function() { 
 
    results += $(this).find('select').prop('name') + ' IS VISIBLE: ' + +$(this).is(':visible') + '<br>'; 
 
    }); 
 
    $('#results').html(results); 
 
});
div { 
 
    padding: 5px 10px; 
 
} 
 

 
div.child { 
 
    display: none 
 
} 
 

 
select { 
 
    width: 200px; 
 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> 
 
<div class="option"> 
 
    <select name="container" data-parent="true" data-parent_id="10"> 
 
    <option value="">--- Select a container---</option> 
 
\t \t <option value="1">Box</option> 
 
\t \t <option value="2">Bag</option> 
 
\t \t <option value="3">Pallet</option> 
 
\t </select> 
 
</div> 
 
<div class="child option" data-parent_id="10" data-parent_values="[1]"> 
 
    <select name="box color"> 
 
\t \t <option value="100">Red</option> 
 
\t \t <option value="200">Blue</option> 
 
\t </select> 
 
</div> 
 
<div class="child option" data-parent_id="10" data-parent_values="[2]"> 
 
    <select name="bag color"> 
 
\t \t <option value="300">Green</option> 
 
\t \t <option value="400">Yellow</option> 
 
\t </select> 
 
</div> 
 
<div class="child option" data-parent_id="10" data-parent_values="[3]"> 
 
    <select name="pallet color"> 
 
\t \t <option value="500">Black</option> 
 
\t \t <option value="600">Yellow</option> 
 
\t </select> 
 
</div> 
 
<div id="results"> 
 
</div>

回答

1

在jQuery動畫可以返回承諾.promise()和使用.map代替.each - 該代碼可以是簡單的:

$('select[data-parent="true"]').change(function() { 
 
    var parentValue = +this.value; 
 
    var parentId = $(this).data('parent_id'); 
 
    $.when.apply($, $('div[data-parent_id="' + parentId + '"]').map(function(i, v) { 
 
     var parentValues = $(this).data('parent_values'); 
 
     if (parentValues.indexOf(parentValue) !== -1) { 
 
      return $(this).slideDown(1000).promise(); 
 
     } else { 
 
      return $(this).slideUp(1000).promise(); 
 
     } 
 
    })).done(function() { 
 
     var results = ''; 
 
     $('div.option').each(function() { 
 
      results += $(this).find('select').prop('name') + ' IS VISIBLE: ' + +$(this).is(':visible') + '<br>'; 
 
     }); 
 
     $('#results').html(results); 
 
    }); 
 
});
div { 
 
    padding: 5px 10px; 
 
} 
 

 
div.child { 
 
    display: none 
 
} 
 

 
select { 
 
    width: 200px; 
 
    margin: 4px; 
 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> 
 
<div class="option"> 
 
    <select name="container" data-parent="true" data-parent_id="10"> 
 
    <option value="">--- Select a container---</option> 
 
\t \t <option value="1">Box</option> 
 
\t \t <option value="2">Bag</option> 
 
\t \t <option value="3">Pallet</option> 
 
\t </select> 
 
</div> 
 
<div class="child option" data-parent_id="10" data-parent_values="[1]"> 
 
    <select name="box color"> 
 
\t \t <option value="100">Red</option> 
 
\t \t <option value="200">Blue</option> 
 
\t </select> 
 
</div> 
 
<div class="child option" data-parent_id="10" data-parent_values="[2]"> 
 
    <select name="bag color"> 
 
\t \t <option value="300">Green</option> 
 
\t \t <option value="400">Yellow</option> 
 
\t </select> 
 
</div> 
 
<div class="child option" data-parent_id="10" data-parent_values="[3]"> 
 
    <select name="pallet color"> 
 
\t \t <option value="500">Black</option> 
 
\t \t <option value="600">Yellow</option> 
 
\t </select> 
 
</div> 
 
<div id="results"> 
 
</div>

+0

謝謝 - 我一直是'map()'的粉絲,它是合適的,這比我想出的更優雅和直觀我。 – billynoah

+0

我很驚訝關於這個話題的問答在SO上並不常見..我找到了一些好的線索,但沒有像我期望的那麼多,因爲對此的需求可能始終存在。我認爲很多人只是滿足於它的作品,並不介意20個事件在他們只需要一個事件時就被觸發。 – billynoah

0

實驗已經產生了以下解決方案的一個小時。創建一個$.Deferred()對象的數組,並使用$.when來解決它們。棘手的部分來自this answer,它解釋瞭如何將數組傳遞給$.when。希望這可以幫助別人使用jQuery掙扎遞延對象,好像我是:

$('select[data-parent="true"]').change(function() { 
 
    var deferred = []; 
 
    var parentValue = +this.value; 
 
    var parentId = $(this).data('parent_id'); 
 
    $('div[data-parent_id="' + parentId + '"]').each(function(i, v) { 
 
    deferred[i] = $.Deferred(); 
 
    var parentValues = $(this).data('parent_values'); 
 
    if (parentValues.indexOf(parentValue) !== -1) { 
 
     $(this).slideDown(1000, function() { 
 
     deferred[i].resolve(); 
 
     }); 
 
    } else { 
 
     $(this).slideUp(1000, function() { 
 
     deferred[i].resolve(); 
 
     }); 
 
    } 
 
    }); 
 
    $.when.apply($, deferred).done(function() { 
 
    var results = ''; 
 
    $('div.option').each(function() { 
 
     results += $(this).find('select').prop('name') + ' IS VISIBLE: ' + +$(this).is(':visible') + '<br>'; 
 
    }); 
 
    $('#results').html(results); 
 
    }); 
 
});
div { 
 
    padding: 5px 10px; 
 
} 
 

 
div.child { 
 
    display: none 
 
} 
 

 
select { 
 
    width: 200px; 
 
    margin: 4px; 
 
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> 
 
<div class="option"> 
 
    <select name="container" data-parent="true" data-parent_id="10"> 
 
    <option value="">--- Select a container---</option> 
 
\t \t <option value="1">Box</option> 
 
\t \t <option value="2">Bag</option> 
 
\t \t <option value="3">Pallet</option> 
 
\t </select> 
 
</div> 
 
<div class="child option" data-parent_id="10" data-parent_values="[1]"> 
 
    <select name="box color"> 
 
\t \t <option value="100">Red</option> 
 
\t \t <option value="200">Blue</option> 
 
\t </select> 
 
</div> 
 
<div class="child option" data-parent_id="10" data-parent_values="[2]"> 
 
    <select name="bag color"> 
 
\t \t <option value="300">Green</option> 
 
\t \t <option value="400">Yellow</option> 
 
\t </select> 
 
</div> 
 
<div class="child option" data-parent_id="10" data-parent_values="[3]"> 
 
    <select name="pallet color"> 
 
\t \t <option value="500">Black</option> 
 
\t \t <option value="600">Yellow</option> 
 
\t </select> 
 
</div> 
 
<div id="results"> 
 
</div>

+1

當然,知道jQuery動畫可以「本地」返回承諾 - 並使用.map代替.each - 代碼可以進一步簡化爲https://jsfiddle.net/0ruud9xL/ –

+0

那麼這就是最酷的事情我一整天都見過。你應該把它作爲另一個答案,我很樂意將它標記爲已接受。謝謝! – billynoah

+0

答案補充 - 雖然我沒有測試它 - 從頭開始​​測試它現在 –