jQuery 输入时的可排序更新输出
jQuery Sortable update output on input
我们使用 jQuery Sortable 库来进行类似于 WordPress 的动态菜单管理。并且多级管理。
<ol id="my-nav">
<li data-id="unique-id-here" data-label="">
<span>My Label</span>
<input type="text" class="label-change">
<ol><!-- PLACEHOLDER FOR SUBMENU --></ol>
</li>
</ol>
<textarea id="output"></textarea>
我们想使用继承的输入字段更新菜单标签。所以我们做了如下的事情:
$('body').on('keyup change', '.label-change', function () {
var this_menu_label_field = $(this);
var this_field_val = this_menu_label_field.val();
var this_menu_nav = this_menu_label_field.parents('li');
// Update the text string inside the <li>
this_menu_nav.find('span').html(this_field_val);
// Update the data-label attribute
this_menu_nav.attr('data-label', this_field_val).sortable('refresh');
var serialized_data = menu_container.sortable('serialize').get();
$('#output').val(JSON.stringify(serialized_data));
});
代码正在更新相应 <li>
中 <span>
中的字符串,并且还会更改 data-label
。但不幸的是,它只更新了进入 #output
文本区域的第一次击键。
例如:如果我们键入 "Whatever",它可能需要 "W" 或 "Wha"。并且没有进一步的击键更新到 #output
。但是 <span>
和 data-label
中的更新总是工作正常。
我们非常需要这种功能。但是我们怎样才能做到这一点呢?
演示版Fiddle
https://jsfiddle.net/mayeenulislam/Lsrgu0qy/38/
- 什么都不做,查看
<textarea>
中的值
- 什么都不做,只需在文本框中输入
- 现在再次查看
<textarea>
中的值
我 fiddle 研究了这个并且能够让它工作。在此处检查我的分叉 fiddle:jsfiddle.net/qf7n89oe。
我做的唯一改变是替换
this_menu_nav.attr('data-label', this_field_val).sortable('refresh');
和
this_menu_nav.data('label', this_field_val).sortable('refresh');
似乎使用 attr()
会导致一些缓存问题,所以我将其替换为 data()
似乎没有这个问题。
jQuery Sortable 将数据存储在数据对象中。所以它不处理 data-* 属性。如果您想使用 jQuery 数据对象,您可以使用 Kodos Johnson 回答。另一种方法是拥有自己的序列化函数定义。看这个例子:
/**
* ---------------------------------------
* OUR CODE STARTS HERE
* ---------------------------------------
*/
jQuery(document).ready(function($) {
var menu_container = $('#my-nav');
var menu_data_field = $('#output');
var sortable = menu_container.sortable({
delay: 500,
group: 'serialization',
onDrop: function($item, container, _super) {
var data = sortable.sortable('serialize').get();
var jsonString = JSON.stringify(data);
menu_data_field.val(jsonString);
_super($item, container);
},
serialize: function($parent, $children, parentIsContainer) {
let result = $parent.map(function(e) {
let attr = {};
$.each(this.attributes, function() {
const name = this.name.replace("data-", "");
attr[name] = this.value;
});
return attr;
});
if (parentIsContainer)
return [$children]
else if ($children[0]) {
result[0].children = $children
}
delete result.subContainers
delete result.sortable
return result.get();
},
});
$('body').on('keyup', '.label-change', function() {
var this_menu_label_field = $(this);
var this_field_val = this_menu_label_field.val();
var this_menu_nav = this_menu_label_field.parent('li');
// Update the text string inside the <li>
this_menu_nav.find('> span').html(this_field_val);
// Update the data-label attribute
this_menu_nav.attr('data-label', this_field_val).sortable('refresh');
var serialized_data = menu_container.sortable('serialize').get();
console.log(serialized_data);
$('#output').val(JSON.stringify(serialized_data));
});
});
body.dragging,
body.dragging * {
cursor: move !important;
}
input[type="text"] {
border-color: red;
}
#my-nav {
padding-left: 0;
list-style-type: none;
overflow: hidden;
}
#my-nav ol {
padding-left: 0;
margin-left: 20px;
list-style-type: none;
}
#my-nav li {
padding: 5px 10px;
background-color: #f8f8f8;
color: #333;
border: 1px solid #999;
border-radius: 3px;
margin-bottom: 10px;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
cursor: move;
}
#my-nav li.placeholder {
position: relative;
border-style: dashed;
background-color: #ededed;
min-height: 34px;
}
#my-nav li.placeholder:before {
position: absolute;
}
#my-nav li.dragged {
position: absolute;
top: 0;
opacity: .5;
z-index: 2000;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.5);
}
#my-nav li.dragged i.icon-move {
color: var(--primary);
}
#my-nav li.highlight {
background: gray;
color: lightgray;
}
#my-nav li:first-of-type {
margin-top: 10px;
}
#my-nav i.icon-move {
cursor: pointer;
color: #999;
padding: 5px;
}
.dd {
position: relative;
display: block;
margin: 0;
padding: 0;
max-width: 600px;
list-style: none;
font-size: 13px;
line-height: 20px;
}
.dd-list {
display: block;
position: relative;
margin: 0;
padding: 0;
list-style: none;
}
.dd-list .dd-list {
padding-left: 30px;
}
.dd-item,
.dd-empty,
.dd-placeholder {
display: block;
position: relative;
margin: 0;
padding: 0;
min-height: 20px;
font-size: 13px;
line-height: 20px;
}
.dd-handle {
display: block;
height: 30px;
margin: 5px 0;
padding: 5px 10px;
color: #333;
text-decoration: none;
font-weight: bold;
border: 1px solid #ccc;
background: #fafafa;
border-radius: 3px;
box-sizing: border-box;
}
.dd-handle:hover {
color: #2ea8e5;
background: #fff;
}
.dd-item>button {
position: relative;
cursor: pointer;
float: left;
width: 25px;
height: 20px;
margin: 5px 0;
padding: 0;
text-indent: 100%;
white-space: nowrap;
overflow: hidden;
border: 0;
background: transparent;
font-size: 12px;
line-height: 1;
text-align: center;
font-weight: bold;
}
.dd-item>button:before {
display: block;
position: absolute;
width: 100%;
text-align: center;
text-indent: 0;
}
.dd-item>button.dd-expand:before {
content: '+';
}
.dd-item>button.dd-collapse:before {
content: '-';
}
.dd-expand {
display: none;
}
.dd-collapsed .dd-list,
.dd-collapsed .dd-collapse {
display: none;
}
.dd-collapsed .dd-expand {
display: block;
}
.dd-empty,
.dd-placeholder {
margin: 5px 0;
padding: 0;
min-height: 30px;
background: #f2fbff;
border: 1px dashed #b6bcbf;
box-sizing: border-box;
-moz-box-sizing: border-box;
}
.dd-empty {
border: 1px dashed #bbb;
min-height: 100px;
background-color: #e5e5e5;
background-size: 60px 60px;
background-position: 0 0, 30px 30px;
}
.dd-dragel {
position: absolute;
pointer-events: none;
z-index: 9999;
}
.dd-dragel>.dd-item .dd-handle {
margin-top: 0;
}
.dd-dragel .dd-handle {
box-shadow: 2px 4px 6px 0 rgba(0, 0, 0, 0.1);
}
.dd-nochildren .dd-placeholder {
display: none;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-sortable/0.9.13/jquery-sortable-min.js"></script>
<ol id="my-nav">
<li data-id="unique-id-here" data-label="My Label">
<span>My Label</span>
<input type="text" class="label-change form-control" placeholder="Type here to see the impact">
<ol>
<li data-id="unique-id-here" data-label="My Sub Label">
<span>My Sub Label</span>
<input type="text" class="label-change form-control" placeholder="Type here to see the impact">
<ol>
<!-- PLACEHOLDER FOR SUBMENU -->
</ol>
</li>
</ol>
</li>
</ol>
<h4>OUTPUT HERE</h4>
<textarea id="output" class="form-control" placeholder="">[[{"label":"My Label","id":"unique-id-here","children":[[{"label":"My Sub Label","id":"unique-id-here","children":[[]]}]]}]]</textarea>
我们使用 jQuery Sortable 库来进行类似于 WordPress 的动态菜单管理。并且多级管理。
<ol id="my-nav">
<li data-id="unique-id-here" data-label="">
<span>My Label</span>
<input type="text" class="label-change">
<ol><!-- PLACEHOLDER FOR SUBMENU --></ol>
</li>
</ol>
<textarea id="output"></textarea>
我们想使用继承的输入字段更新菜单标签。所以我们做了如下的事情:
$('body').on('keyup change', '.label-change', function () {
var this_menu_label_field = $(this);
var this_field_val = this_menu_label_field.val();
var this_menu_nav = this_menu_label_field.parents('li');
// Update the text string inside the <li>
this_menu_nav.find('span').html(this_field_val);
// Update the data-label attribute
this_menu_nav.attr('data-label', this_field_val).sortable('refresh');
var serialized_data = menu_container.sortable('serialize').get();
$('#output').val(JSON.stringify(serialized_data));
});
代码正在更新相应 <li>
中 <span>
中的字符串,并且还会更改 data-label
。但不幸的是,它只更新了进入 #output
文本区域的第一次击键。
例如:如果我们键入 "Whatever",它可能需要 "W" 或 "Wha"。并且没有进一步的击键更新到 #output
。但是 <span>
和 data-label
中的更新总是工作正常。
我们非常需要这种功能。但是我们怎样才能做到这一点呢?
演示版Fiddle
https://jsfiddle.net/mayeenulislam/Lsrgu0qy/38/
- 什么都不做,查看
<textarea>
中的值
- 什么都不做,只需在文本框中输入
- 现在再次查看
<textarea>
中的值
我 fiddle 研究了这个并且能够让它工作。在此处检查我的分叉 fiddle:jsfiddle.net/qf7n89oe。
我做的唯一改变是替换
this_menu_nav.attr('data-label', this_field_val).sortable('refresh');
和
this_menu_nav.data('label', this_field_val).sortable('refresh');
似乎使用 attr()
会导致一些缓存问题,所以我将其替换为 data()
似乎没有这个问题。
jQuery Sortable 将数据存储在数据对象中。所以它不处理 data-* 属性。如果您想使用 jQuery 数据对象,您可以使用 Kodos Johnson 回答。另一种方法是拥有自己的序列化函数定义。看这个例子:
/**
* ---------------------------------------
* OUR CODE STARTS HERE
* ---------------------------------------
*/
jQuery(document).ready(function($) {
var menu_container = $('#my-nav');
var menu_data_field = $('#output');
var sortable = menu_container.sortable({
delay: 500,
group: 'serialization',
onDrop: function($item, container, _super) {
var data = sortable.sortable('serialize').get();
var jsonString = JSON.stringify(data);
menu_data_field.val(jsonString);
_super($item, container);
},
serialize: function($parent, $children, parentIsContainer) {
let result = $parent.map(function(e) {
let attr = {};
$.each(this.attributes, function() {
const name = this.name.replace("data-", "");
attr[name] = this.value;
});
return attr;
});
if (parentIsContainer)
return [$children]
else if ($children[0]) {
result[0].children = $children
}
delete result.subContainers
delete result.sortable
return result.get();
},
});
$('body').on('keyup', '.label-change', function() {
var this_menu_label_field = $(this);
var this_field_val = this_menu_label_field.val();
var this_menu_nav = this_menu_label_field.parent('li');
// Update the text string inside the <li>
this_menu_nav.find('> span').html(this_field_val);
// Update the data-label attribute
this_menu_nav.attr('data-label', this_field_val).sortable('refresh');
var serialized_data = menu_container.sortable('serialize').get();
console.log(serialized_data);
$('#output').val(JSON.stringify(serialized_data));
});
});
body.dragging,
body.dragging * {
cursor: move !important;
}
input[type="text"] {
border-color: red;
}
#my-nav {
padding-left: 0;
list-style-type: none;
overflow: hidden;
}
#my-nav ol {
padding-left: 0;
margin-left: 20px;
list-style-type: none;
}
#my-nav li {
padding: 5px 10px;
background-color: #f8f8f8;
color: #333;
border: 1px solid #999;
border-radius: 3px;
margin-bottom: 10px;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
cursor: move;
}
#my-nav li.placeholder {
position: relative;
border-style: dashed;
background-color: #ededed;
min-height: 34px;
}
#my-nav li.placeholder:before {
position: absolute;
}
#my-nav li.dragged {
position: absolute;
top: 0;
opacity: .5;
z-index: 2000;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.5);
}
#my-nav li.dragged i.icon-move {
color: var(--primary);
}
#my-nav li.highlight {
background: gray;
color: lightgray;
}
#my-nav li:first-of-type {
margin-top: 10px;
}
#my-nav i.icon-move {
cursor: pointer;
color: #999;
padding: 5px;
}
.dd {
position: relative;
display: block;
margin: 0;
padding: 0;
max-width: 600px;
list-style: none;
font-size: 13px;
line-height: 20px;
}
.dd-list {
display: block;
position: relative;
margin: 0;
padding: 0;
list-style: none;
}
.dd-list .dd-list {
padding-left: 30px;
}
.dd-item,
.dd-empty,
.dd-placeholder {
display: block;
position: relative;
margin: 0;
padding: 0;
min-height: 20px;
font-size: 13px;
line-height: 20px;
}
.dd-handle {
display: block;
height: 30px;
margin: 5px 0;
padding: 5px 10px;
color: #333;
text-decoration: none;
font-weight: bold;
border: 1px solid #ccc;
background: #fafafa;
border-radius: 3px;
box-sizing: border-box;
}
.dd-handle:hover {
color: #2ea8e5;
background: #fff;
}
.dd-item>button {
position: relative;
cursor: pointer;
float: left;
width: 25px;
height: 20px;
margin: 5px 0;
padding: 0;
text-indent: 100%;
white-space: nowrap;
overflow: hidden;
border: 0;
background: transparent;
font-size: 12px;
line-height: 1;
text-align: center;
font-weight: bold;
}
.dd-item>button:before {
display: block;
position: absolute;
width: 100%;
text-align: center;
text-indent: 0;
}
.dd-item>button.dd-expand:before {
content: '+';
}
.dd-item>button.dd-collapse:before {
content: '-';
}
.dd-expand {
display: none;
}
.dd-collapsed .dd-list,
.dd-collapsed .dd-collapse {
display: none;
}
.dd-collapsed .dd-expand {
display: block;
}
.dd-empty,
.dd-placeholder {
margin: 5px 0;
padding: 0;
min-height: 30px;
background: #f2fbff;
border: 1px dashed #b6bcbf;
box-sizing: border-box;
-moz-box-sizing: border-box;
}
.dd-empty {
border: 1px dashed #bbb;
min-height: 100px;
background-color: #e5e5e5;
background-size: 60px 60px;
background-position: 0 0, 30px 30px;
}
.dd-dragel {
position: absolute;
pointer-events: none;
z-index: 9999;
}
.dd-dragel>.dd-item .dd-handle {
margin-top: 0;
}
.dd-dragel .dd-handle {
box-shadow: 2px 4px 6px 0 rgba(0, 0, 0, 0.1);
}
.dd-nochildren .dd-placeholder {
display: none;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-sortable/0.9.13/jquery-sortable-min.js"></script>
<ol id="my-nav">
<li data-id="unique-id-here" data-label="My Label">
<span>My Label</span>
<input type="text" class="label-change form-control" placeholder="Type here to see the impact">
<ol>
<li data-id="unique-id-here" data-label="My Sub Label">
<span>My Sub Label</span>
<input type="text" class="label-change form-control" placeholder="Type here to see the impact">
<ol>
<!-- PLACEHOLDER FOR SUBMENU -->
</ol>
</li>
</ol>
</li>
</ol>
<h4>OUTPUT HERE</h4>
<textarea id="output" class="form-control" placeholder="">[[{"label":"My Label","id":"unique-id-here","children":[[{"label":"My Sub Label","id":"unique-id-here","children":[[]]}]]}]]</textarea>