admin管理员组文章数量:1312878
I have created my own little image slider, and to get the loader working I had to create an addEventListener
and then append the loaded image into the DOM.
However, there's a bug in this scenario: When an image takes a while to load and the user clicks past it before it is loaded to see the next image, the event listener is still working in the background, and when the image is then fully loaded it overwrites what the user is currently looking at.
My HTML:
<template name="ImageGallery">
{{#each galleryImages}}
{{#if viewingImage}}
{{> Image}}
{{/if}}
{{/each}}
</template>
<template name="Image">
<div class="image-in-gallery">LOADING</div>
</template>
Checking if the "image" the user wants to see is the one we have hit in the each
iteration (thus displaying it):
Template.ImageGallery.helpers({
viewingImage: function() {
var self = this
var galleryImages = Template.parentData().galleryImages
var renderImage = false
var viewing = Template.instance().viewingImage.get()
var posOfThisImage = lodash.indexOf(galleryImages, self)
if (viewing === posOfThisImage) {
renderImage = true
}
return renderImage
}
})
Enabling the user to see the next "image" and looping if we have hit the end:
Template.ImageGallery.events({
'click .click-to-see-next-image': function(event, template) {
var viewing = template.viewingImage.get()
var imageCount = this.galleryImages.length
var nextImage = ++viewing
if (nextImage < imageCount) {
template.viewingImage.set(nextImage)
}
else {
template.viewingImage.set(0)
}
}
})
The loader:
Template.Image.onRendered({
var imageUrl = Template.currentData().name + Template.parentData().name + '.jpg'
var imageObj = new Image()
imageObj.src = imageUrl
imageObj.addEventListener('load', function() {
$('.image-in-gallery').empty()
$('.image-in-gallery').append($(imageObj))
}
})
You can see where the problem lies: the empty()
and append()
of course overwrites the image the user currently looking at and sets it to be whatever is next loaded.
I want to add a break somehow in the addEventListener
function to see if the image that is loaded is actually the one the user wants to see. But there are two problems:
1) The ReactiveVar
variable isn't available in this template. Do I need to use a Session
variable after all?
2) I have no idea how to break.
Can anyone help?
I have created my own little image slider, and to get the loader working I had to create an addEventListener
and then append the loaded image into the DOM.
However, there's a bug in this scenario: When an image takes a while to load and the user clicks past it before it is loaded to see the next image, the event listener is still working in the background, and when the image is then fully loaded it overwrites what the user is currently looking at.
My HTML:
<template name="ImageGallery">
{{#each galleryImages}}
{{#if viewingImage}}
{{> Image}}
{{/if}}
{{/each}}
</template>
<template name="Image">
<div class="image-in-gallery">LOADING</div>
</template>
Checking if the "image" the user wants to see is the one we have hit in the each
iteration (thus displaying it):
Template.ImageGallery.helpers({
viewingImage: function() {
var self = this
var galleryImages = Template.parentData().galleryImages
var renderImage = false
var viewing = Template.instance().viewingImage.get()
var posOfThisImage = lodash.indexOf(galleryImages, self)
if (viewing === posOfThisImage) {
renderImage = true
}
return renderImage
}
})
Enabling the user to see the next "image" and looping if we have hit the end:
Template.ImageGallery.events({
'click .click-to-see-next-image': function(event, template) {
var viewing = template.viewingImage.get()
var imageCount = this.galleryImages.length
var nextImage = ++viewing
if (nextImage < imageCount) {
template.viewingImage.set(nextImage)
}
else {
template.viewingImage.set(0)
}
}
})
The loader:
Template.Image.onRendered({
var imageUrl = Template.currentData().name + Template.parentData().name + '.jpg'
var imageObj = new Image()
imageObj.src = imageUrl
imageObj.addEventListener('load', function() {
$('.image-in-gallery').empty()
$('.image-in-gallery').append($(imageObj))
}
})
You can see where the problem lies: the empty()
and append()
of course overwrites the image the user currently looking at and sets it to be whatever is next loaded.
I want to add a break somehow in the addEventListener
function to see if the image that is loaded is actually the one the user wants to see. But there are two problems:
1) The ReactiveVar
variable isn't available in this template. Do I need to use a Session
variable after all?
2) I have no idea how to break.
Can anyone help?
Share Improve this question asked Dec 18, 2015 at 10:26 YeatsYeats 2,0653 gold badges32 silver badges51 bronze badges 6- 1 Can you try to setup an jsfiddle? The code looks to scattered to make a good judgement where the so called bug is. – Ferry Kobus Commented Dec 20, 2015 at 10:48
- When I add the full code it's "too scattered", when I don't people demand the full code. Love this site. – Yeats Commented Dec 20, 2015 at 15:45
- There's a difference in adding a full code on the site and setting up a jsfiddle. Now I have to clone your code and create a working example myself to start helping you. When adding a jsfiddle (just a link) you'll be helped much quicker and almost always have a garanteed answer. – Ferry Kobus Commented Dec 20, 2015 at 18:02
- @FerryKobus This is Meteor. I can't make a jsfiddle. – Yeats Commented Dec 20, 2015 at 18:08
- so, no answer given was good enough? What was missing? – Martins Untals Commented Dec 27, 2015 at 18:39
4 Answers
Reset to default 4 +25Reading through your question made me think, that maybe it is possible to rebuild your templating setup without the need of this plicated code within templates and fix the root cause - inability to understand which image is currently visible and to easily set next one to be visible.
I have found that best bet is to forget about trying to access parent data context from child templates, because ReactiveVar
is not accessible via data contexts anyway (As you have no doubt found out). And data context hierarchies are dependent on Blaze html, so it is not advised to use them in this way.
And other extreme is having Session variable, that would be very global.
Luckily there is a middle way.
Define ReactiveVar
outside of Template namespace, and then it will be equally accessible by both parent and children templates. This is works especially well, if you are trying to write a package, and do not want to pollute global Session namespace.
For example put this in hello.html:
<head>
<title>rangetest</title>
</head>
<body>
<h1>Wele to RangeTest!</h1>
{{> hello}}
{{> rangeList}}
</body>
<template name="hello">
<p>And another test {{anotherTest}}</p>
</template>
<template name="rangeList">
{{#each ranges}}
{{> range}}
{{/each}}
</template>
<template name="range">
<p>{{name}}</p>
<input type="range" value="{{value}}" min="1" max="10">
</template>
and this in hello.js
if (Meteor.isClient) {
anotherDict = new ReactiveDict('another');
anotherDict.set('hey',2);
Template.hello.helpers({
anotherTest: function() {
return anotherDict.get('hey');
}
});
Template.rangeList.helpers({
ranges: function() {
var ranges = [
{
'name':'first',
'value': 5
},
{
'name':'second',
'value': 6
},
{
'name':'third',
'value': 7
},
{
'name':'fourth',
'value': 8
}
];
return ranges;
},
});
Template.range.events({
'input input[type="range"]': function(e,t) {
var value = parseInt(e.target.value);
anotherDict.set('hey',value);
}
});
}
you will see that reactive variables propagate nicely between templates.
Probably this does not answer your question about event listeners, but hopefully you will be able to replace your manually added event listener with the Template.Image.events({
event listeners after implementing reactivity the way I have proposed, and those will be tightly bound to particular child template and there will be no way that they would fire unpredictably.
p.s. I had example using ReactiveDict and range inputs, as this was the usecase where I needed to solve similar issue. I had all three options investigated, and this was the one that finally worked and felt more or less meteor way as well.
I think there are two flaws in your code :
You never remove your
load
event when your template is destroyed. So even if your template is destroyed because you clicked to get your next image, the image is still loading in your browser and theload
event is launched. destroyed elements removeEventListenerYou are using
$
in youronRendered
.$
refers to the the global DOM whereas this.$ would only look for the local DOM of your template. If you had usedthis.$
it should have thrown an error because your local DOM was destroyed and the wrong image wouldn't have been showed.
This is how I would do it : https://github./darkship/ImageGallery
I see, probably the issue is the architecture itself.
You should use two different data structures. Separate the loading from the rendering.
Let's say galleryImages is the images data, you should iterate them just in order to load images on the client side and push them into a different array loadedImages.
Then the template should iterate and interacts with loadedImages.
Sorry but I'm not familiar with meteor I can not help with code.
Use a dummy/fake/hidden img tag to initial load the next image, and listen to the load event of this element, after the image was loaded into this element it is cached and you can put it into viewer.
Hint: for the faked element you should use
.fake-img {visibility: hidden; position: absolute}
for the hidden image tag, some browsers are not loading images if the rule is display: none
hope it helps..
But I would also agree, that you should think about your architecture and try not to use jquery dom manipulations in meteor
本文标签: javascriptHow do I break an eventListener if there has been changesStack Overflow
版权声明:本文标题:javascript - How do I break an eventListener if there has been changes? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741904375a2404055.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论