【问题标题】:AngularJS Directive to generate a table of contentsAngularJS 指令生成目录
【发布时间】:2014-03-27 09:01:29
【问题描述】:

想象一个包含标题 (h1-h6) 的 html 文本。在我的情况下,它也以 DOM 的形式出现在 html 页面中。所以使用 jQuery 我会做类似$('.text').find('h1,h2,h3,h4,h5,h6') 的事情来提取所有的标题。

但我不想使用 jQuery 或任何其他繁重的框架。我怎么能用 angularJS 做到这一点? 请记住,我需要以正确的顺序将标题显示为目录。

【问题讨论】:

    标签: javascript html angularjs dom angularjs-directive


    【解决方案1】:

    看看这个库 - https://www.cssscript.com/generating-a-table-of-contents-with-pure-javascript-toc/

    你可以简单地将js代码放在你的控制器中,它会将所有标题放在一个id为#toc的标签下

    在您的 html 文件中创建

    <div class="col-md-3 col-xs-12">
        <aside id="toc"></aside> <!-- Content will appear here -->
    </div>
    <div class="col-md-9 col-xs-12 no-padding">
        <div id="doc-content">
        ... Your content is here ...
    </div>
    
    

    然后在你的控制器中包含这个函数。例如,您可以从 onInit 调用它。

    loadTOC(){
    
    
        // Definitions
        var extendObj = function (src, target) {
          for (var prop in target) {
              if (target.hasOwnProperty(prop) && target[prop]) {
                  src[prop] = target[prop];
              }
          }
    
          return src;
        };
    
        var getHeaders = function (selector, scope) {
            var ret = [];
            var target = document.querySelectorAll(scope);
    
            Array.prototype.forEach.call(target, function (elem) {
                var elems = elem.querySelectorAll(selector);
                ret = ret.concat(Array.prototype.slice.call(elems));
            });
    
            return ret;
        };
    
        var getLevel = function (header) {
            if (typeof header !== 'string') {
                return 0;
            }
    
            var decs = header.match(/\d/g);
            return decs ? Math.min.apply(null, decs) : 1;
        };
    
        var createList = function (wrapper, count) {
            while (count--) {
                wrapper = wrapper.appendChild(
                    document.createElement('ol')
                );
    
                if (count) {
                    wrapper = wrapper.appendChild(
                        document.createElement('li')
                    );
                }
            }
    
            return wrapper;
        };
    
        var jumpBack = function (currentWrapper, offset) {
            while (offset--) {
                currentWrapper = currentWrapper.parentElement;
            }
    
            return currentWrapper;
        };
    
        var setAttrs = function (overwrite, prefix) {
            return function (src, target, index) {
                var content = src.textContent;
                var pre = prefix + '-' + index;
                target.textContent = content;
    
                var id = overwrite ? pre : (src.id || pre);
    
                id = encodeURIComponent(id);
    
                src.id = id;
                target.href = '#' + id;
            };
        };
    
        var buildTOC = function (options) {
            var selector = options.selector;
            var scope = options.scope;
    
            var ret = document.createElement('ol');
            var wrapper = ret;
            var lastLi = null;
    
            var _setAttrs = setAttrs(options.overwrite, options.prefix);
    
            getHeaders(selector, scope).reduce(function (prev, cur, index) {
                var currentLevel = getLevel(cur.tagName);
                var offset = currentLevel - prev;
    
                if (offset > 0) {
                    wrapper = createList(lastLi, offset);
                }
    
                if (offset < 0) {
                    wrapper = jumpBack(wrapper, -offset * 2);
                }
    
                wrapper = wrapper || ret;
    
                var li = document.createElement('li');
                var a = document.createElement('a');
    
                _setAttrs(cur, a, index);
    
                wrapper.appendChild(li).appendChild(a);
    
                lastLi = li;
    
                return currentLevel;
            }, getLevel(selector));
    
            return ret;
        };
    
        var initTOC = function (options) {
            var defaultOpts = {
                selector: 'h1, h2, h3, h4, h5, h6',
                scope: 'body',
                overwrite: false,
                prefix: 'toc'
            };
    
            options = extendObj(defaultOpts, options);
    
            var selector = options.selector;
    
            if (typeof selector !== 'string') {
                throw new TypeError('selector must be a string');
            }
    
            if (!selector.match(/^(?:h[1-6],?\s*)+$/g)) {
                throw new TypeError('selector must contains only h1-6');
            }
    
            return buildTOC(options);
          };
    
        // Generating the TOC 
        var container = document.querySelector('#toc');
    
        var toc = initTOC({
            selector: 'h1, h2',
            scope: '#doc-content', // you can specify here a tag where to look at 
            overwrite: false,
            prefix: 'toc'
        });
    
        container.appendChild(toc);
      }
    

    【讨论】:

      【解决方案2】:

      所以这是我的最终解决方案。 ng-model 部分用于在文本更新时更新标题。

      .directive('tableOfContents', function(){
          return {
              restrict:'A',
              require:'?ngModel',
              link : function(scope, elm, attrs,ngModel) {
                  function updateHeadlines() {
                      scope.headlines=[];
                      angular.forEach(elm[0].querySelectorAll('h1,h2,h3,h4,h5,h6'), function(e){
                          scope.headlines.push({ 
                              level: e.tagName[1], 
                              label: angular.element(e).text(),
                              element: e
                          });
                      });
                  }
                  // avoid memoryleaks from dom references
                  scope.$on('$destroy',function(){
                      scope.headlines=[];
                  });
                  // scroll to one of the headlines
                  scope.scrollTo=function(headline){
                      headline.element.scrollIntoView();
                  }
                  // when the html updates whe update the headlines
                  ngModel.$render = updateHeadlines;
                  updateHeadlines();
              }
          }
      })
      

      用法:

      <a ng-repeat="headline in headlines" ng-click="scrollTo(headline)">{{headline.label}}</a>
      <div table-of-contents ng-model="html">{{html}}</div>
      

      【讨论】:

        【解决方案3】:

        您可以使用

        找到具有“.text”类的元素
        angular.element(document.querySelectorAll(".text"));
        

        【讨论】:

        • 它没有完全回答我的问题,但是 document.querySelectorAll 把我带到了正确的道路上。我可以做 element.querySelectorAll('h1,h2,h3,h4,h5,h6') 来解决我的问题。
        【解决方案4】:

        Angular jqLit​​e 是您的选择,但不要忘记 .find() 仅限于按标签名称查找。

        如果有帮助,请检查这些 SO SO 答案。

        【讨论】:

        • 不回答问题,因为它不处理检索标题的顺序。
        【解决方案5】:

        你很幸运,应该能够使用 angular.element 几乎与使用 jQuery 完全相同的方式。

        angular.element('h1') 将找到 h1 元素。

        【讨论】:

        • 问题是:我可以找到所有的标题,但是如何确定它们的正确顺序?
        猜你喜欢
        • 2015-04-12
        • 1970-01-01
        • 1970-01-01
        • 2016-08-22
        • 1970-01-01
        • 2014-12-23
        • 1970-01-01
        • 2021-06-14
        • 1970-01-01
        相关资源
        最近更新 更多