在克隆行上显示来自Textarea的字符限制

时间:2018-01-17 08:38:34

标签: javascript jquery html

[编辑]修改原始问题以提高清晰度 此表单的此部分允许用户根据需要添加任意数量的行,并且可以正常工作。我的问题是我无法弄清楚如何使字符计数在克隆行的textarea上工作。 (感谢zer00ne提供了所有非常棒的帮助。非常简洁的编码!与我以前的Javascript代码相比,还提供了一小部分行的jQuery字符数。) 这是一个小提琴:https://jsfiddle.net/RationalRabbit/2vmqk26b/4/

CSS

textarea,
output,
button {font-size:inherit;}
output:nth-of-type(n+2) {margin-left:3px;}
.msg {border:none;}
.clearfix:before,
.clearfix:after {display:table; content: "";}
.clearfix:after {clear:both;}
.RowDeleteButton {float:right; font-family:arial, sansserif; font-size:14px; display:inline-block; text-decoration:none; color:#AC0F0F; font-weight:900; cursor:pointer;}
.RowDeleteButton:hover, .RowDeleteButton:focus {color:#FF0000;}

HTML

    <fieldset>
    <div class="parent-group">
        <div class="form-group">
            <input id="Name" name="Name[]" size="20" value="" />
            <input type="checkbox" id="HM" name="HM[]" value="X" />
            <textarea class="txt" id="TA" rows="1" cols="30" name="TA[]" maxlength="100"></textarea>
            <input class='msg' name="Output" id="Output" size="3" readonly value="100" />
            <input type="text" name="Location[]" id="Location" size="30" value="" />
            <div class="form-group RowDelete">
                <a class="RowDeleteButton" id="DeleteRow" href="javascript:void(0)"> X </a>
            </div>
            <div class="Clear"></div>
        </div>
    </div>
    <div class="clearfix"></div>
    <div id="container"></div>
    <div class="form-group">
        <a id="AddRow" href="javascript:void(0)"><span style="color:#0F61AC;">Add Row</span></a>
    </div>
</fieldset>

的jQuery

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript">
    // onkeyup invoke charCount
    $('.txt').on('keyup', charCount);

    // onclick...
    $('#DeleteRow').closest('.form-group').hide();
    $('#AddRow').on('click', function (e)
    {
        var len = $('.child-border').length;
        $('.parent-group').clone(true, false).find(':input').each(function (idx, ele)
        {
            ele.name = ele.name + len;
            ele.id = ele.id + len;
            ele.value = '';
        }).end().find('.form-group').toggle(true).end()
            .toggleClass('parent-group child-border').hide()
            .appendTo('#container').slideDown('slow');
    });
    $('#container').on('click', '[id^=DeleteRow]', function(e)
    {
        var jsonData = $(this).closest('.child-border, .parent-group')
        .find(':input:not(button)').get()
        .reduce(function (acc, ele)
        {
            acc[ele.name || ele.id] = ele.value;
            return acc;
        }, {});
        $(this).closest('.child-border, .parent-group').remove();
        console.log(jsonData);
    });

    function charCount(e)
    {
        // Get the text
        var chars = this.value;
        // Get maxlength as a number
        var charMax = Number(this.getAttribute('maxlength'));
        // Number of chars typed
        var charDone = chars.length;
        // Chars remaining is 100 - chars typed
        var charToGo = charMax - charDone;
        // Display chars remaining
        $(this).next('.msg').val(charToGo);
    }
</script>

2 个答案:

答案 0 :(得分:2)

2 nd 更新

我已经在 Update 1 中说明了问题:

$('.parent-group').clone(true, false).find(':input')

第二个参数应为 true

这将允许clone()方法在克隆上保留已注册的事件。注意我在第一次更新上说了同样的话,但我没有更改演示2 中的代码。

演示3 是对当前更新的OP代码的重大修改。它功能齐全,可以像演示2 一样在克隆上保留注册事件。添加的功能包括:local/sessionStorage,将数据发送到实时测试服务器,并显示服务器响应。

演示4 是OP代码和一个简单的改变...想要猜测可能是什么?

<小时/>

1 st 更新

克隆时,使用第一个参数确定克隆是否保留原始节点的已注册事件处理程序。

$('#original').clone(true, true);

参见演示2

<小时/> 不确定你的意思是“一排”。与OP代码相比,该演示简化了。我添加了自动高度而不是克隆行。

详情在演示

中发表

演示1

// On keyup...
$('#txt').on('keyup', function() {
  // Get the text
  var chars = this.value;
  // if there are any linebreaks...
  if (chars.match(/\n/g)) {
    /* The value of rows attribute equals
    || the number of line breaks +2
    */
    this.rows = chars.match(/\n/g).length + 2;
  }
  /* Get value of maxlength attribute convert
  || to number
  */
  var charMax = Number(this.getAttribute('maxlength'));
  // Number of chars typed
  var charDone = chars.length;
  // Chars remaining is 100 - chars typed
  var charToGo = charMax - charDone;
  // Display chars remaining
  $('#msg').val(charToGo + ' characters remaining');
});
<textarea id='txt' rows="1" cols="30" maxlength="100"></textarea><br>
<output id='msg' for='txt'></output>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

演示2

// onkeyup invoke charCount
$('.txt').on('keyup', charCount);

// onclick...
$('button').on('click', function() {

  /* clone the first .txt and .msg
  || true: keep registered events
  || false: copy content
  || set .val('') to blank
  || add to fieldset
  */
  $('.txt:first').clone(true, true).val('').appendTo('fieldset');
  $('.msg:first').clone(true, true).val('').appendTo('fieldset');
});


function charCount(e) {
  // Get the text
  var chars = this.value;
  // Get maxlength as a number
  var charMax = Number(this.getAttribute('maxlength'));
  // Number of chars typed
  var charDone = chars.length;
  // Chars remaining is 100 - chars typed
  var charToGo = charMax - charDone;
  // Display chars remaining
  $(this).next('.msg').val(charToGo + ' characters remaining');
}
textarea,
output,
button {
  font-size: inherit
}

output {
  display: inline-block;
  vertical-align: top;
}

output:nth-of-type(n+2) {
  margin-left: 3px
}

button {
  margin-left: 90%
}
<button type='button'>Add</button>
<fieldset>
  <textarea class='txt' rows="1" cols="30" maxlength="100"></textarea>
  <output class='msg'></output>
</fieldset>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

演示3

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1, user-scalable=no">
  <title></title>
  <style>
    textarea,
    output,
    button {
      font-size: inherit;
    }
    
    output:nth-of-type(n+2) {
      margin-left: 3px;
    }
    
    .msg {
      border: none;
    }
    
    .clearfix:before,
    .clearfix:after {
      display: table;
      content: "";
    }
    
    .clearfix:after {
      clear: both;
    }
    
    .del {
      float: right;
      margin-top: .5px;
      font-family: arial, sansserif;
      font-size: 14px;
      display: inline-block;
      text-decoration: none;
      color: #AC0F0F;
      font-weight: 900;
      cursor: pointer;
    }
    
    .del:hover,
    .del:focus {
      color: #FF0000;
    }
    
    main {
      display: table;
    }
  </style>
  <script>
    /* Global counter variable */
    var counter = 0;
  </script>
</head>

<body>
  <main class="main-group">
    <!--This form submits to a live test server, the [target] attribute value
   is that of an iframe's name attribute. By targeting the iframe the
   form can display the test server's response in the iframe
   -->
    <form id='main' action='https://httpbin.org/post' method='post' target='response'>
      <!--The original fieldset is cloned-->
      <fieldset id='set' class="form-group">
        <button id="del" class='ui del' type='button'> X </button>
        <input id='ID' name='ID' class='data' type='hidden'>
        <input id="name" name="name" class='data name' size="25">
        <input id="chx" name="chx" class='data chx' type="checkbox" value="X">
        <br>
        <textarea id="txt" name="txt" class='data txt' rows="1" cols="30" maxlength="100"></textarea>
        <output class='ui msg'></output>
        <br>
        <input id="loc" name="loc" class='data loc' size="30">
      </fieldset>
    </form>
    <nav class="btn-group">
      <a id="add" href="#/" class='ui'> <b style="color:#0F61AC;">Add Row</b> </a>
      <!--This submit button must use the [form] attribute with the ID of the
     form to be accociated with-->
      <input type='submit' form='main' class='ui'>
    </nav>
    <iframe src='about:blank' name='response' class='ui'></iframe>
  </main>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
  <script>
    // Click...
    $('#add').on('click', function(e) {

      // Increment counter
      counter++;

      /* clone fieldset#set  true: clone descendants / 
      || TRUE: KEEP EVENTS ON CLONES
      || Gather all of the .data in clone then on each clone...
      */
      var dupe = $('#set').clone(true, true);
      dupe[0].id = 'set' + counter;
      dupe.find('.data').each(function(idx, ele) {
        // Set all .data with name and id, the counter suffix makes them unique
        ele.name = this.name + counter;
        ele.id = this.id + counter;
        // Clear all data in each .data
        ele.value = '';
        // Cool animation and append clone to form#main	
      }).end().find('.form-group').toggle(true).end().hide().appendTo('#main').slideDown('slow');
      // Clear .ui of data
      dupe.find('output').val('');
      dupe.find(':checkbox').prop('checked', false);
    });

    // Clicking any button.del... 
    $('.del').on('click', function(e) {
      // Define arrays
      var jsonData = [];
      var JSONKeys = [];
      // This collects all accossiated .data of ,del
      var dataRow = $(this).nextAll('.data').toArray();
      // This map() will create an object literal and add it to an array
      jsonData = dataRow.map(function(data, idx) {
        var D = {};
        D.k = data.id;
        D.v = data.value;
        return D;
      });
      console.log(jsonData);
      // Proceedure to timestamp data
      var stamp = new Date();
      var jKey = stamp.toJSON();
      // Fill an array of keys for future reference
      JSONKeys.push(jKey);
      /* Store JSON data in sessionStorage (can be localStorage also) */
      setData(jKey, jsonData);
      // Save an index of the jsondata
      setData('JSONKeys', jKey);
      // if there's only one fieldset, reset the form if user tries to delete it
      if ($('fieldset').is(':only-child')) {
        $('#main')[0].reset();
      } else {
        // Remove fieldset
        $(this).parent('.form-group').remove();
      }
    });


    // onkeyup invoke charCount
    $('.txt').on('keyup', charCount);

    function charCount(e) {
      // Get the text
      var chars = this.value;
      // Get maxlength as a number
      var charMax = Number(this.getAttribute('maxlength'));
      // Number of chars typed
      var charDone = chars.length;
      // Chars remaining is 100 - chars typed
      var charToGo = charMax - charDone;
      // Display chars remaining
      $(this).next('.msg').val(charToGo);
    }

    function setData(dataKey, dataVal) {
      sessionStorage.setItem(dataKey, JSON.stringify(dataVal));
    }

    function getData(dataKey) {
      return JSON.parse(sessionStorage.getItem(dataKey));
    }
  </script>
</body>

</html>

演示4

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1, user-scalable=no">
  <title></title>

</head>

<body>


  <fieldset>
    <div class="parent-group">
      <div class="form-group">
        <input id="Name" name="Name[]" size="20" value="" />
        <input type="checkbox" id="HM" name="HM[]" value="X" />
        <textarea class="txt" id="TA" rows="1" cols="30" name="TA[]" maxlength="100"></textarea>
        <input class='msg' name="Output" id="Output" size="3" readonly value="100" />
        <input type="text" name="Location[]" id="Location" size="30" value="" />
        <div class="form-group RowDelete">
          <a class="RowDeleteButton del" href="javascript:void(0)"> X </a>
        </div>
        <div class="Clear"></div>
      </div>
    </div>
    <div class="clearfix"></div>
    <div id="container"></div>
    <div class="form-group">
      <a id="AddRow" href="javascript:void(0)"><span style="color:#0F61AC;">Add Row</span></a>
    </div>
  </fieldset>


  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
  <script type="text/javascript">
    // onclick...
    $('#DeleteRow').closest('.form-group').hide();
    $('#AddRow').on('click', function(e) {
      var len = $('.child-border').length;
      $('.parent-group').clone(true, true).find(':input').each(function(idx, ele) {
          ele.name = ele.name + len;
          ele.id = ele.id + len;
          ele.value = '';
        }).end().find('.form-group').toggle(true).end()
        .toggleClass('parent-group child-border').hide()
        .appendTo('#container').slideDown('slow');
    });
    $('.del').on('click', function(e) {
      var jsonData = $(this).closest('.child-border, .parent-group')
        .find(':input:not(button)').get()
        .reduce(function(acc, ele) {
          acc[ele.name || ele.id] = ele.value;
          return acc;
        }, {});
      $(this).closest('.child-border, .parent-group').remove();
      console.log(jsonData);
    });

    function charCount(e) {
      // Get the text
      var chars = this.value;
      // Get maxlength as a number
      var charMax = Number(this.getAttribute('maxlength'));
      // Number of chars typed
      var charDone = chars.length;
      // Chars remaining is 100 - chars typed
      var charToGo = charMax - charDone;
      // Display chars remaining
      $(this).next('.msg').val(charToGo);
    }

    // onkeyup invoke charCount
    $('.txt').on('keyup', charCount);
  </script>

</body>

</html>

答案 1 :(得分:1)

使用zer00ne的演示4,这是我最终提出的。差异是:
1.使用regex将第一个工作行设置为数组插入数组。也使用括号指定唯一的行输入id 2.设置删除按钮,使其不出现在第一行 3.我的脚本设置最大行数(8) 4.用于保存行数的全局变量。如果用户删除集合中间的一行或多行,则重要,然后在下面添加行 5.设置复选框,以便将值保留在克隆的行中。 &#34;检查&#34;设为false。

PHP显示了我如何处理数组服务器端,以便我按数字顺序出现数组。这可能会以不同的方式处理,但对我有用。不使用LastArrayValue变量来处理最后一个数组值,而使用ChildCount变量(例如,实际上有多少行),如果你删除,例如,行2&amp;八分之三,你只有6行,但不允许添加额外的。此外,如果没有这些变量,当在中间删除行时,您将最终得到重复的数组键。当您有最大行数要处理时,这变得更加重要 https://jsfiddle.net/uyz2zjj6/

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1, user-scalable=no">
    <title></title>
    <style type="text/css">
        textarea,
        output,
        button {font-size:inherit;}
        output:nth-of-type(n+2) {margin-left:3px;}
        .msg {border:none;}
        .clearfix:before,
        .clearfix:after {display:table; content: "";}
        .clearfix:after {clear:both;}
        .RowDeleteButton {float:right; font-family:arial, sansserif; font-size:14px; display:inline-block; text-decoration:none; color:#AC0F0F; font-weight:900; cursor:pointer;}
        .RowDeleteButton:hover, .RowDeleteButton:focus {color:#FF0000;}
    </style>
</head>
<body>
<fieldset>
    <div class="parent-group">
        <div class="form-group">
            <input id="Name(0)" name="Name[0]" size="20" value="" />
            <input type="checkbox" id="HM(0)" name="HM[0]" value="X" />
            <textarea class="txt" id="TA(0)" rows="1" cols="30" name="TA[0]" maxlength="100"></textarea>
            <input class='msg' name="Output" id="Output(0)" size="3" readonly value="100" />
            <input type="text" name="Location[0]" id="Location(0)" size="30" value="" />
            <div class="form-group" style="display:inline-block;">
                <a class="RowDeleteButton del" id="DeleteRow" href="javascript:void(0)"> X </a>
            </div>
            <div class="Clear"></div>
        </div>
    </div>
    <div class="clearfix"></div>
    <div id="container"></div>
    <div class="form-group">
        <a id="AddRow" href="javascript:void(0)"><span style="color:#0F61AC;">Add Row</span></a>
    </div>
</fieldset>


<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript">
// onclick...
window.LastArrayValue = 0;
$('#DeleteRow').closest('.form-group').hide();
$('#AddRow').on('click', function(e)
{
    var ChildCount = $('.child-group').length;
    if(ChildCount == 7)
    {
        alert("Sorry, 8 is the maximum number of rows");
    }
    else
    {
        var len = window.LastArrayValue;
        window.LastArrayValue = len + 1;
        $('.parent-group').clone(true, true).find(':input').each(function(idx, ele)
        {
            var ename = ele.name;
            var eid    = ele.id
            var ArrayValue = len+1;
            ele.name = ename.replace(/(\[\/?[^\]]*\])/g, "["+ArrayValue+"]");
            ele.id    = eid.replace(/(\(\/?[^\]]*\))/g, "("+ArrayValue+")");
            if(ele.type == "checkbox"){ele.checked = false;}
            else{ele.value = '';}
        }).end().find('.form-group').toggle(true).end()
          .toggleClass('parent-group child-group').hide()
          .appendTo('#container').slideDown('slow');
    }
});
$('.del').on('click', function(e)
{
    var jsonData = $(this).closest('.child-group, .parent-group')
    .find(':input:not(button)').get()
    .reduce(function(acc, ele)
    {
        acc[ele.name || ele.id] = ele.value;
        return acc;
    }, {});
    $(this).closest('.child-group, .parent-group').remove();
    console.log(jsonData);
});

function charCount(e)
{
    // Get the text
    var chars = this.value;
    // Get maxlength as a number
    var charMax = Number(this.getAttribute('maxlength'));
    // Number of chars typed
    var charDone = chars.length;
    // Chars remaining is 100 - chars typed
    var charToGo = charMax - charDone;
    // Display chars remaining
    $(this).next('.msg').val(charToGo);
}

// onkeyup invoke charCount
$('.txt').on('keyup', charCount)
</script>
</body>
</html>

<强> PHP

// Get the last key number in the array in the event that rows were deleted
// TA is the only required field. If that field is not filled, the row is ignored
end($_POST['TA']);  // move the internal pointer to the end of the array
$key = key($_POST['TA']);  // fetches the key of the element pointed to by the internal pointer
$Ct = $key+1; // account for array start at 0
reset($_POST['TA']); // Reset the array back to the top for processing.
$j=0; // Count only if TA has a value (revaluate $Ct when complete for later use ($Ct = $j))
for($i=0;$i<$Ct;$i++)
{
   if(empty($_POST['TA'][$i])) // Empty may be that field is empty or there is no such row.
   {
    continue;
   }
   $Name[$j]     =  $_POST['Name'][$i];
   $HM[$j]       =  $_POST['HM'][$i];
   $TA[$j]       =  $_POST['TA'][$i];
   $Location[$j] =  $_POST['Location'][$i];
   $j++;
}
$Ct = $j; // $Ct now holds the number of records and those records are in numerical order, 1-8