This question already has answers here:
Imitating a blink tag with CSS3 animations
(10 answers)
Closed 7 years ago.
I'm working on a small application that has a message window. The messages are stored in a db and updated by fetching 5 of the latest messages:
scienceTeamMessages = new Meteor.Collection('scienceTeamMessages');
Meteor.methods({
'sendMessageFromMS': function(message, destination) {
if (destination === "scienceTeam") {
scienceTeamMessages.insert({
message: message,
createdAt: new Date()
});
}
}
});
These messages are then iterated over in an html template:
{{#each messages}}
<li><h6>{{message}}</h6></li>
{{/each}}
What I would like, and I can't figure out how to do, is for the latest message to blink a few times, so as to draw attention from the user when a new message arrives. Like, fading in and out from black to red 3 times.
Any suggestions? I know how to do the css, but I am unsure about how to do it on changes to the database. That is why the other solutions on SO won't work in this specific question.
For the animations have a look at the link posted in the comments: Imitating a blink tag with CSS3 animations
If you'd like to add animations for a specific time limit use Meteor.setTimeout().
To do animations in Meteor see this Microscope demo example: https://github.com/DiscoverMeteor/Microscope/blob/master/client/templates/application/layout.js
And lastly, if you'd like to perform a certain action when an element is added to a collection, consider using cursor.observe or cursor.observeChanges which is documented here: http://docs.meteor.com/#/full/observe
A lot of links, but hopefully with all that together you can put together the solution you're looking for.
You need to set this logic on your helper :
Template.foo.helpers({
messages: function() {
var elements = scienceTeamMessages.find({},{sort: {created_at: -1}}).fetch();
for (var i = elements.length - 1; i >= 0; i--) {
elements[i].drawAttention = (i == 0);
};
return elements;
},
})
<template name="foo">
{{#each messages}}
<li><h6 {{#if drawAttention}}class="burn-yours-eyes"{{/if}}>{{message}}</h6></li>
{{/each}}
</template>
I did not tested this code, hope it will help you.
Related
I am trying to make moving cars on MapBox using new promising GL Symbol layer/source. It looks very nice both on android and ios, but I faced with two impossibilities.
Symbols are always clustering. setIconAllowOverlap() and setIconIgnorePlacement() don't help: on some zoom it WILL be clustered. On both platforms.
How can I disable symbols clustering completely?
UPDATE: the code and even fast solution! (possibly bug? see comment at withTextField)
in onStyleLoaded():
...
carManager = new SymbolManager(mapView, mapboxMap, style);
carManager.setIconAllowOverlap(true);//doesn't help
carManager.setIconIgnorePlacement(true);//doesn't help
...
in drawCarFunction():
...
SymbolOptions carOptions = new SymbolOptions()
.withLatLng(latLng)
.withIconImage(carPlate)
//.withTextField(carPlate) //!!!! here it will cluster if text exists, and will NOT - without any text
;
Symbol car= carManager.create(carOptions);
carSymbols.add(car);
...
Next question:
On Android we have symbolManager.addClickListener() , but how could I catch a click on iOS? I know I can catch the tap, calculate nearest marker etc but
How to get symbol click simpler in swift?
In Moving cars task I should enumerate existing cars, move running, add newest. Where should I store cars IDs to get it later on next move? Where are no even symbol.setTag() option... Storing IDs in snippet (as on GMaps) is not that choice I expected from MapBox. Sure, I can make an array of pairs "car ID = symbol ID", but
How to store my own UID in the symbol?
UPDATE: the code. Note the comment near getTag()
void moveExistingCarOrAddNew(int carId, LatLng newLocation){
for (int i = 0; i < carManager.getAnnotations().size(); i++) {
if (carManager.getAnnotations().get(i).getTag()==carId){ //but no getTag() here, I should fit data into text fields
car.setLatLng(newLocation); //move!
} else {
...//create new marker as shown above }
}
}
}
I'm a teacher and creating a page to organize my lesson plans. There should be the ability to add new lessons (li) and new weeks (ul). The lessons are sortable between each of the weeks. Each newly added item will then be saved to localStorage.
So far, I'm able to create the lessons and new weeks. The sortable function works. The save function works... except that it will not save any of the new weeks (ul). When I refresh, the new lessons (li) are still on the page, but the new weeks (ul) are gone.
$("#saveAll").click(function(e) {
e.preventDefault();
var listContents = [];
$("ul").each(function(){
listContents.push(this.innerHTML);
})
localStorage.setItem('todoList', JSON.stringify(listContents));
});
$("#clearAll").click(function(e) {
e.preventDefault();
localStorage.clear();
location.reload();
});
loadToDo();
function loadToDo() {
if (localStorage.getItem('todoList')){
var listContents = JSON.parse(localStorage.getItem('todoList'));
$("ul").each(function(i){
this.innerHTML = listContents [i];
})
}
}
I created a fiddle here.
You can click the "Add New Week" button and then click the "Create Lesson" button and drag the new lesson into one of the weeks. After clicking "Save All", only the first week is saved.
I can't seem to figure out what's missing.
It's saving correctly, but since the page only has one <ul> element initially, that is the only one that gets populated in loadToDo(). (listContents has more than one element, but $("ul").each(...) only iterates over one element.)
There is a quick band-aid you can use to resolve this. Refactor your #new-week-but click handler into a named function:
function addNewWeek() {
var x = '<ul class="sortable weeklist"></ul>';
$(x).appendTo('.term').sortable({connectWith: '.sortable'});
}
$('#new-week-but').click(addNewWeek);
Then add this block after you fetch the array from storage but before you enumerate the <ul> elements:
var i;
for (i = 2; i < listContents.length; ++i) {
addNewWeek();
}
This will add the required number of <ul> elements before attempting to populate them.
I chose to initialize i to two because this creates two fewer than the number of elements in listContents. We need to subtract one because there is a <ul> in .term when the page loads, and another because the <ul id="new-lesson-list"> contents also get saved in listContents. (Consider filtering that element out in your #saveAll click handler.)
(Note that this requires merging all of your $(document).ready() functions into one big function so that addNewWeek() is visible to the rest of your code.)
Suggestions to improve code maintainability:
Give each editable <ul> a CSS class so that they can be distinguished from other random <ul> elements on the page. Filter for this class when saving data so that the "template" <ul> doesn't get saved, too.
Remove the one default editable <ul> from the page. Instead, in your loadToDo() function, add an else block to the if block and call addNewWeek() from the else block. Also, call it if listContents.length == 0. This will prevent duplicating the element in the HTML source (duplication is bad!) and having to account for it in your load logic.
If you implement both of these then you can initialize i to 0 instead of 2 in my sample code, which is a lot less weird-looking (and less likely to trip up future maintainers).
Just started to learn a Meteor and stuck with a silly problem. I have a collection "Images" and i trying to get one random image from her. But in the browser's console it's says that "Cannot read property 'url' of undefined", but if type a db's "findOne" method in the console there is a record that i wanted.
A client code:
Template.main.helpers({
img: function () {
return Images.findOne({rand: {$gte: Math.random()}}).url;
}
});
Collection:
Images = Meteor.Collection('images');
On a server's side i've got simple fixtures for initial tests
if(Images.find().count() === 0){
Images.insert({url: "http://domain.com/test1.jpg", rand: Math.random()});
Images.insert({url: "http://domain.com/test2.jpg", rand: Math.random()});
Images.insert({url: "http://domain.com/test3.jpg", rand: Math.random()});
}
And simple template:
<head>
<title>Test</title>
</head>
<body>
{{> main}}
</body>
<template name="main">
{{img}}
</template>
P.S. I'm working under the Win 8.
First, I'm going to assume that the images are published to the client. You should verify that Images.find().count() > 0 is true in your browser console (not in your template code).
Next, you should read about how to add guards to your template code to fix the error you are seeing. This article should explain what you need to know.
Finally, unless you actually need rand on your documents, there are better ways to accomplish a random selection. Your question says you are looking for one image, but it's possible that 1 or 0 could be returned with your code. Instead you could use the built-in Random package. Give this a try:
Template.main.helpers({
img: function() {
var image = Random.choice(Images.find().fetch());
return image && image.url;
}
});
Although, I think it makes more sense to return the image object:
Template.main.helpers({
image: function() {
return Random.choice(Images.find().fetch());
}
});
And then select the url in your template (no guards are needed in this case):
<template name="main">
<img src="{{image.url}}">
</template>
You need to be careful with your assumptions when you load a template. When your web page has loaded on the web browser, it won't necessarily have any data in it (your Images.findOne() query could return null)
You just need to take account of this possibility
var image = Images.findOne({rand: {$gte: Math.random()}});
return image && image.url
I'm attempting to fade-in new elements in a reactive {{#each}} of the comments posted by users.
I have a code sample at https://gist.github.com/3119147 of a very simple comments section (textarea and new comment insert code not included, but it's very boilerplate.). Included is a snippet of CSS where I give .comment.fresh { opacity: 0; }, and then in my script, I have:
Template.individual_comment.postedago_str = function() {
var id = this._id;
Meteor.defer(function() {
$('#commentid_'+id+'.fresh').animate({'opacity':'1'}, function() {
$(this).removeClass('fresh');
});
});
return new Date(this.time).toString();
};
Which seems like a terrible place to execute an animation. My thinking is that each time a new comment is rendered, it will need to call all my Template.individual_comment.* functions, so that's why my animation defers from one of those. However, Meteor is calling Template.individual_comment.postedago_str() each time a different collection (Likes) is inserted to. This means I click the Like button, and my whole list of comments flashes white and fades back in (very annoying!).
I read the Meteor documentation and tried to figure out how to better slice up my templates so only chunks will update, and I added id="" attributes everywhere that seemed reasonable.. still this bug. Anyone know what's going on?
TIA!
As a workaround, you could wrap an {{if}} block around the fresh class on individual comments, that would check the comment's creation time and only add the fresh class in the first place if the comment is actually recent. Something like:
<div class="comment{{#if isActuallyFresh}} fresh{{/if}}" id="commentid_{{_id}}">
And then define the isActuallyFresh function:
Template.individual_comment.isActuallyFresh = function() {
if ((new Date().getTime() - this.time) < 300000) // less than 5 minutes old
return true;
else
return false;
I want to block scrolling page "out of the iPhone screen" (when gray Safari's background behind the page border is visible). To do this, I'm cancelling touchmove event:
// Disables scrolling the page out of the screen.
function DisableTouchScrolling()
{
document.addEventListener("touchmove", function TouchHandler(e) { e.preventDefault(); }, true);
}
Unfortunately, this also disables mousemove event: when I tap on a button then move my finger out of it, then release the screen, the button's onclick event is triggered anyway.
I've tried mapping touch events on mouse events, as desribed here: http://ross.posterous.com/2008/08/19/iphone-touch-events-in-javascript/, but to no avail (the same behavior).
Any ideas?
From what I understand of your question, you've attempted to combine the code you've presented above with the code snippet provided by Ross Boucher on Posterous. Attempting to combine these two snippets back-to-back won't work, because in disabling touchmove, you've also disabled the shim that allows mousemove to work via his sample.
This question and its answers sketch out a workable solution to your problem. You should try these two snippets to see if they resolve your issue:
This snippet, which disables the old scrolling behavior:
elementYouWantToScroll.ontouchmove = function(e) {
e.stopPropagation();
};
Or this one, from the same:
document.ontouchmove = function(e) {
var target = e.currentTarget;
while(target) {
if(checkIfElementShouldScroll(target))
return;
target = target.parentNode;
}
e.preventDefault();
};
Then, drop in the code on Posterous:
function touchHandler(event)
{
var touches = event.changedTouches,
first = touches[0],
type = "";
switch(event.type)
{
case "touchstart": type = "mousedown"; break;
case "touchmove": type="mousemove"; break;
case "touchend": type="mouseup"; break;
default: return;
}
//initMouseEvent(type, canBubble, cancelable, view, clickCount,
// screenX, screenY, clientX, clientY, ctrlKey,
// altKey, shiftKey, metaKey, button, relatedTarget);
var simulatedEvent = document.createEvent("MouseEvent");
simulatedEvent.initMouseEvent(type, true, true, window, 1,
first.screenX, first.screenY,
first.clientX, first.clientY, false,
false, false, false, 0/*left*/, null);
first.target.dispatchEvent(simulatedEvent);
event.preventDefault();
}
And that should do it for you. If it doesn't, something else isn't working with Mobile Safari.
Unfortunately I haven't had the time to check out to above yet but was working on an identical problem and found that the nesting of elements in the DOM and which relation you apply it to affects the handler a lot (guess the above solves that, too - 'var target = e.currentTarget').
I used a slightly different approach (I'd love feedback on) by basically using a class "locked" that I assign to every element which (including all its children) i don't want the site to scroll when someone touchmoves on it.
E.g. in HTML:
<header class="locked">...</header>
<div id="content">...</div>
<footer class="locked"></div>
Then I have an event-listener running on that class (excuse my lazy jquery-selector):
$('.ubq_locked').on('touchmove', function(e) {
e.preventDefault();
});
This works pretty well for me on iOs and Android and at least gives me the control to not attach the listener to an element which I know causes problems. You do need to watch your z-index values by the way.
Plus I only attach the listener if it is a touch-device, e.g. like this:
function has_touch() {
var isTouchPad = (/hp-tablet/gi).test(navigator.appVersion);
return 'ontouchstart' in window && !isTouchPad;
}
This way non-touch devices will not be affected.
If you don't want to spam your HTML you could of course just write the selectors into an array and run through those ontouchmove, but I would expect that to be more costly in terms of performance (my knowledge there is limited though). Hope this can help.