I have an images and some text that is being generated by Knockout.js. The text shows up immediately while the images takes longer to load.
I want to have a class on the container that houses each image/text pair and when the image finishes loading then toggle that class and have the image/text pair fade in at the same time.
I know there is an afterRender callback, I'm having difficulty writing the function that gets called to target the element and manipulate the class.
http://jsfiddle.net/ClydeGrey/mfjv5j2c/4/
function SomeModel() {
this.Firstname = ko.observable();
this.Lastname = ko.observable();
this.fadeIn = ko.observable();
}
function SomeViewModel() {
var self = this;
this.ArrayOfModels = ko.mapping.fromJS([]);
this.dataArray = new Array;
this.GetModelsByAjax = function() {
$.ajax({
type: 'POST',
url: '/echo/json/',
data: {
json: JSON.stringify([{
loaded: false,
img: "https://source.unsplash.com/random",
Firstname: "Bob",
Lastname: "Smith"
},
{
loaded: false,
img: "https://source.unsplash.com/user/erondu",
Firstname: "Ted",
Lastname: "Smith"
},
{
loaded: false,
img: "https://source.unsplash.com/user/erondu/1600x900",
Firstname: "Jim",
Lastname: "Gump"
}
]),
delay: 0
},
context: this,
success: function(data) {
for (var x = 0; x < data.length; x++) {
self.dataArray.push(data[x]);
}
/* self.dataArray.push(data); */
console.log(self.dataArray);
console.log(typeof(self.dataArray));
console.log(data);
console.log(typeof(data));
self.SuccessfullyRetrievedModelsFromAjax(self.dataArray);
},
dataType: 'json'
});
};
this.SuccessfullyRetrievedModelsFromAjax = function(models) {
ko.mapping.fromJS(models, self.ArrayOfModels);
};
}
ko.applyBindings(new SomeViewModel());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://rawgit.com/SteveSanderson/knockout.mapping/master/build/output/knockout.mapping-latest.debug.js"></script>
<button data-bind="click: GetModelsByAjax">Go</button>
<div data-bind="template: {name: 'model-template', foreach: ArrayOfModels}"></div>
<script id="model-template" type="text/html">
<div data-bind="css: { fadeIn: loaded() === true }">
<div data-bind="text: Firstname"></div>
<div data-bind="text: Lastname"></div>
<img width="100" height="100" data-bind="attr: { src: img }">
</div>
</script>
You already have a loaded
property that is initialized as false
and observable. We'll use this property in two ways:
css
binding, to add an is-visible
class to the img + label wrapper that we can styleevent
binding, to set the value when the <img>
tag fires a load
event.Your new template, without any other changes:
<div class="Img" data-bind="css: { 'is-visible': loaded }">
<div data-bind="text: Firstname"></div>
<div data-bind="text: Lastname"></div>
<img width="100" height="100"
data-bind="attr: { src: img },
event: { load: loaded.bind($data, true) }">
</div>
The css
for the fade, that you can change to your liking:
.Img {
opacity: 0;
transition: opacity 1s ease-in-out;
}
.Img.is-visible { opacity: 1; }
In an updated version of your fiddle: http://jsfiddle.net/L7cn6y5r/