Jquery UI Sortable not working in Dynamic container - javascript

I have a container where I dynamically add sections, I create items inside the section , that should be sortable, But Jquery sortable does not work inside dynamically created containers, while if I give the containers static it works,
Can anyone please share a solution.Thanks
https://jsfiddle.net/anubala/7ut5wk3j/1/
HTML:
<div class='row page-layout'>
<div class='col-md-10 col-md-offset-1' >
<div class="add_section_div">
<p>Add section</p>
</div>
<div id="contents">
</div>
</div>
</div>
JavaScript:
$(".wrap_body").sortable();
$(".add_section_div").on("click", function() {
var $section = "<div class='section_div'><div class='div_header clearfix'><p class='pull-left'>Add Wrap</p></div><div class='section_body div_body'></div></div>";
$("#contents").append($section);
})
$("body").on("click", ".section_div>.div_header", function() {
var $wrap = "<div class='wrap_div'><div class='div_header clearfix'><p class='pull-left'>Add Item</p></div><div class='wrap_body div_body clearfix'></div></div>";
$(this).parent().find('.section_body').append($wrap);
})
const regex = /^.*-\s*(.*)$/;
$("body").on("click", ".wrap_div>.div_header", function() {
var $item1 = "<div class='drag col-sm-";
var $item2 = "'><div class='item_div'><div class='div_header clearfix'><p class='pull-left'><span class='minus'><i class='fa fa-minus'></i></span><span class='plus'><i class='fa fa-plus'></i></span></p><p class='pull-left header_text'>";
var $item3 = "</p></div><div class='div_body'></div></div></div>";
var length_item = $(this).parent().find(".wrap_body .item_div").length;
var count = 0;
for (i = 0; i < length_item; i++) {
if ($(this).parent().find(".wrap_body>div:eq('" + i + "')")) {
console.log($(this).parent().find(".wrap_body>div:eq('" + i + "')"))
var col_count_text = $(this).parent().find(".wrap_body>div:eq('" + i + "')").attr('class');
count += parseInt(find_col_count(col_count_text));
}
}
var current_col_count = 12 - (count % 12);
if (count < 12) {
$(this).parent().find('.wrap_body').append($item1 + current_col_count + $item2 + "col-sm-" + current_col_count + $item3);
}
})
function find_col_count(col_text) {
var col_count = regex.exec(col_text);
col_count.forEach((match, groupIndex) => {
count1 = match;
});
return count1;
}
$("body").on("click", ".plus", function() {
var $parent = $(this).parent().parent().parent().parent();
var col_count_text = $parent.attr('class');
var length_item = $parent.parent().find(".item_div").length;
var count = 0;
for (i = 0; i < length_item; i++) {
if ($parent.parent().find(".item_div:eq('" + i + "')").parent()) {
var col_count_text = $parent.parent().find(".item_div:eq('" + i + "')").parent().attr('class');
count += parseInt(find_col_count(col_count_text));
}
}
var col_count_text = $parent.attr('class');
var col_count = find_col_count(col_count_text);
if (count < 12) {
var col_count_new = "col-sm-" + (++col_count);
var col_count_drag = "drag " + col_count_new;
$parent.attr("class", col_count_drag);
$parent.find(".header_text").html(col_count_new);
}
});
$("body").on("click", ".minus", function() {
var $parent = $(this).parent().parent().parent().parent();
var col_count_text = $parent.attr('class');
var col_count = find_col_count(col_count_text);
if (col_count > 1) {
var col_count_new = "col-sm-" + (--col_count);
var col_count_drag = "drag " + col_count_new;
$parent.attr("class", col_count_drag);
$parent.find(".header_text").html(col_count_new);
}
});

You probably may have found the solution already but to solve this issue you only need to move this line
$(".wrap_body").sortable();
To where you actually append the .wrapper_body to the body as following:
$("body").on("click", ".section_div>.div_header", function() {
var $wrap = "<div class='wrap_div'><div class='div_header clearfix'><p class='pull-left'>Add Item</p></div><div class='wrap_body div_body clearfix'></div></div>";
$(this).parent().find('.section_body').append($wrap);
$(".wrap_body").sortable();
});
This should solve your problem. See JsFiddle

Related

Javascript filled unsorted list auto scroll

I'm struggling to make this idea of mine work..
The idea is to auto-scroll the dynamically filled unsorted list.
This is how I've build the Unsorted List with List Items in JavaScript
$.getJSON(sportlink_url + 'programma?gebruiklokaleteamgegevens=NEE&aantaldagen=' + programma_dagen + '&eigenwedstrijden=JA&thuis=JA&uit=JA&' + sportlink_clientID, function (uitslag) {
for (let i = 0; i < Object.keys(uitslag).length; i++) {
//for (let i = 0; i < 1; i++) {
var aanvangstijd = uitslag[i].aanvangstijd;
var thuisteam = uitslag[i].thuisteam;
var uitteam = uitslag[i].uitteam;
var accommodatie = uitslag[i].accommodatie;
var competitiesoort = uitslag[i].competitiesoort;
var datumNumber = uitslag[i].datum.substring(0,2);
var datumMonth = uitslag[i].datum.slice(-4);
var datumMonthClean = datumMonth.substring(0,3);
//Fetch the DIV
var el = document.getElementById("match_program");
//Create new list item
var node = document.createElement("li");
node.setAttribute('role', 'presentation');
//Create ticketDiv
var ticketDiv = document.createElement("div");
ticketDiv.setAttribute('class', 'tg-ticket');
//Create timeBox
var timeBox = document.createElement("time");
timeBox.setAttribute('class', 'tg-matchdate');
timeBox.innerHTML = (datumNumber + " <span>" + datumMonthClean + "</span>");
//Create matchdetail
var matchDetail = document.createElement("div");
matchDetail.setAttribute('class', 'tg-matchdetail');
matchDetail.innerHTML = ("<h4>" + thuisteam + "<span> - </span>" + uitteam + "   |   " + aanvangstijd + ", " + accommodatie);
//Create themeTag
var themeTag = document.createElement("span");
themeTag.setAttribute('class', 'tg-theme-tag');
themeTag.innerHTML = (competitiesoort);
//Build the hole thing
ticketDiv.appendChild(timeBox);
matchDetail.appendChild(themeTag);
ticketDiv.appendChild(matchDetail)
node.appendChild(ticketDiv);
el.appendChild(node);
This is the Unsorted List in HTML
<ul id="match_program" class="tg-tickets tg-tabnav" role="tablist" data-autoscroll="">
</ul>
This is the function i'm currently using for auto-scroll, but it has .ulContent').height() > $('.ulContainer').height() and because my ulContent doesn't have a prefix height in CSS it's not going to work..
And I can't put a height prefix in CSS for the ulContent cause I don't know on forehand if it's going to be 500px of 800px, the unsorted list is being filled from a JSON string.
$(document).ready(function() {
if($('.ulContent').height() > $('.ulContainer').height()) {
setInterval(function () {
start();
}, 3000);
}
});
function animateContent(direction) {
var animationOffset = $('.ulContainer').height() - $('.ulContent').height();
if(direction == 'up') {
animationOffset = 0;
}
}
The animatie function is being called at the bottom of the HTML file just before the closing tags of the body
I manged to figure it out;
var amountGames = Object.keys(uitslag).length
var calulContent = amountGames * 116 + 500;
var setulContent = calulContent + "px";
document.getElementById('ulContent').style.height= setulContent;
That way the ulContent is always filled and the container uses a fixed number of 500px;

Attempting to retrieve a <select>'s .value with jQuery

So I have this HTML Code/Javascript,
var spellNumber = 0;
function createSpell() {
var spellOption = document.createElement("option");
var spellOption2 = document.createElement("option");
var spellSelect = document.createElement("select");
var spellLabel = document.createElement("label");
var spellEnvelope = document.createElement("p");
spellOption.innerHTML = 'Vanish';
spellOption.setAttribute('value', 'vanish');
spellOption2.innerHTML = 'Teleport';
spellOption2.setAttribute('value', 'teleport');
spellSelect.setAttribute('id', 'spell');
spellSelect.setAttribute('name', 'spell');
spellLabel.setAttribute('for', 'spell');
spellLabel.innerHTML = '<strong>Spell ' + (spellNumber + 1) + '</strong> = ';
spellEnvelope.appendChild(spellLabel);
spellEnvelope.appendChild(spellSelect);
spellSelect.appendChild(spellOption);
spellSelect.appendChild(spellOption2);
document.getElementById("spells").appendChild(spellEnvelope);
spellNumber += 1;
}
createSpell()
function generateYaml() {
var spellCheck = 1;
for (allSpells = 0; allSpells < spellNumber; allSpells++) {
var multipleSpell = $("#spell:contains('Spell " + spellCheck + "')").val();
console.log(multipleSpell);
spellCheck++
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="spells"></div>
<button onClick="createSpell()">Add A Spell</button>
<button id="button" onClick="generateYaml()">Make the Magic Happen</button>
And am trying to retrieve the .value of the <select id="spell"> options. However, the console returns an undefined, instead of vanish or teleport.
Can anyone point me in the right direction for this?
Well:
Id should be unique so I added spellSelect.setAttribute('id', 'spell' + spellNumber); ( + other rows)
Also multipleSpell = $("#spell" + spellCheck).val();
And console.log(multipleSpell); is called only once at the begining -> you should change it to function too
`
var spellNumber = 0;
function createSpell() {
var spellOption = document.createElement("option");
var spellOption2 = document.createElement("option");
var spellSelect = document.createElement("select");
var spellLabel = document.createElement("label");
var spellEnvelope = document.createElement("p");
spellOption.innerHTML = 'Vanish';
spellOption.setAttribute('value', 'vanish');
spellOption2.innerHTML = 'Teleport';
spellOption2.setAttribute('value', 'teleport');
spellSelect.setAttribute('id', 'spell' + spellNumber);
spellSelect.setAttribute('name', 'spell' + spellNumber);
spellLabel.setAttribute('for', 'spell' + spellNumber);
spellLabel.innerHTML = '<strong>Spell ' + (spellNumber + 1) + '</strong> = ';
spellEnvelope.appendChild(spellLabel);
spellEnvelope.appendChild(spellSelect);
spellSelect.appendChild(spellOption);
spellSelect.appendChild(spellOption2);
document.getElementById("spells").appendChild(spellEnvelope);
spellNumber += 1;
}
createSpell()
function generateYaml() {
var spellCheck = 0;
for (allSpells = 0; allSpells < spellNumber; allSpells++) {
var multipleSpell = $("#spell" + spellCheck).val();
console.log(multipleSpell);
spellCheck++
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="spells"></div>
<button onClick="createSpell()">Add A Spell</button>
<button id="button" onClick="generateYaml()">Make the Magic Happen</button>
var multipleSpell = $("#spell:contains('Spell " + spellCheck + "')").val();
More info here.
Edited:
Well, i see you are trying to get all select values. Why not try this?
function generateYaml() {
var spellCheck = 1;
var selects = $("select").each(function (index, element) {
var value = $(element).val()
console.log("The value at index %d is %s", index, value);
spellCheck++
});
}
This way you iterate over all the selects, and get their values inside the loop. Try it here (open the developer console):https://darkcyanpointlessbooleanvalue--parzibyte.repl.co/

Execute function on each element (with the same id)

I have this HTML code:
<div id="dadosalvaje" class="draconistone"><dl class="codebox"><dd><strong>Número aleatorio (1,10) : </strong>1</dd></dl></div>
<div id="dadosalvaje" class="draconistone"><dl class="codebox"><dd><strong>Número aleatorio (1,10) : </strong>3</dd></dl></div>
And i want to execute this JavaScript code on each one:
$(document).ready(function() {
//Arreglos
var zonas = ['draconistone','cessabit', 'valoran'];
var draconistone = ['bulbasaur', 'pikachu', 'squirtle'];
//Variables
var contenedor = $('#dadosalvaje');
var texto = contenedor.text().split(' ');
var resultado = texto.pop();
var zonaID = $('#dadosalvaje').attr('class');
for (var i = 0; i < zonas.length; i++) {
if (zonaID == zonas[i]) {
if (zonaID == 'draconistone') {
var pokemonSprite = draconistone[resultado - 1];
}
}
}
for (var i = 0; i < zonas.length; i++) {
if (zonas[i] == zonaID) {
contenedor.append('<img src="https://www.pkparaiso.com/imagenes/xy/sprites/animados/' + pokemonSprite + '.gif"><div class="salvajeNombre">' + pokemonSprite + '</div>');
contenedor.attr('id', 'salvajelisto');
}
}
});
It just affects the first element and I can't find the way to modify both of them.
Is there any way to modify every single element with the same ID ?
First you need to make unique id
You do not need the "draconistone" class
Inside the $(document).ready(function() { and }); tags make the code a function with an id parameter like this:
function name(id) {
//Arreglos
var zonas = ['draconistone','cessabit', 'valoran'];
var draconistone = ['bulbasaur', 'pikachu', 'squirtle'];
//Variables
var contenedor = $('#' + id);
var texto = contenedor.text().split(' ');
var resultado = texto.pop();
var zonaID = $('#' + id).attr('class');
for (var i = 0; i < zonas.length; i++) {
if (zonaID == zonas[i]) {
if (zonaID == 'draconistone') {
var pokemonSprite = draconistone[resultado - 1];
}
}
}
for (var i = 0; i < zonas.length; i++) {
if (zonas[i] == zonaID) {
contenedor.append('<img src="https://www.pkparaiso.com/imagenes/xy/sprites/animados/' + pokemonSprite + '.gif"><div class="salvajeNombre">' + pokemonSprite + '</div>');
contenedor.attr('id', 'salvajelisto');
}
}
}
Then execute the functions with the two different ids as parameters.
Maybe you can use $.each()
Like this
$('.draconistone').each(function() {
var zonaID = $(this).attr('class');
..
});
There is one for the same class
$( document).ready(function() {
$( ".draconistone" ).each(function( i ) {
var zonas = ['draconistone','cessabit', 'valoran'];
var draconistone = ['bulbasaur', 'pikachu', 'squirtle'];
//Variables
var texto = this.innerText.split(' ');
var resultado = texto.pop();
var zonaID = this.className;
for (var i = 0; i < zonas.length; i++) {
if (zonaID == zonas[i]) {
if (zonaID == 'draconistone') {
var pokemonSprite = draconistone[resultado - 1];
}
}
}
for (var i = 0; i < zonas.length; i++) {
if (zonas[i] == zonaID) {
this.innerHTML += '<img src="https://www.pkparaiso.com/imagenes/xy/sprites/animados/' + pokemonSprite + '.gif"><div class="salvajeNombre">' + pokemonSprite + '</div>';
this.id = 'salvajelisto';
}
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="dadosalvaje1" class="draconistone"><dl class="codebox"><dd><strong>Número aleatorio (1,10) : </strong>1</dd></dl></div>
<div id="dadosalvaje2" class="draconistone"><dl class="codebox"><dd><strong>Número aleatorio (1,10) : </strong>3</dd></dl></div>

Scroll to different id(s) from one button

I have different boxes with different id
<div id="49" align="center" class="feed_box_id"></div>
<div id="50" align="center" class="feed_box_id"></div>
<div id="51" align="center" class="feed_box_id"></div>
i want to scroll a next id whenever the same button is clicked.
I tried
scrollTop
scrollTo
window.location.href
But couldn't find a way out.
if you want, here is my fuzzy code
var id_down = parseInt($(".feed_box_id").attr("id")) - 1;
$("#feed_down").click(function() {
window.location.href = "#" + id_down;
});
var id_up = parseInt($(".feed_box_id").attr("id")) + 1;
$("#feed_up").click(function() {
window.location.href = "#" + id_up;
});
Maintain a variable that keeps the track of the last clicked id.
var lastclicked={id:49};
$("#feed_up").click(lastclicked, function(e) {
if($("#" + (e.data.id+1)).length>0)
{
window.location.href = "#" + (e.data.id+1);
e.data.id++;
}
});
$("#feed_down").click(lastclicked, function(e) {
if($("#" + (e.data.id-1)).length>0)
{
window.location.href = "#" + (e.data.id-1);
e.data.id--;
}
});
Okay, you can create the fiddle by yourself.
HTML:
<button id="feed_down">feed_down</button>
<button id="feed_up">feed_up</button>
JS:
for(var i=0;i<100;i++)
{
$('<div id="' + (i) + '" align="center" class="feed_box_id">'+ (i) +'</div>').appendTo($('body'));
}
var lastclicked={id:49};
$("#feed_up").click(lastclicked, function(e) {
if($("#" + (e.data.id+1)).length>0)
{
window.location.href = "#" + (e.data.id+1);
e.data.id++;
}
});
$("#feed_down").click(lastclicked, function(e) {
if($("#" + (e.data.id-1)).length>0)
{
window.location.href = "#" + (e.data.id-1);
e.data.id--;
}
});
CSS:
#feed_down
{
position:fixed;
top:0;
right:0
}
#feed_up
{
position:fixed;
top:0;
right:100px
}
There are 2 ways: using only class without ids, using ids and no class is needed.
Class only:
var elementIndex = 0, elements = document.getElementsByClassName('myClass');
var scrollToElement = function(element){
window.scrollTo(0, element.getBoundingClientRect().top);
};
var nextElement = function(){
if(++elementIndex >= elements.length)
elementIndex = 0;
scrollToElement(elements[elementIndex]);
};
var previousElement = function(){
if(--elementIndex < 0)
elementIndex = elements.length - 1;
scrollToElement(elements[elementIndex]);
};
Id only (may be not only numbers):
var elementIndex = 0, elements = ['id1', 'myDog', 'last'];
var scrollToElement = function(element){
window.scrollTo(0, element.getBoundingClientRect().top);
};
var nextElement = function(){
if(++elementIndex >= elements.length)
elementIndex = 0;
scrollToElement(document.getElementById(elements[elementIndex]));
};
var previousElement = function(){
if(--elementIndex < 0)
elementIndex = elements.length - 1;
scrollToElement(document.getElementById(elements[elementIndex]));
};

Dynamically replacing the content for large list

I am trying to render child elements of an element if the element is in view or removing the content if not in view like below on scroll event like below
list.addEventListener('scroll', function () {
var elements = document.querySelectorAll('.aBox');
var toBe = counter - 1 - elements.length;
for (var i = 0; i < elements.length; i++) {
var inView = visibleY(elements[i]),
ele = elements[i].querySelector('.item');
if (inView === false && ele) {
console.log("Not in visible, keeping it none");
var height = elements[i].clientHeight;
elements[i].style.height = height + "px";
elements[i].innerHTML = "";
} else if(!ele){
console.log('Placing the content');
var minArray = arr[toBe + 1 + i],
str = "";
for (var j = 0; j < minArray.length; j++) {
str += "<div class='item'>" + minArray[j] + "</div>";
}
elements[i].innerHTML = str;
}
}
});
It seems working but if I have a look at the DOM this is not working as expected. Someone please help me to find the problem, fiddle.
Update
function updateData(callback) {
var elements = document.querySelectorAll('.aBox');
elements = Array.prototype.slice.call(elements);
var toBe = counter - 1 - elements.length;
async.each(elements, function (element, cb) {
var inView = $(element).is_on_screen(),
ele = element.querySelector('.item');
if (inView == false && ele) {
console.log("Not in visible, keeping it none");
var height = element.clientHeight;
element.style.height = height + "px";
element.innerHTML = "";
} else if (!ele && inView) {
console.log('Placing the content');
var minArray = arr[toBe + 1 + i],
str = "";
if (typeof minArray === "object") {
for (var j = 0; j < minArray.length; j++) {
str += "<div class='item'>" + minArray[j] + "</div>";
}
element.innerHTML = str;
}
}
cb();
}, function () {
callback()
});
}
Fiddle
Hi I have solved this problem. Posting here, so that it will be more helpful for people who want to work on mobiles to display very large lists with virtual scrolling
var arr = new Array(10000);
for (var i = 0; i < arr.length; i++) {
arr[i] = "Hello Dudes..." + i;
}
Array.prototype.chunk = function (chunkSize) {
var array = this;
return [].concat.apply([],
array.map(function (elem, i) {
return i % chunkSize ? [] : [array.slice(i, i + chunkSize)];
}));
}
arr = arr.chunk(50);
var list = document.getElementById('longList');
var button = document.getElementById('loadMore');
var counter = arr.length,
aBoxLen = 1;
function appendBox() {
var div = document.createElement('div'),
str = "";
div.className = "aBox";
var minArray = arr[counter - aBoxLen];
for (var i = 0; i < minArray.length; i++) {
str += "<div class='item'>" + minArray[i] + "</div>";
}
div.innerHTML = str;
div.setAttribute('index', counter - aBoxLen);
var box = document.querySelector('.aBox');
if (box) {
list.insertBefore(div, box);
} else {
list.appendChild(div);
}
aBoxLen += 1;
}
appendBox();
button.addEventListener('click', function () {
appendBox();
});
$.fn.is_on_screen = function () {
var win = $(window);
var viewport = {
top: win.scrollTop(),
left: win.scrollLeft()
};
viewport.right = viewport.left + win.width();
viewport.bottom = viewport.top + win.height();
var bounds = this.offset();
bounds.right = bounds.left + this.outerWidth();
bounds.bottom = bounds.top + this.outerHeight();
return (!(viewport.right < bounds.left || viewport.left > bounds.right || viewport.bottom < bounds.top || viewport.top > bounds.bottom));
};
function updateData(callback) {
var elements = document.querySelectorAll('.aBox');
elements = Array.prototype.slice.call(elements);
var toBe = counter - 1 - elements.length;
async.each(elements, function (element, cb) {
var inView = $(element).is_on_screen(),
ele = element.querySelector('.item');
if (inView == false && ele) {
console.log("Not in visible, keeping it none");
var height = element.clientHeight;
element.style.height = height + "px";
element.innerHTML = "";
} else if (!ele && inView) {
console.log('Placing the content');
console.log(element.getAttribute('index'));
var minArray = arr[element.getAttribute('index')],
str = "";
for (var j = 0; j < minArray.length; j++) {
str += "<div class='item'>" + minArray[j] + "</div>";
}
element.innerHTML = str;
}
cb();
}, function () {
// callback()
});
}
var delay = false;
var timeout = null;
list.addEventListener('touchmove', function () {
clearTimeout(timeout);
timeout = setTimeout(function () {
updateData();
}
}, delay);
});
None of the solutions were specifically designed for mobiles, so I have implemented this.
I think there is lots of space for improvement in this. If anybody want to improve it, please feel free to make it
Demo

Categories