jqueryui Add sortable component dynamically - javascript

Hi i'm having a problem creating a dynamically sortable list, the idea is have a button to create a list, the list has some option like add new item to itself or drag them between lists, i have a main list like that:
TL;DR:
Create and initialize a new sortable list from a ul just insertered in the DOM
<ul class="list-1">
<li id="dynamic-id" class="first-level-items">
- Some content -
</li>
<ul>
Everything works fine there, there is an action to add more ".first-level-item" and works fine, but inside those elements is and action to create another another list and inside the list other list items, so the structure should be something like:
<ul class="list-1">
<li id="dynamic-id" class="first-level-items">
<ul class="dynamic-list" id="dynamic-list-id">
<li class="dynamic-generate-li">
- content -
</li>
</ul>
</li>
<ul>
My constructor functions:
createNewCategoryRow: function(parent,obj){
var controller =this;
var insertAfter = $('[data-parent-ul="'+parent+'"]');
var element = "<li data-id='"+obj.uniqueId+"' id='"+obj.uniqueId+"' ><div class='row sub-title-inner' data-obj='"+obj.uniqueId+"'>" +
"<div class='cell cell-2 row-title '>"+
"<span class='span-title'><i> "+obj.value+" </i></span>"+
"</div>"+
"</div>";
element += "<ul class='sort sort-"+obj.uniqueId+"' data-parent='"+obj.uniqueId+"'></ul>"+
"<div class='row bold title-total-child-inner'>"+
"<div class='cell cell-2 '>"+
"<span class='span-title'><strong> "+ obj.total.value+" </strong></span>"+
"</div>";
for(var i = 0; i < obj.total.amounts.length; i++){
element += "<div class='cell overflow-cell' id='"+ obj.total.amounts[i].uniqueId +"'>"+ obj.total.amounts[i].value +"</div>";
}
element += "</div></li>";
insertAfter.append( element);
insertAfter.sortable('refresh')
//Here Everything works as expected
//Here nope
//Initialize the new generate ul container Not working :(
$('.sort-'+obj.uniqueId).sortable({
placeholder: "ui-state-highlight",
cancel: ".cancel-sort",
axis: 'y',
update: function(e, ui) {
$("ul.sort li").find('div.row').removeClass("gray");
$('.sort-'+obj.uniqueId+" li:even").find('div.row').addClass("gray");
},
stop: function(e, ui) {
var cctManager = Client.CCTManager.create();
var idArr = $.map($(this).children('li'), function(el) {
return el.id ;
})
var me = ui.item.data('id');
var position = idArr.indexOf(me);
cctManager.dragCriteriaGroup(_workpage, me, position, function(newWorkpage){
controller.set('saved_workpage',newWorkpage);
})
}
}).disableSelection();
}
//create the item lists for the new list container (ul)
createNewCriteriaRow:function(id,obj){
var insertAfter = $('[data-parent="'+id+'"]');
var element = "<li data-obj='"+obj.uniqueId+"' id='"+obj.uniqueId+"'>"+
"<div class='row'>"+
"<div class='cell cell-2 row-title'>"+
"<span class='span-title'>"+obj.value+"</span>"+
"</div>";
for(var i = 0; i < obj.amounts.length; i++){
element += "<div class='cell overflow-cell' data-obj='"+ obj.amounts[i].uniqueId +"'>"+ obj.amounts[i].value +"</div>";
}
element += "</div></li>"
insertAfter.append(element);
//Until here everything works as expected
//Here i'm trying to refresh the last created item and add some style to the childs, it add the childs but not refresh the sortable
console.log(id)
$('.sort-'+id + 'location').find('div.row').removeClass("gray");
$('.sort-'+id + 'li:even').find('div.row').addClass("gray");
$('.sort-'+id).sortable( "refresh" );
}
Both elements works on click.
Thanks

Related

Call javascript function through dynamically generated href tag (the js function has one paramter)

I need to call getStandards() function from the dynamically generated href tag.All the code is in .js file. I went through many stackoverflow questions but I couldn't find the solution. The function has one parameter(classId) as shown below:
var ul = $('<ul></ul>');
var li = $('<li></li>');
for (var i = 0; i < classes.length; i++) {
var classId = classes[i].classId;
var html = "";
html = "<li><a href='#' id='" + classId + "' onClick='getStandards(" +
classId + ")' >" + classes[i].className + "</a></li><br/>";
li.append(html);
}
ul.append(li);
function getStandards(classId) {
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Can someone help me !! thank you.
Rather than making multiple inline bindings, I would suggest using a data attribute and a delegate event binding for this logic.
var ul = $('<ul></ul>');
ul.append(
classes.map( aClass => {
return `
<li>
<a href="#" class="class-entry"
data-class-id="${aClass.classId}">
${aClass.className}
</a>
</li>
`;
} )
);
ul.on( 'click', '.class-entry', event => {
let $link = $( event.target );
getStandards( $link.data( 'classId' ) );
} );
function getStandards(classId) {
}
You can use a template literal to make your html construction more readable
Mapping all the classes to html and appending them all and once, reduces the number of appends to the ul you are doing
Attaching a delegate event listener to the ul lets you handle the click for all the children of it, and you can grab the class id from the data attribute
I would hazard a guess that you aren't appending your ul to the body and that the classId is a string. With a few modifications this code can work:
<head>
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"> .
</script>
</head>
<body>
<script>
// This code is copyright by nobody and may be used freely without attribution
var ul = $('<ul></ul>');
var li = $('<li></li>');
const classes = [
{ classId: 'id1', className: 'name1'}
];
for (var i = 0; i < classes.length; i++) {
var classId = classes[i].classId;
var html = "";
html = "<li><a href='#' id='" + classId + "' onClick='getStandards(\"" +
classId + "\")' >" + classes[i].className + "</a></li><br/>";
li.append(html);
}
ul.append(li);
$('body').append(ul);
function getStandards(classId) {
alert('get standards! ' + classId);
}
</script>
</body>
Note the appending to the body as well as the extra quotes by the onclick. If you don't have those quotes, it will look like this: onclick="getStandards(id1)" which is invalid since id1 is a string

slideToggle() doesn't push elements created with jquery append() down

I do have a problem using slideToggle(). The following DOM elements won't be pushed down, they just overlap. I tried a lot of versions. I do create the tags with jquery getting a JSON from which I fill in the tag content, so I iterate over the JSON to create the HTML tags for each element :
var project_infos = '';
jQuery(document).ready(function(){
$.getJSON('project.json', function(data){
project_infos += '<div class=\"row\">'
+ '<div class=\"span3\">'
+ '<div class=\"well\">'
+ '<div>'
+'<ul class=\"nav nav-list sidebar-list\">';
for( var i = 0; i < data.length; i ++ ) {
var project_name = data[i]["item"];
project_infos += '<li>'
+'<label class=\"tree-toggler nav-header\">'
+'<i class=\"fa fa-arrow-right\"></i>' + project_name + '</label>'
+'<ul class=\"nav nav-list tree\">'
+'<li><label class=\"tree-toggler nav-header\">Katalogue</label></li>'
+'<li>'
+'<ul class=\"nav nav-list tree\">'
+'<li>Link</li>'
+'<li>Link</li>'
+'<li>Link</li>'
+'</ul>'
+'</li>'
+'<li><label class=\"tree-toggler nav-header\">History</label></li>'
+'<li><label class=\"tree-toggler nav-header\">Whole project</label></li>'
+'</ul>'
+'</li>';
}
project_infos += '</ul></div></div></div></div>';
$('#tree').append(project_infos);
$('.tree-toggler').click(function () {
$(this).parent().children('ul.tree').slideToggle('slow');
});
});
});
EDIT : screenshot of html output.
screenshot html output
Thanks in advance!
Use
find() instead of children()
$(this).parent().find('ul.tree').slideToggle('slow');
children() will only search for first level child elements.
I just reset all css styles for my div and finally it worked. I already defined styles for the sidebar and for the ul and li tags. One of the styles was the problem. It kept the elements from toggle down in the sidebar.
Thanks for your time!

Wrap each list array in a Tag

I am trying to get each Post lists wrapped in the href so it can be clicked on. Basically when I try to click on each Posts, it does not work until I hover my mouse close to the top of each posts before it works.
Below is my code and also an image of what I mean:
JS:
function getPosts(data) {
var $output = $('<ul class="posts" data-role="listview" data-filter="true">')
$.each(data.posts,function(i, val) {
$('<li><a href="#devotionpost" onclick="showPost(' + val.id + ')"</a>').append([$("<h3>", {html: val.title}),$("<p>", {html: val.excerpt})]).appendTo($output);
if ( i == 3 ) return false;
// return (postlimit-- > 1);
});
$('#postlist').empty().append($output);
}
function showPost(id) {
$('#mypost').html('<span class="img_spin">Loading post...</span>');
$.getJSON('http://howtodeployit.com/category/daily-devotion/?json=get_post&post_id=' + id + '&callback=?', function(data) {
var posts='';
posts += '<h3>' + data.post.title + '</h3>';
posts += data.post.content;
$('#mypost').html(posts);
});
}
Image:
If you look at the image, when i hover my mouse close to the top edge of the Post, then the URL at the bottom shows and that works but any where else does not work.
From looking at the Chrome Elements tab in this demo the JavaScript is generating invalid HTML because of mismatched closing elements.
Using an example posts of var posts = { "posts" : [ {"id":"1", "title":"lorem", "excerpt":"foo"}, {"id":"2", "title":"ipsum", "excerpt":"bar"} ] }
This '<li><a href="#devotionpost" onclick="showPost(' + val.id + ')"</a>' is resulting in the broken first child of the following <li>:
<li>
<a href="#devotionpost" onclick="showPost(1)" < a></a>
<h3>lorem</h3>
<p>foo</p>
</li>
Depending on exactly what you want to wrap in the anchor element, you could just build the post <li> like in this updated demo or see code below:
$.each(data.posts,function(i, val) {
$output.append('<li><h3>' + val.title + '</h3><p>' + val.excerpt + '</p></li>');
});
Or if you want to have a little less string concatenation, you could use .wrapInner()
$.each(data.posts,function(i, val) {
var $post = $('<li><h3>' + val.title + '</h3><p>' + val.excerpt + '</p></li>');
$post.wrapInner('');
$output.append($post);
});
Or keeping your .append() approach:
$.each(data.posts,function(i, val) {
var $post = $('<li/>').append([$("<h3>", {html: val.title}),$("<p>", {html: val.excerpt})]).wrapInner('');
$output.append($post);
});
Note: The string concatenation approach, combined with a single .append() is the best performing code.
By the looks of it, your lists are floating. You should use a clearfix technique to make "A" element box include the list. You also should know that by standard "A" elements are not allowed to contain block elements and as far as internet explorer 9 (didn't check in 10 or 11) "A" will not work the way you intended.
Possible workaround:
<div style="position:relative">
<ul>
<li></li>
<ul>
<a style="position:absolute;display:block;top:0;bottom:0;left:0:right:0;" href="#"></a>
<div>

Saving JQuery-UI Sortable Connected Lists Custom Configuration to Local Cookie

Please Skip to Update #2 at the Bottom if you don't want to read the whole post.
I have created a customizable UI using jquery-ui connected lists:
http://jqueryui.com/sortable/#connect-lists
I now want to save the user's configuration of the UI to a cookie on their local machine so that the next time they load the page the layout they previously setup will be loaded, as discussed on this page:
http://devheart.org/articles/jquery-customizable-layout-using-drag-and-drop/
The problem is that after discussing how to save the custom configuration of multiple connected lists in part 2 of his writeup, he neglects to include multiple connected lists in part 3 where he implements the code into an actual design. I have been able to get everything on my page to work like the final example on that page, but whenever I try to modify the code to work with connected lists the page no longer works.
I understand the basic idea behind what the code is doing, but I don't know JavaScript and have had no success in modifying the code to work with connected lists. I'm hoping that someone who does know JavaScript will be able to easily modify the code below to work with connected lists like part 2 does.
Part 2 saves the order of multiple connected lists, but doesn't load external html pages with the corresponding name.
Part 3 loads external html pages with the corresponding names of the item, but only supports a single list.
Code for Connected Lists from Part 2:
// Load items
function loadItemsFromCookie(name)
{
if ( $.cookie(name) != null )
{
renderItems($.cookie(name), '#wrapper');
}
else
{
alert('No items saved in "' + name + '".');
}
}
// Render items
function renderItems(items, container)
{
var html = '';
var columns = items.split('|');
for ( var c in columns )
{
html += '<div class="column"><ul class="sortable-list">';
if ( columns[c] != '' )
{
var items = columns[c].split(',');
for ( var i in items )
{
html += '<li id="' + items[i] + '">Item ' + items[i] + '</li>';
}
}
html += '</ul></div>';
}
$('#' + container).html(html);
}
Code from part 3 that does not support connected lists (Trying to modify this to support connected lists):
// Get items
function getItems(id)
{
return $('#' + id + '-list').sortable('toArray').join(',');
}
// Render items
function renderItems(id, itemStr)
{
var list = $('#' + id + '-list');
var items = itemStr.split(',')
for ( var i in items )
{
html = '<li class="sortable-item';
if ( id == 'splash' )
{
html += ' col3 left';
}
html += '" id="' + items[i] + '"><div class="loader"></div></li>';
list.append(html);
// Load html file
$('#' + items[i]).load('content/' + items[i] + '.html');
}
}
Update #1:
I think I almost have it, I just can't get it to insert html from the external html files. It was able to get it to insert variables and plain text, just not the external html.
// Render items
function renderItems(items)
{
var html = '';
var columns = items.split('|');
for ( var c in columns )
{
html += '<div class="column';
if ( c == 0 )
{
html += ' first';
}
html += '"><ul class="sortable-list">';
if ( columns[c] != '' )
{
var items = columns[c].split(',');
for ( var i in items )
{
html += '<li class="sortable-item" id="' + items[i] + '">';
//---------This Line Isn't Working--------->
$('#' + items[i]).load(items[i] + '.html');
//---------This Line Isn't Working--------->
html += '</li>';
}
}
html += '</ul></div>';
}
$('#example-2-3').html(html);
}
Update #2:
I've been looking up exactly what each JavaScript command in the example does and I think I've figured out the problem. I can't just load the page, I need to append the code from the external page to the html variable. I think I need to use the .get command, something like:
var pagedata = '';
.get(items[i] + '.html', function(pagedata);
html += pagedata;
Anyone know what the correct syntax to accomplish this would be?
I finally got the code to work. Here is the full code (not including jquery-ui related code). External pages need to be named the same as the list id.
HTML
<div id="example-2-3">
<div class="column first">
<ul class="sortable-list">
<li class="sortable-item" id="id1"></li>
<li class="sortable-item" id="id2"></li>
<li class="sortable-item" id="id3"></li>
</ul>
</div>
<div class="column">
<ul class="sortable-list">
<li class="sortable-item" id="id4"></li>
</ul>
</div>
<div class="column">
<ul class="sortable-list">
<li class="sortable-item" id="id5"></li>
<li class="sortable-item" id="id6"></li>
</ul>
</div>
</div>
Script
$(document).ready(function(){
window.onload = loadItemsFromCookie('cookie-2');
// Get items
function getItems(exampleNr)
{
var columns = [];
$(exampleNr + ' ul.sortable-list').each(function(){
columns.push($(this).sortable('toArray').join(','));
});
return columns.join('|');
}
// Load items from cookie
function loadItemsFromCookie(name)
{
if ( $.cookie(name) != null )
{
renderItems($.cookie(name));
}
else
{
alert('No items saved in "' + name + '".');
}
}
// Render items
function renderItems(items)
{
var html = '';
var pagedata = '';
var columns = items.split('|');
for ( var c in columns )
{
html += '<div class="column';
if ( c == 0 )
{
html += ' first';
}
html += '"><ul class="sortable-list">';
if ( columns[c] != '' )
{
var items = columns[c].split(',');
for ( var i in items )
{
html += '<li class="sortable-item" id="' + items[i] + '">';
var pagedata = '';
var scriptUrl = items[i] + ".html"
$.ajax({
url: scriptUrl,
type: 'get',
dataType: 'html',
async: false,
success: function(data) {
result = data;
html += data;
}
});
html += '</li>';
}
}
html += '</ul></div>';
}
$('#example-2-3').html(html);
}
$('#example-2-3 .sortable-list').sortable({
connectWith: '#example-2-3 .sortable-list',
update: function(){
$.cookie('cookie-2', getItems('#example-2-3'));
}
});
});
</script>

making a list in jquery mobile using innerhtml

I am trying to create a list using the styles provided by jquery mobile. When I make the list in the html file, the list incorporates the styles that jquery mobile provide: http://jquerymobile.com/demos/1.0/docs/lists/lists-search.html
However in my website, i need to generate the list based on the number of objects in my array so I want to do this by making the html script using javascript and changing the innerHTML of a div. When I do this, a normal list appears without the stylings of jquery mobile.
Does anyone have a solution to my problem?
Here's my markup code:
function GenerateList(appArray) {
var searchList = document.getElementById('searchList');
var app;
var htmlString = "<ul data-role='listview' data-filter='true' data-filter- placeholder='Search...' data-filter-theme='a' data-theme='a'>";
for(i=0; i<appArray.length;i++) {
app = appArray[i];
htmlString = htmlString + "<li><a id=App" + (i+1).toString() + " onclick='AppSelected(id);'>";
htmlString = htmlString + "<img src='DummyImages/" + app[1] + "' alt='Logo' class='ListAppLogo'>";
htmlString = htmlString + "<h3>"+ app[2] + "</h3>";
htmlString = htmlString + "<p>" + app[4] + ".0/5.0</p>";
htmlString = htmlString + "<input type='hidden'>" + app[0] + "</a></li>";
}
htmlString = htmlString + "</ul>";
searchList.innerHTML = htmlString;
}
You'll want to make sure JQuery Mobile initializes the list after you've created it with javascript. Use the following snippet where thislist is the id of the unordered list element.
$('#thislist').listview()
Also, if the unordered list with data-role='listview' already exists, then you'll want to use the same initializer with a parameter, 'refresh'. Here is another snippet where thislist is the id of the unordered list. You'll want to use the refresh if you've added/removed items from it dynamically.
$('#thislist').listview('refresh');
I'm not taking credit for the solution instead trying to explain it in detail since it took me some time to figure it out but the changes should be something like this.
Original code:
function GenerateList(appArray) {
var searchList = document.getElementById('searchList');
var app;
var htmlString = "<ul data-role='listview' data-filter='true' data-filter- placeholder='Search...' data-filter-theme='a' data-theme='a'>";
...
searchList.innerHTML = htmlString;
}
Fixed code:
function GenerateList(appArray) {
var searchList = document.getElementById('searchList');
var app;
var htmlString = "<ul id='thislist' data-role='listview' data-filter='true' data-filter- placeholder='Search...' data-filter-theme='a' data-theme='a'>";
...
searchList.innerHTML = htmlString;
$('#thislist').listview();
}
This way it shows your list using the listview from jquery mobile.
You're not using the same CSS as jQuery Mobile. If you look at the HTML on the link you provided, you'll see that they have additional classes on their list items:
<ul data-role="listview" data-filter="true" class="ui-listview">
<li data-theme="c" class="ui-btn ui-btn-icon-right ui-li-has-arrow ui-li ui-btn-up-c">
<div class="ui-btn-inner ui-li" aria-hidden="true">
<div class="ui-btn-text">Acura</div>
<span class="ui-icon ui-icon-arrow-r ui-icon-shadow"></span>
</div>
</li>
</ul>

Categories