Select CSS Parent `selector` in iOS Content Blocking - css

I am setting up some content blockers (https://developer.apple.com/documentation/safariservices/creating_a_content_blocker)
The HTML I am testing on looks something like this:
<div class="<random>">
<div>
<div>
<div>
<a class="bad" />
Now, I am wondering if I can have a CSS selector that selects on .bad but then removes the entire .random div block.
I have tried things like:
{
"action": {
"type": "css-display-none",
"selector": "div > div > div > div >a[href*='speedtest.net']"
},
"trigger": {
"url-filter": "^https?:\/+([^\/:]+\\.)?google.*[:\/]"
}
}
and div:has(a).
Nothing seems to work.
Anyone know if its possible? I can't even find anywhere that says what apple supports for this (CSS4?)

Update:: has is now supported in Safari and Safari iOS. source: caniuse
There is currently no parent selector in css even though it is one of the most requested CSS feature.
The simple reason for the lack of implementation yet is performance issues.
https://snook.ca/archives/html_and_css/css-parent-selectors
The closest thing we have to a parent selector in CSS for now is :focus-within which will match an element if the element or any of its descendants is focused. However it is of no use in your case.
The proposed implementation of a parent selector as you mentioned is :has and is part of the level 4 of CSS selectors.
https://www.w3.org/TR/selectors-4/
Although it isn't implemented yet in any browser, it is now part of the technical preview for Safari so we might get it some day.
However it is not yet part of the technical preview for iOS and that might still take a lot of time.
https://caniuse.com/css-has
Even though there is no CSS parent selector, what you are describing can be easily achieved through Javascript in your Safari extension.
However you will need to give permissions to your Safari extension to inject scripts in the web page.
The users will have to accept the permissions. If they refuse you won't be able to access the page DOM through javascript.
You can do that by going in the manifest.json of your Xcode project : AppName => Shared (Extension) => Ressources => manifest
In content_scripts you will need to add
"content_scripts": [{
"js": [ "content.js" ],
"matches": [ "<all_urls>" ]
}]
You can also select the sites you want your extension to have access to with regular expressions like you did previously.
https://developer.apple.com/documentation/safariservices/safari_web_extensions/managing_safari_web_extension_permissions
Then in the content.js file you can add your javascript code to edit the web pages as you please.
Here is a function you could use to remove the parent of the bad elements:
const removeBadElementParent = () =>{
const badElements = document.querySelectorAll('.bad-class-name')
if(badElements.length === 0) return;
badElements.forEach(element =>{
if(!element instanceof HTMLElement) return;
const parent = element.parentElement;
if(!parent instanceof HTMLElement) return;
parent.style.display = 'none'
})
}
You don't want to use it immediately but only when the DOM has loaded so you will need to call it like this:
if( document.readyState !== 'loading' ) {
removeBadElementParent();
} else {
document.addEventListener("DOMContentLoaded", function(event) {
removeBadElementParent();
});
}
However most website make changes to the DOM and might add the bad elements after the DOMContentLoaded event has fired. So you will need to add a mutation observer to check when the DOM is changed:
const onDOMMutation = (callback) =>{
MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
const observer = new MutationObserver(function(mutations, observer) {
callback();
});
observer.observe(document, {
subtree: true,
attributes: true
});
}
And you can call it like this instead of calling directly the removeBadElementParent function:
if( document.readyState !== 'loading' ) {
onDOMMutation(removeBadElementParent);
} else {
document.addEventListener("DOMContentLoaded", function(event) {
onDOMMutation(removeBadElementParent);
});
}
https://developer.apple.com/documentation/safariservices/safari_app_extensions/injecting_a_script_into_a_webpage

Related

CSS style is not correctly being applied when conditional render in React

I need to conditionally render components based on screen size.
I use nextjs and getInitialProps for data fetching, the page is server-side rendered. I want to detect device screen size on the client-side, so I implement a customized hook to do it.
useWindowSize.js
import { useEffect, useState } from 'react';
export default function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: typeof window === 'undefined' ? 1200 : window.innerWidth, // default width 1200
});
useEffect(() => {
// Handler to call on window resize
function handleResize() {
// Set window width/height to state
setWindowSize({
width: window.innerWidth,
//height: window.innerHeight,
});
}
// Add event listener
window.addEventListener('resize', handleResize);
// Call handler right away so state gets updated with initial window size
handleResize();
// Remove event listener on cleanup
return () => window.removeEventListener('resize', handleResize);
}, []); // Empty array ensures that effect is only run on mount
return windowSize.width <= 600;
}
then I use this hook to detect window size and conditional render components
export default function IndexPage() {
const isMobile = useWindowSize();
if (typeof window !== "undefined") {
// if you are running it on codesanbox, I don't know why log is not printed
console.log("client side re-render");
}
return (
<div>
{isMobile ? (
<div
style={{
color: "red",
fontSize: 40
}}
>
mobile
</div>
) : (
<div
style={{
color: "blue",
fontSize: 20
}}
>
desktop
</div>
)}
</div>
);
}
IndexPage.getInitialProps = () => {
return {
a: 1
};
};
when I load the page on mobile browser, you will see
text mobile is applied wrong CSS style. video demo: https://share.getcloudapp.com/nOuk08L0
how to reproduce:
https://codesandbox.io/s/thirsty-khayyam-npqpt
Can someone please help me out. Thank you in advance!
This is an issue that is related to how React patch up DOM from SSR. When there is a mismatch between client-side and server-side rendering, React will only patch/sync the text context for the node. The DOM attribute will not be automatically updated. In your case, the SSR result has the desktop style because there is no window object, and client side has the mobile result. After the mismatch, React update the text node from 'desktop' to mobile but not the style attributes.
In my opinion, you can use two different approaches. You can use Media Query to style your component based on the screen width instead of the hook. If you are doing SSR, not SSG, you can use user agent req.headers["user-agent"] to detect the device your device is being viewed on.
For the first approach, you might need to render more DOM node you might need to. For the second approach, you won't be able to know the actual viewport size, which can cause visual issue. You might be able to combine both approach to produce a good viewing experience for your user.
Reference
https://github.com/facebook/react/issues/11128#issuecomment-334882514
Thanks for #Andrew Zheng's detailed explanation! Today I learned.
I know that I can style the layout by using pure CSS media query, but my use case needs a variable like isMobile to
if (isMobile) {
doSomethingOnlyOnMobileWeb();
} else {
doSomethingOnlyForDesktopWeb();
}
So I combined two approaches you provided, and modify my hook this way:
export default function useWindowSize(userAgent) {
let isMobile = Boolean(
userAgent &&
userAgent.match(
/Android|BlackBerry|iPhone|iPod|Opera Mini|IEMobile|WPDesktop/i
)
);
const [windowSize, setWindowSize] = useState({
width: isServer
? isMobile
? BREAKPOINT_SMALL
: BREAKPOINT_LARGE
: window.innerWidth,
});
useEffect(() => {
// Handler to call on window resize
function handleResize() {
// Set window width/height to state
setWindowSize({
width: window.innerWidth,
//height: window.innerHeight,
});
}
// Add event listener
window.addEventListener('resize', handleResize);
// Call handler right away so state gets updated with initial window size
handleResize();
// Remove event listener on cleanup
return () => window.removeEventListener('resize', handleResize);
}, []); // Empty array ensures that effect is only run on mount
return windowSize.width <= BREAKPOINT_SMALL;
}
diff: passing user-agent string to useWindowSize for server-side detection and use window.innerWidth for client-side detection. There won't be a mismatch between server and client.

Custom background for Chrome new tab replacement extension

I'm developing a new tab replacement extension for Google Chrome and I'd like to allow the user to customize the background, to do so I'm using the storage.sync API as suggested by this page.
The problem is that the style changes are applied asynchronously, so the default background (white) is briefly used during the page load resulting in unpleasing flashes.
Possible (unsatisfying) solutions are:
do not allow to change the background;
hard code a black background in the CSS (and move the problem to custom light backgrounds);
use a CSS transition (still super-ugly).
What could be an alternative approach?
Follows a minimal example.
manifest.json
{
"manifest_version": 2,
"name": "Dummy",
"version": "0.1.0",
"chrome_url_overrides": {
"newtab": "newtab.html"
},
"permissions": [
"storage"
]
}
newtab.html
<script src="/newtab.js"></script>
newtab.js
chrome.storage.sync.get({background: 'black'}, ({background}) => {
document.body.style.background = background;
});
I come up with a reasonable solution. Basically since the localStorage API is synchronous we can use it as a cache for storage.sync.
Something like this:
newtab.js
// use the value from cache
document.body.style.background = localStorage.getItem('background') || 'black';
// update the cache if the value changes from the outside (will be used the next time)
chrome.storage.sync.get({background: 'black'}, ({background}) => {
localStorage.setItem('background', background);
});
// this represents the user changing the option
function setBackground(background) {
// save to storage.sync
chrome.storage.sync.set({background}, () => {
// TODO handle error
// update the cache
localStorage.setItem('background', background);
});
}
This doesn't work 100% of the times but neither do the simple:
document.body.style.background = 'black';
So it's good enough.¹
¹ In the real extension I change the CSS variables directly and I obtain much better results than setting the element style.

How to apply style rules that are tied to a model in AngularJS?

I am building an application which sends 1-5 views and the appropriate model&controller to the client on page load. The user is guided to each view consecutively using routing and this is working just fine.
The problem I have is that I want to apply style rules to the view, but the style rules are tied to the model. I know that I cannot simply save some css as text and inject them into a <style></style> block. I cannot simply add a reference to a .css file because all of this is coming from a database, and each model could potentially have style rules that conflict with another model.
Any advice on how to solve this issue?
You can insert a style element into the page:
angular.module('myApp', [])
.controller('MyController', function($scope) {
$scope.css = ".red { color: red;}"
})
.directive('myDirective', function() {
return {
restrict: 'E',
link: function(scope, elem, attrs) {
var style = angular.element('<style type="text/css"></style>');
style.append(scope.css);
elem.append(style);
}
}
});
Plunker Demo
Here's an example of using ng-class. If you can get your css rules out of the database and into a $scope object like this:
$scope.css = {
divcss:{
'border-style': 'solid',
'border-width': '2px',
'background-color': 'yellow'
},
headercss:{
'font-weight': 'bold',
'font-size': '50px'
}
}
Then you can apply them to your view element like this:
<div ng-style="css.divcss">Check it out</div>
<span ng-style="css.headercss">Big ol' Header</span>
Plunker demo

How to include view/partial specific styling in AngularJS?

What is the proper/accepted way to use separate stylesheets for the various views my application uses?
Currently I'm placing a link element in the view/partial's html at the top but I've been told this is bad practice even though all modern browsers support it but I can see why it's frowned upon.
The other possibility is placing the separate stylesheets in my index.html's head but I would like it to only load the stylesheet if its view is being loaded in the name of performance.
Is this bad practice since styling won't take effect until after the css is loaded form the server, leading to a quick flash of unformatted content in a slow browser? I have yet to witness this although I'm testing it locally.
Is there a way to load the CSS through the object passed to Angular's $routeProvider.when?
I know this question is old now, but after doing a ton of research on various solutions to this problem, I think I may have come up with a better solution.
UPDATE 1: Since posting this answer, I have added all of this code to a simple service that I have posted to GitHub. The repo is located here. Feel free to check it out for more info.
UPDATE 2: This answer is great if all you need is a lightweight solution for pulling in stylesheets for your routes. If you want a more complete solution for managing on-demand stylesheets throughout your application, you may want to checkout Door3's AngularCSS project. It provides much more fine-grained functionality.
In case anyone in the future is interested, here's what I came up with:
1. Create a custom directive for the <head> element:
app.directive('head', ['$rootScope','$compile',
function($rootScope, $compile){
return {
restrict: 'E',
link: function(scope, elem){
var html = '<link rel="stylesheet" ng-repeat="(routeCtrl, cssUrl) in routeStyles" ng-href="{{cssUrl}}" />';
elem.append($compile(html)(scope));
scope.routeStyles = {};
$rootScope.$on('$routeChangeStart', function (e, next, current) {
if(current && current.$$route && current.$$route.css){
if(!angular.isArray(current.$$route.css)){
current.$$route.css = [current.$$route.css];
}
angular.forEach(current.$$route.css, function(sheet){
delete scope.routeStyles[sheet];
});
}
if(next && next.$$route && next.$$route.css){
if(!angular.isArray(next.$$route.css)){
next.$$route.css = [next.$$route.css];
}
angular.forEach(next.$$route.css, function(sheet){
scope.routeStyles[sheet] = sheet;
});
}
});
}
};
}
]);
This directive does the following things:
It compiles (using $compile) an html string that creates a set of <link /> tags for every item in the scope.routeStyles object using ng-repeat and ng-href.
It appends that compiled set of <link /> elements to the <head> tag.
It then uses the $rootScope to listen for '$routeChangeStart' events. For every '$routeChangeStart' event, it grabs the "current" $$route object (the route that the user is about to leave) and removes its partial-specific css file(s) from the <head> tag. It also grabs the "next" $$route object (the route that the user is about to go to) and adds any of its partial-specific css file(s) to the <head> tag.
And the ng-repeat part of the compiled <link /> tag handles all of the adding and removing of the page-specific stylesheets based on what gets added to or removed from the scope.routeStyles object.
Note: this requires that your ng-app attribute is on the <html> element, not on <body> or anything inside of <html>.
2. Specify which stylesheets belong to which routes using the $routeProvider:
app.config(['$routeProvider', function($routeProvider){
$routeProvider
.when('/some/route/1', {
templateUrl: 'partials/partial1.html',
controller: 'Partial1Ctrl',
css: 'css/partial1.css'
})
.when('/some/route/2', {
templateUrl: 'partials/partial2.html',
controller: 'Partial2Ctrl'
})
.when('/some/route/3', {
templateUrl: 'partials/partial3.html',
controller: 'Partial3Ctrl',
css: ['css/partial3_1.css','css/partial3_2.css']
})
}]);
This config adds a custom css property to the object that is used to setup each page's route. That object gets passed to each '$routeChangeStart' event as .$$route. So when listening to the '$routeChangeStart' event, we can grab the css property that we specified and append/remove those <link /> tags as needed. Note that specifying a css property on the route is completely optional, as it was omitted from the '/some/route/2' example. If the route doesn't have a css property, the <head> directive will simply do nothing for that route. Note also that you can even have multiple page-specific stylesheets per route, as in the '/some/route/3' example above, where the css property is an array of relative paths to the stylesheets needed for that route.
3. You're done
Those two things setup everything that was needed and it does it, in my opinion, with the cleanest code possible.
#tennisgent's solution is great. However, I think is a little limited.
Modularity and Encapsulation in Angular goes beyond routes. Based on the way the web is moving towards component-based development, it is important to apply this in directives as well.
As you already know, in Angular we can include templates (structure) and controllers (behavior) in pages and components. AngularCSS enables the last missing piece: attaching stylesheets (presentation).
For a full solution I suggest using AngularCSS.
Supports Angular's ngRoute, UI Router, directives, controllers and services.
Doesn't required to have ng-app in the <html> tag. This is important when you have multiple apps running on the same page
You can customize where the stylesheets are injected: head, body, custom selector, etc...
Supports preloading, persisting and cache busting
Supports media queries and optimizes page load via matchMedia API
https://github.com/door3/angular-css
Here are some examples:
Routes
$routeProvider
.when('/page1', {
templateUrl: 'page1/page1.html',
controller: 'page1Ctrl',
/* Now you can bind css to routes */
css: 'page1/page1.css'
})
.when('/page2', {
templateUrl: 'page2/page2.html',
controller: 'page2Ctrl',
/* You can also enable features like bust cache, persist and preload */
css: {
href: 'page2/page2.css',
bustCache: true
}
})
.when('/page3', {
templateUrl: 'page3/page3.html',
controller: 'page3Ctrl',
/* This is how you can include multiple stylesheets */
css: ['page3/page3.css','page3/page3-2.css']
})
.when('/page4', {
templateUrl: 'page4/page4.html',
controller: 'page4Ctrl',
css: [
{
href: 'page4/page4.css',
persist: true
}, {
href: 'page4/page4.mobile.css',
/* Media Query support via window.matchMedia API
* This will only add the stylesheet if the breakpoint matches */
media: 'screen and (max-width : 768px)'
}, {
href: 'page4/page4.print.css',
media: 'print'
}
]
});
Directives
myApp.directive('myDirective', function () {
return {
restrict: 'E',
templateUrl: 'my-directive/my-directive.html',
css: 'my-directive/my-directive.css'
}
});
Additionally, you can use the $css service for edge cases:
myApp.controller('pageCtrl', function ($scope, $css) {
// Binds stylesheet(s) to scope create/destroy events (recommended over add/remove)
$css.bind({
href: 'my-page/my-page.css'
}, $scope);
// Simply add stylesheet(s)
$css.add('my-page/my-page.css');
// Simply remove stylesheet(s)
$css.remove(['my-page/my-page.css','my-page/my-page2.css']);
// Remove all stylesheets
$css.removeAll();
});
You can read more about AngularCSS here:
http://door3.com/insights/introducing-angularcss-css-demand-angularjs
Could append a new stylesheet to head within $routeProvider. For simplicity am using a string but could create new link element also, or create a service for stylesheets
/* check if already exists first - note ID used on link element*/
/* could also track within scope object*/
if( !angular.element('link#myViewName').length){
angular.element('head').append('<link id="myViewName" href="myViewName.css" rel="stylesheet">');
}
Biggest benefit of prelaoding in page is any background images will already exist, and less lieklyhood of FOUC
#sz3, funny enough today I had to do exactly what you were trying to achieve: 'load a specific CSS file only when a user access' a specific page. So I used the solution above.
But I am here to answer your last question: 'where exactly should I put the code. Any ideas?'
You were right including the code into the resolve, but you need to change a bit the format.
Take a look at the code below:
.when('/home', {
title:'Home - ' + siteName,
bodyClass: 'home',
templateUrl: function(params) {
return 'views/home.html';
},
controler: 'homeCtrl',
resolve: {
style : function(){
/* check if already exists first - note ID used on link element*/
/* could also track within scope object*/
if( !angular.element('link#mobile').length){
angular.element('head').append('<link id="home" href="home.css" rel="stylesheet">');
}
}
}
})
I've just tested and it's working fine, it injects the html and it loads my 'home.css' only when I hit the '/home' route.
Full explanation can be found here, but basically resolve: should get an object in the format
{
'key' : string or function()
}
You can name the 'key' anything you like - in my case I called 'style'.
Then for the value you have two options:
If it's a string, then it is an alias for a service.
If it's function, then it is injected and the return value is treated
as the dependency.
The main point here is that the code inside the function is going to be executed before before the controller is instantiated and the $routeChangeSuccess event is fired.
Hope that helps.
Awesome, thank you!! Just had to make a few adjustments to get it working with ui-router:
var app = app || angular.module('app', []);
app.directive('head', ['$rootScope', '$compile', '$state', function ($rootScope, $compile, $state) {
return {
restrict: 'E',
link: function ($scope, elem, attrs, ctrls) {
var html = '<link rel="stylesheet" ng-repeat="(routeCtrl, cssUrl) in routeStyles" ng-href="{{cssUrl}}" />';
var el = $compile(html)($scope)
elem.append(el);
$scope.routeStyles = {};
function applyStyles(state, action) {
var sheets = state ? state.css : null;
if (state.parent) {
var parentState = $state.get(state.parent)
applyStyles(parentState, action);
}
if (sheets) {
if (!Array.isArray(sheets)) {
sheets = [sheets];
}
angular.forEach(sheets, function (sheet) {
action(sheet);
});
}
}
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
applyStyles(fromState, function(sheet) {
delete $scope.routeStyles[sheet];
console.log('>> remove >> ', sheet);
});
applyStyles(toState, function(sheet) {
$scope.routeStyles[sheet] = sheet;
console.log('>> add >> ', sheet);
});
});
}
}
}]);
If you only need your CSS to be applied to one specific view, I'm using this handy snippet inside my controller:
$("body").addClass("mystate");
$scope.$on("$destroy", function() {
$("body").removeClass("mystate");
});
This will add a class to my body tag when the state loads, and remove it when the state is destroyed (i.e. someone changes pages). This solves my related problem of only needing CSS to be applied to one state in my application.
'use strict';
angular.module('app')
.run(
[
'$rootScope', '$state', '$stateParams',
function($rootScope, $state, $stateParams) {
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
}
]
)
.config(
[
'$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$urlRouterProvider
.otherwise('/app/dashboard');
$stateProvider
.state('app', {
abstract: true,
url: '/app',
templateUrl: 'views/layout.html'
})
.state('app.dashboard', {
url: '/dashboard',
templateUrl: 'views/dashboard.html',
ncyBreadcrumb: {
label: 'Dashboard',
description: ''
},
resolve: {
deps: [
'$ocLazyLoad',
function($ocLazyLoad) {
return $ocLazyLoad.load({
serie: true,
files: [
'lib/jquery/charts/sparkline/jquery.sparkline.js',
'lib/jquery/charts/easypiechart/jquery.easypiechart.js',
'lib/jquery/charts/flot/jquery.flot.js',
'lib/jquery/charts/flot/jquery.flot.resize.js',
'lib/jquery/charts/flot/jquery.flot.pie.js',
'lib/jquery/charts/flot/jquery.flot.tooltip.js',
'lib/jquery/charts/flot/jquery.flot.orderBars.js',
'app/controllers/dashboard.js',
'app/directives/realtimechart.js'
]
});
}
]
}
})
.state('ram', {
abstract: true,
url: '/ram',
templateUrl: 'views/layout-ram.html'
})
.state('ram.dashboard', {
url: '/dashboard',
templateUrl: 'views/dashboard-ram.html',
ncyBreadcrumb: {
label: 'test'
},
resolve: {
deps: [
'$ocLazyLoad',
function($ocLazyLoad) {
return $ocLazyLoad.load({
serie: true,
files: [
'lib/jquery/charts/sparkline/jquery.sparkline.js',
'lib/jquery/charts/easypiechart/jquery.easypiechart.js',
'lib/jquery/charts/flot/jquery.flot.js',
'lib/jquery/charts/flot/jquery.flot.resize.js',
'lib/jquery/charts/flot/jquery.flot.pie.js',
'lib/jquery/charts/flot/jquery.flot.tooltip.js',
'lib/jquery/charts/flot/jquery.flot.orderBars.js',
'app/controllers/dashboard.js',
'app/directives/realtimechart.js'
]
});
}
]
}
})
);

Ember.js Router: How to animate state transitions

Has somebody found a good way to animate state transitions?
The router immediately removes the view from the DOM. The problem with that is that I can't defer that until the end of the animation. Note: I'm using v1.0.0-pre.4.
Billy's Billing just released an Ember module that supports animated transitions.
I'll expand on Lesyk's answer. If you need to apply it to multiple views in a DRY way, you can create a customization class like this:
App.CrossfadeView = {
didInsertElement: function(){
//called on creation
this.$().hide().fadeIn(400);
},
willDestroyElement: function(){
//called on destruction
this.$().slideDown(250);
}
};
And then in your code you apply it on your various view classes. As Ember depends on jQuery you can use pretty much any jQuery animation.
App.IndexView = Ember.View.extend(App.CrossfadeView);
App.PostView = Ember.View.extend(App.CrossfadeView);
I know this is pretty old, but the best solution for this context-specific animation today is probably ember liquid fire.
It allows you to do things like this in a transition file:
export default function(){
this.transition(
this.fromRoute('people.index'),
this.toRoute('people.detail'),
this.use('toLeft'),
this.reverse('toRight')
);
};
Ran into this same requirement on my app. Tried Ember Animated Outlet, but didn't give the granularity I needed (element specific animations).
The solution that worked for me was as follows --
Change linkTo to be an action
{{#linkTo "todos"}}<button>Todos</button>{{/linkTo}}
Becomes...
<a href="#/todos" {{action "goToTodos"}}><button>Todos</button></a>
Create Method for goToTodos in current controller
App.IndexController = Ember.Controller.extend({
goToTodos: function(){
// Get Current 'this' (for lack of a better solution, as it's late)
var holdThis = this;
// Do Element Specific Animation Here
$('#something').hide(500, function(){
// Transition to New Template
holdThis.transitionToRoute('todos');
});
}
});
Finally -- To animate in elements on the Todos Template, use didInsertElement on the view
App.TodosView = Ember.View.extend({
didInsertElement: function(){
// Hide Everything
this.$().hide();
// Do Element Specific Animations Here
$('#something_else').fadeIn(500);
}
});
So far, this is the most elegant solution I've found for element specific animations on transition. If there is anything better, would love to hear!
I've found another drop-in solution that implements animations in Views: ember-animate
Example:
App.ExampleView = Ember.View.extend({
willAnimateIn : function () {
this.$().css("opacity", 0);
},
animateIn : function (done) {
this.$().fadeTo(500, 1, done);
},
animateOut : function (done) {
this.$().fadeTo(500, 0, done);
}
}
Demo: author's personal website
App.SomeView = Ember.View.extend({
didInsertElement: function(){
//called on creation
this.$().hide().fadeIn(400);
},
willDestroyElement: function(){
//called on destruction
this.$().slideDown(250)
}
});

Resources