1

I setup a single-page app with AngularJS and used Skrollr on the home page. I have not used Skrollr before, so I wanted to check with others about the proper 'Angular' way to integrate it with AngularJS, before I start to dive into using more features

What I did in Angular was create a service to load the script onto the page and call skrollr.init() and return it as a promise. Then injected the service to a directive which calls refresh as needed. If a page needs skrollr, I can use this directive on the page somewhere and set the data attributes per skrollr documentation.

ie this works:

<div class="main" skrollr-tag>
    <div data-0="color:rgb(0,0,255);" data-90="color:rgb(255,0,0);">WOOOT</div>
</div>

It seems elements added to DOM later on, such as by ngRepeat, skrollr doesn't know about, so I need to include this directive on all elements generated dynamically w/ skrollr data attributes for it to work.

<div class="main" skrollr-tag> 
    <!-- this heading will animate all the time -->
    <h1 data-0="opacity: 1" data-50="opacity: 0">WOOT!</h1>
    <div data-ng-repeat="item in items" class="had-to-add-skrollr-again" skrollr-tag> 
        <!-- skrollr animates this only on page refresh, unless skrollr-tag duplicated above -->
        <div data-0="color:rgb(0,0,255);" data-90="color:rgb(255,0,0);">{{item.name}}</div>
    </div>
</div>

So, to recap, skrollr is 'aware' of these dynamic elements on the 1st load after refresh, but then after navigating to a different route then back again they no longer get animated unless you refresh page again, or add skrollr-tag directive to the dynamic elements themselves.

Is this a bad idea for performance reasons to include this directive on each dynamic element needing skrollr, thus calling refresh() again for each one? Ideally solution would be load skrollr-tag directive once per page, and it's aware of dynamic elements. I am open to any completely different cleaner more simple way to integrate skrollr to angular.

The angular code is here:

service:

.service('skrollrService', ['$document', '$q', '$rootScope', '$window', 
    function($document, $q, $rootScope, $window){
        var defer = $q.defer();

        function onScriptLoad() {
            // Load client in the browser
            $rootScope.$apply(function() { 
                var s = $window.skrollr.init({
                        forceHeight: false
                    });
                defer.resolve(s); 
            });
        }

        // Create a script tag with skrollr as the source
        // and call our onScriptLoad callback when it
        // has been loaded

        var scriptTag = $document[0].createElement('script');
        scriptTag.type = 'text/javascript'; 
        scriptTag.async = true;
        scriptTag.src = 'lib/skrollr/dist/skrollr.min.js';

        scriptTag.onreadystatechange = function () {
            if (this.readyState === 'complete') onScriptLoad();
        };

        scriptTag.onload = onScriptLoad;

        var s = $document[0].getElementsByTagName('body')[0];
        s.appendChild(scriptTag);

        return {
            skrollr: function() { return defer.promise; }
        };

    }
 ]);

directive:

.directive('skrollrTag', [ 'skrollrService', 
    function(skrollrService){
        return {
            link: function(){
                skrollrService.skrollr().then(function(skrollr){
                    skrollr.refresh();
                });
            }
        };
    }
])
aarosil
  • 4,848
  • 3
  • 27
  • 41
  • Can I see your html, with an example of what you're talking about when you mean you have to add the directive on all of them (not sure I understand why, not having ever used this scrolling library before). – Andrew Church May 17 '14 at 19:42
  • Sure I linked to the homepage in the 1st sentence above. Basically if the elements are generated dynamically (from ngRepeat, etc) I need to have skrollr-tag directive on them for skrollr to work. Otherwise I can just throw skrollr-tag directive on element anywhere on the page, and it works fine. – aarosil May 17 '14 at 19:47

3 Answers3

0

This answer should help: AngularJS watch DOM change. Try updating your directive to watch for child node changes. This way, it'll automatically refresh whenever new nodes are added.

.directive('skrollrTag', [ 'skrollrService', 
    function(skrollrService){
        return {
            link: function(scope, element, attrs){
                skrollrService.skrollr().then(function(skrollr){
                    skrollr.refresh();
                });

               //This will watch for any new elements being added as children to whatever element this directive is placed on. If new elements are added, Skrollr will be refreshed (pulling in the new elements
               scope.$watch(
                   function () { return element[0].childNodes.length; },
                   function (newValue, oldValue) {
                   if (newValue !== oldValue) {
                       skrollrService.skrollr().then(function(skrollr){
                           skrollr.refresh();
                       });
                   }
               });
            }
        };
    }
]);

EDIT

Updated to account for the promise you're using (that would already be resolved), and added a comment to further explain the solution.

Community
  • 1
  • 1
Andrew Church
  • 1,391
  • 11
  • 13
  • Hi, sorry, example was initially somewhat unclear. This does work already if `skrollr-tag` directive is added on the dynamic items themselves. I was more looking for a way just to have it load the directive once per page. I checked the skrollr source code though and you could pass `refresh()` a element to reparse, so maybe a fine approach is just to use the `skrollr-tag` directive for every element wanting to to be used w/ skrollr, and update directive to use `refresh(element[0])`? – aarosil May 17 '14 at 22:35
  • My solution IS so you could have the skrollr-tag once per page. It watches all child elements (so you don't need to add the directive for all your dynamic elements inside your ng-repeat). Just add the skrollr-tag directive (updated to reflect the code above) on your outermost parent div of the scrolling section, and when child nodes are dynamically added, the watcher will cause skrollr to refresh. – Andrew Church May 17 '14 at 22:56
  • Forgot to mention, it didn't work. Rather, it works in 1st page load or refresh, then not again (either with, or without $watch). Basically, it made no difference. I'm guessing due to how skrollr works. – aarosil May 17 '14 at 23:16
  • Note that if you're copy and pasting this code to use for yourself, you'll need to change "$scope" to "scope" on line 10 to avoid getting a syntax error. – Himmel Feb 26 '15 at 23:33
0

I'm currently having the same issue trying to integrate Skrollr into AngularJS.

The problem is basically this directive, it works when the page loads for the first time but then nothing is happening even though its being called when new html elements are created - or when you change views.

.directive('skrollrTag', [ 'skrollrService', 
function(skrollrService){
    return {
        link: function(){
            skrollrService.skrollr().then(function(skrollr){
                skrollr.refresh();
            });
        }
    };
}

])

I think the reason is the way angularJS injects new html content. By the time skrollr does "refresh" its not yet rendered or some sort of conflict. Maybe the only solution is to modify skrollr script.

user1214120
  • 173
  • 1
  • 2
  • 15
0

i made a directive for skrollr

(function () {
  'use strict';
  angular.module('myApp', [])
    .directive('skrollr', function () {
      var obj = {
        link: function () {
          /* jshint ignore:start */
          skrollr.init().refresh();
          /* jshint ignore:end */
        }
      };
      return obj;
    });
})();

and use like this

<div skrollr data-0="background-color:rgb(0,0,255);" data-500="background-color:rgb(255,0,0);">WOOOT</div>
Deepak Sisodiya
  • 850
  • 9
  • 11