将缩进文本列表转换为HTML列表(jQuery)

时间:2013-12-14 01:30:12

标签: javascript jquery html html-lists

我正在尝试创建一个jQuery脚本,它将任意长度和深度的缩进文本列表转换为格式正确的HTML列表。我将运行此脚本的列表是目录的简单树结构。在树结构中,文件夹在文件夹名称后面用分号表示(文件没有结尾标点符号)。鉴于此,我想在适当的位置附加<span class="folder"></span><span class="file"></span>

我发现生成大部分结构相当容易,但我似乎无法得到递归(我怀疑是必要的)以确保标签正确嵌套。将要实现它的页面将包括Bootstrap的最新版本(即3.0.3),因此可以随意使用它的任何功能。我有大约二十几个(通常是流产的)代码片段,我已经尝试过,或者我正在尝试调整以产生所需的结果。我没有发布大量(可能无用的)代码,而是创建了一个JSFiddle,其基本形式将用于输入/输出,一些jQuery,以及一个示例列表和一些外部库加载。

非常感谢任何帮助或建议。

3 个答案:

答案 0 :(得分:2)

试试这个。我将它复制到你的小提琴中,似乎有效。

var indentedToHtmlList = function indentedToHtmlList (text, indentChar, folderChar, listType, showIcons) {
  indentChar = indentChar || '\t';
  folderChar = folderChar || ':';
  listType = listType || 'ul';
  showIcons = !!showIcons;

  var lastDepth,
      lines = text.split(/\r?\n/),
      output = '<' + listType + '>\n',
      depthCounter = new RegExp('^(' + indentChar + '*)(.*)');

  for (var i = 0; i < lines.length; i++) {
    var splitted = lines[i].match(depthCounter),
        indentStr = splitted[1],
        fileName = splitted[2],
        currentDepth = (indentStr === undefined) ? 0 : (indentStr.length / indentChar.length),
        isFolder = (fileName.charAt(fileName.length - 1) === folderChar);

    if (isFolder) {
      fileName = fileName.substring(0, fileName.length -1);
    }

    if (lastDepth === currentDepth) {
      output += '</li>\n';
    } else if (lastDepth > currentDepth) {
      while (lastDepth > currentDepth) {
        output += '</li>\n</' + listType + '>\n</li>\n';
        lastDepth--;
      }
    } else if (lastDepth < currentDepth) {
      output += '\n<' + listType + '>\n';
    }

    output += '<li>';
    if (showIcons) {
      output += '<span class=" glyphicon glyphicon-' +
      (isFolder ? 'folder-open' : 'file') +
      '"></span> ';
    }
    output += fileName;

    lastDepth = currentDepth;
  }

  while (lastDepth >= 0) {
    output += '\n</li>\n</' + listType + '>';
    lastDepth--;
  }

  return output;
};

答案 1 :(得分:1)

您可以使用跨度和类来表示文件和文件夹,但是您应该考虑使用ul和li元素,它们是为此而构建的。

整个列表应该包含在ul元素中。顶级列表中的每个条目都应在主元素内创建一个li元素。如果元素是一个文件夹,那么它还应附加另一个ul。这是您需要递归以允许正确嵌套的地方。

但是,如果你打算使用缩进(没有双关语缩进),那么制表符和/或空格解析本身就是一个问题,我在这个答案中没有解决。为了这个例子,我将假装你有一个魔术函数将文本转换为一个名为MyList的解析列表,属于一个文件夹的文件就是每个列表元素的第一个分号之后的所有文件。

var turnTextIntoList=function(AText) {
    //magic stuff;
    return SomeList;
};

var populateList=function(AList) {
    var mainelement=jQuery('<ul></ul>');
    for(element in AList) { 
        var the_li=jQuery('<li></li>');
        if(element.indexOf(';')!=-1) {
             the_li.append('<span class="file">'+element+'</span>');
        } else {
              var thefolder=element.split(';')
              the_li.append('<span class="folder">'+thefolder[0]+'</span>');
              the_li.append(populateList(turnTextIntoList(thefolder[1])));
        } 
        mainelement.append(the_li);
    }
    return mainelement;
};

var MyList=turnTextIntoList(MyText);
jQuery('#targetdiv').append(populateList(MyList));

看,递归部分是你做的地方

the_li.append(populateList(turnTextIntoList(thefolder[1])));

将继续钻进嵌套级别,直到它到达文件,以便它可以开始返回。

答案 2 :(得分:0)

似乎某人已经创建了script来执行此操作。不幸的是,该脚本是用CoffeeScript编写的,而不是JavaScript。但是,有一些在线转换器可以从CoffeeScript转换为JavaScript。感谢@charlietfl提供了一个工作转换器的链接, supra

这是转换后的工作代码:

var bind, blank, convert, index, li, lineToMap, linesToMaps, parse, parseTuples, ptAccum, runConvert, tabCount, ul, ulEnd;

convert = function(text) {
  return parse(text.split('\n'));
};

li = function(t) {
  var html;
  html = "<li>" + t['line'] + "</li>";
  ptAccum.push(html);
  return html;
};

ul = function(t) {
  return ptAccum.push("<ul>" + (li(t)));
};

ulEnd = function() {
  return ptAccum.push("</ul>");
};

ptAccum = [];

index = 0;

parse = function(lines) {
  var ts;
  ts = linesToMaps(lines);
  ptAccum = ["<ul>"];
  index = 0;
  parseTuples(ts, 0);
  ulEnd();
  return ptAccum.join("\n");
};

parseTuples = function(tuples, level) {
  var stop, _p, _results;
  stop = false;
  _p = function() {
    var curLevel, t;
    t = tuples[index];
    curLevel = t['level'];
    index++;
    if (curLevel === level) {
      return li(t);
    } else if (curLevel < level) {
      index--;
      return stop = true;
    } else {
      ul(t);
      parseTuples(tuples, level + 1);
      return ulEnd();
    }
  };
  _results = [];
  while (!stop && index < tuples.length) {
    _results.push(_p());
  }
  return _results;
};

tabCount = function(line) {
  var c, count, i, inc, isTab, tc;
  tc = 0;
  c = '\t';
  count = 0;
  if (line) {
    count = line.length;
  }
  i = 0;
  isTab = function() {
    return c === '\t';
  };
  inc = function() {
    c = line.charAt(i);
    if (isTab()) {
      tc++;
    }
    return i++;
  };
  while (isTab() && i < count) {
    inc();
  }
  return tc;
};

lineToMap = function(line) {
  return {
    line: line,
    level: tabCount(line)
  };
};

blank = function(line) {
  return !line || line.length === 0 || line.match(/^ *$/);
};

linesToMaps = function(lines) {
  var line, _i, _len, _results;
  _results = [];
  for (_i = 0, _len = lines.length; _i < _len; _i++) {
    line = lines[_i];
    if (!(blank(line))) {
      _results.push(lineToMap(line));
    }
  }
  return _results;
};

runConvert = function() {
  var result;
  result = convert($('#textarea-plain-text').val());
  $('#textarea-converted-text').val(result);
  return $('#div-converted-text').html(result);
};

bind = function() {
  return $('#list-conversion-button').click(runConvert);
};

$(bind);

JSFiddle