I have this unslider which display tweets polled from some sources via Ajax which refreshes every 8 sec. The Unslider works fine for the first time but subsequently when new ajax queries are fired, it is supposed to clear the first tags and repopulate with new one. For some reason, it doesn't clear out the old tags and instead appends the new tags to the old.
Here are the screenshots:
var flag = false;
(function setTweets() {
$.ajax({
type: "GET",
url: "../tweets/get_latest_tweets",
success: function(data) {
//clear all children first
$('#tweets-list').children().remove();
for (var i = 0; i < data.length; i++) {
$('<li>' + data[i].text + '<div class="card-footer bg-twitter"><div class="card-profile-image"><img src="' + data[i].pic + '" class="rounded-circle img-border box-shadow-1" alt="Card Image"></div>' +
'<footer class="blockquote-footer bg-twitter white"><strong>#' + data[i].screen_name + '</strong></footer></div></li>').appendTo($('#tweets-list'))
}
if (!flag) {
$('#tweet-slider').unslider({ //initialize unslider only once
autoplay: true,
arrows: true,
speed: 1000,
delay: 7000,
});
flag = true;
}
},
error: function(err) {
console.log(err);
}
}).then(function() {
setTimeout(setTweets, 8000); //Todo: Check how to do this async (dynamic adding of points)
});
})();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="card bg-twitter white">
<div class="card-content p-2">
<div class="card-body">
<div class="text-center mb-1">
<i class="ft-twitter font-large-3"></i>
</div>
<div class="tweet-slider" id="tweet-slider">
<ul id='tweets-list' class="text-center">
</ul>
</div>
</div>
</div>
Similar to .empty(), the .remove() method takes elements out of the DOM. Use .remove() when you want to remove the element itself, as well as everything inside it. In addition to the elements themselves, all bound events and jQuery data associated with the elements are removed. To remove the elements without removing data and events, use .detach() instead.
According to this, you are removing # tweets-list as an element, I think if you use .empty () you will clean it
uses $('#tweets-list').empty();
I was reading the unslider website and it says that if you delete or add slides you should use the method slider.unslider('calculateSlides');
var flag = false;
(function setTweets() {
$.ajax({
type: "GET",
url: "../tweets/get_latest_tweets",
success: function(data) {
//clear all children first
$('#tweets-list').children().remove();
for (var i = 0; i < data.length; i++) {
$('<li>' + data[i].text + '<div class="card-footer bg-twitter"><div class="card-profile-image"><img src="' + data[i].pic + '" class="rounded-circle img-border box-shadow-1" alt="Card Image"></div>' +
'<footer class="blockquote-footer bg-twitter white"><strong>#' + data[i].screen_name + '</strong></footer></div></li>').appendTo($('#tweets-list'))
}
if (!flag) {
$('#tweet-slider').unslider('calculateSlides');
flag = true;
}
},
error: function(err) {
console.log(err);
}
}).then(function() {
setTimeout(setTweets, 8000); //Todo: Check how to do this async (dynamic adding of points)
});
})();
apparently there is no need to reinitialize the unslider, try it but, you restart it
Related
This question already has answers here:
Use magnificPopup with dynamic elements
(3 answers)
Closed 4 years ago.
my problem is when i append to the div from jquery after ajax post the popup magnifier does not work at all
i am using jquery.magnific-popup
$(document).ready(function () {
$('.image-popup').magnificPopup({
type: 'image',
closeOnContentClick: true,
mainClass: 'mfp-fade',
gallery: {
enabled: true,
navigateByImgClick: true,
preload: [0, 1] // Will preload 0 - before current, and 1 after the current image
}
});
});
and this is my div
<div id="officialDiv" class="row m-b-15">
<%
for (int i = 0; i < officialDocsList.Count; i++)
{
%>
<div class="col-sm-6 col-lg-3 col-md-4 mobiles" style="flex: 0 0 15%; max-width: 15%;">
<div class="product-list-box thumb">
<a href="<%= officialDocsList[i] %>" class="image-popup" title="Screenshot-1">
<img style="width:100px;height:100px;" src="<%= officialDocsList[i] %>" class="thumb-img" alt="work-thumbnail">
</a>
<div class="product-action">
<i class="md md-close"></i>
</div>
</div>
</div>
<%} %>
but i need to append to the div on file input upload so i did the below :
$('#officialDoc').on('change', function (event) {
var files = $("#officialDoc").prop("files");
for (var i = 0; i < files.length; i++) {
(function (file) {
var fileReader = new FileReader();
fileReader.onload = function (f) {
var d = f.target.result;
var img = d.split("base64,").pop();
var byteArray = _base64ToArrayBuffer(img);
$.ajax({
type: "POST",
url: "BusinessLogic/SaveTempFiles.ashx",
data: {
'file': img,
'name': file.name
},
success: function (result) {
$("#OfficialDocsPath").val($("#OfficialDocsPath").val() + ',' + result);
$("#officialDiv").append("<div class='col-sm-6 col-lg-3 col-md-4 mobiles' style='flex: 0 0 15%; max-width: 15%;'><div class='product-list-box thumb'><a href='" + result + "' class='image-popup' title='Screenshot-1'><img src='" + result + "' class='thumb-img' style='width:100px;height:100px;' alt='work-thumbnail'></a><div class='product-action'> <a href='#' class='btn btn-danger btn-sm'><i class='md md-close'></i></a> </div></div> </div>");
}
});
};
fileReader.readAsDataURL(file);
})(files[i]);
}});
When you call
$('.image-popup')
(with anything after the .) it will only apply to the elements that exist at that time. eg $('.image-popup').click(function() { alert("click"); }); will only put an event handler for those that exist so appending any new elements will not have that click handler (which is why we need to use event delegation for dynamically added elements).
The same applies here.
$('.image-popup').magnificPopup({
will tell magnificPopup only about the image-popups that exist at that time. When you add new ones later, it doesn't know about them.
So you need to recall your magnificPopup initialisation after you add the new elements.
I'm currently using AJAX with Django Framework.
I can pass asynchronous POST/GET to Django, and let it return a json object.
Then according to the result passed from Django, I will loop through the data, and update a table on the webpage.
The HTML for the table:
<!-- Modal for Variable Search-->
<div class="modal fade" id="variableSearch" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>
<h4 class="modal-title" id="myModalLabel">Variable Name Search</h4>
</div>
<div class="modal-body">
<table id="variableSearchTable" class="display" cellspacing="0" width="100%">
<thead>
<tr>
<th> Variable Name </th>
</tr>
</thead>
</table>
<p>
<div class="progress">
<div class="progress-bar progress-bar-striped active" id="variableSearchProgressBar" role="progressbar" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" style="width: 45%">
<span class="sr-only">0% Complete</span>
</div>
</div>
</p>
<p>
<div class="row">
<div class="col-lg-10">
<button class="btn btn-default" type="button" id="addSearchVariable" >Add</button>
</div>
</div>
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" id="variableSearchDataCloseButton" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
Basically it is a bootstrap 3 modal, with jQuery DataTable, and with a progress bar to show the user the current progress.
The Javascript that is used to get Django results:
$('#chartSearchVariable').click(function(event)
{
$('#chartConfigModal').modal("hide");
$('#variableSearch').modal("show");
var csrftoken = getCookie('csrftoken');
var blockname = document.getElementById('chartConfigModalBlockname').value;
$('#variableSearchProgressBar').css('width', "0%").attr('aria-valuenow', "0%");
event.preventDefault();
$.ajax(
{
type:"GET",
url:"ajax_retreiveVariableNames/",
timeout: 4000000,
data:
{
'csrfmiddlewaretoken':csrftoken,
'blockname':blockname
},
success: function(response)
{
if(response.status == "invalid")
{
$('#chartConfigModal').modal("hide");
$('#variableSearch').modal("hide");
$('#invalid').modal("show");
}
else
{
configurationVariableChart.row('').remove().draw(false);
for (i = 0 ; i < response.variables.length; i++)
{
configurationVariableChart.row.add(
$(
'<tr>' +
'<td>' + response.variables[i] + '</td>' +
'<tr>'
)[0]);
}
configurationVariableChart.draw();
$('#variableSearchProgressBar').css('width', "100%").attr('aria-valuenow', "100%");
}
},
failure: function(response)
{
$('#chartConfigModal').modal("hide");
$('#variableSearch').modal("hide");
$('#invalid').modal("show");
}
});
return false;
});
$('#addSearchVariable').click(function(event)
{
$('#variableSearch').modal("hide");
$('#chartConfigModal').modal("show");
document.getElementById('chartConfigModalVariable').value = currentVariableNameSelects;
});
$('#variableSearchDataCloseButton').click(function(event)
{
$('#variableSearch').modal("hide");
$('#chartConfigModal').modal("show");
});
The problem is with the updating table part:
configurationVariableChart.row('').remove().draw(false);
for (i = 0 ; i < response.variables.length; i++)
{
configurationVariableChart.row.add(
$(
'<tr>' +
'<td>' + response.variables[i] + '</td>' +
'<tr>'
)[0]);
}
configurationVariableChart.draw();
$('#variableSearchProgressBar').css('width', "100%").attr('aria-valuenow', "100%");
Since the response.variables can be over 10k, and it will freeze the web browser, even though it is still drawing.
I'm pretty new to Web Design (less than 4 months), but I assume it's because they are all running on the same thread.
Is there a way in Javascript to do threading/async? I had a search, and the results were deferred/promise which seems very abstract at the moment.
Try processing retrieved data incrementally.
At piece below , elements generated in blocks of 250 , primarily utilizing jQuery deferred.notify() and deferred.progress().
When all 10,000 items processed , the deferred object is resolved with the collection of 10,000 elements. The elements are then added to document at single call to .html() within deferred.then()'s .done() callback ; .fail() callback cast as null .
Alternatively , could append elements to the document in blocks of 250 , within deferred.progress callback ; instead of at the single call within deferred.done , which occurs upon completion of the entire task.
setTimeout is utilized to prevent "freeze the web browser" condition .
$(function() {
// 10k items
var arr = $.map(new Array(10000), function(v, k) {
return v === undefined ? k : null
});
var len = arr.length;
var dfd = new $.Deferred();
// collection of items processed at `for` loop in blocks of 250
var fragment = [];
var redraw = function() {
for (i = 0 ; i < 250; i++)
{
// configurationVariableChart.row.add(
// $(
fragment.push('<tr>' +
'<td>' + arr[i] + '</td>' +
'</tr>')
// )[0]);
};
arr.splice(0, 250);
console.log(fragment, arr, arr.length);
return dfd.notify([arr, fragment])
};
$.when(redraw())
// `done` callbacks
.then(function(data) {
$("#results").html(data.join(","));
delete fragment;
}
// `fail` callbacks
, null
// `progress` callbacks
, function(data) {
// log , display `progress` of tasks
console.log(data);
$("progress").val(data[1].length);
$("output:first").text(Math.floor(data[1].length / 100) + "%");
$("output:last").text(data[1].length +" of "+ len + " items processed");
$("#results").html("processing data...");
if (data[0].length) {
var s = setTimeout(function() {
redraw()
}, 100)
} else {
clearTimeout(s);
dfd.resolve(data[1]);
}
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<progress min="0" max="10000"></progress><output for="progress"></output>
<output for="progress"></output><br />
<table id="results"></table>
jsfiddle http://jsfiddle.net/guest271314/ess28zLh/
Deferreds/promises won't help you here. JS in the browser is always single-threaded.
The trick is not to build up DOM elements via JS. That is always going to be expensive and slow. Rather than passing data in JSON from Django and building up a DOM dynamically, you should get Django to render a template fragment on the server side and pass that whole thing to the front-end, where the JS can simply insert it at the relevant point.
I'm trying to sort a list of divs with the properties shown by particular attributes (gender, level, name etc) using the following script:
html:
<div id="sortThis" class="col-xs-12 alert-container">
<div id="1" class="container-element sortable box box-blue" data-gender="1" data-level="4" data-name="AAA"> <h3>AAA</h3><div class="panel-body">AAA is resp</div>
</div>
<div id="2" class="container-element sortable box box-pink" data-gender="2" data-level="3" data-name="DDD"><h3>DDD</h3><div class="panel-body">DDD is a s</div>
</div>
<div id="3" class="container-element sortable box box-blue" data-gender="1" data-level="2" data-name="FFF"><h3>FFF</h3><div class="panel-body">FFF has mad</div>
</div>
<div id="4" class="container-element sortable box box-pink" data-gender="2" data-level="4" data-name="CCC"><h3>CCC</h3><div class="panel-body">CCC has ma</div>
</div>
<div id="5" class="container-element sortable box box-pink" data-gender="2" data-level="2" data-name=EEE><h3>EEE</h3><div class="panel-body">EEE is a f</div>
</div>
<div id="6" class="container-element sortable box box-blue" data-gender="1" data-level="3" data-name="BBB"><h3>BBB</h3><div class="panel-body">BBB is an ou</div>
</div>
</div>
<button id="sLevel" class="LbtnSort">Sort by Level</button><br/>
<button id="sGender" class="GbtnSort">Sort by Gender</button><br/>
js:
var LdivList = $(".box");
LdivList.sort(function(a, b){
return $(a).data("level")-$(b).data("level")
});
var GdivList = $(".box");
GdivList.sort(function(a, b){
return $(a).data("gender")-$(b).data("gender")
});
/* sort on button click */
$("button.LbtnSort").click(function() {
$("#sortThis").html(LdivList);
});
/* sort on button click */
$("button.GbtnSort").click(function() {
$("#sortThis").html(GdivList);
});
when the .sortable divs are static, the sort works fine, as this jfiddle shows, however if the contents of the #sortable div (i.e. .sortable divs) are dynamically generated (in this case as the result of a form submit), when the sort button is pressed, the entire contents of the #sortable div disappears, and I can't seem to get it to work.
Any help or suggestions would be appreciated.
edit: The code for dynamic generation of the list is as follows - effectively it's an AXAX form submit that pulls data from a sorted list of items and outputs them.
$('#formStep2').submit(function(event) {
// get the form data
var studentArray = [];
$(".listbox li").each(function() {
studentArray.push({
'name': ($(this).text()),
'gender': ($(this).closest('ol').attr('id')).substr(0, 1),
'level': ($(this).closest('ol').attr('id')).substr(2, 3),
'topic': ($('input[name=topic]').val())
})
});
var studentString = JSON.stringify(studentArray);
console.log(studentString);
var formData = {
'students': studentString,
};
// process the form
$.ajax({
type: 'POST', // define the type of HTTP verb we want to use (POST for our form)
url: 'process_step2.php', // the url where we want to POST
data: formData, // our data object
dataType: 'json', // what type of data do we expect back from the server
encode: true
})
// using the done promise callback
.done(function(data) {
if (!data.success) {
// error handling to go here.....
} else {
$('.alert-container').empty();
var obj = JSON.parse(data.message);
//sort the array alphabetically by name (default status)
var test = obj.sort(function(a,b){
var lccomp = a.name.toLowerCase().localeCompare(b.name.toLowerCase());
return lccomp ? lccomp : a.name > b.name ? 1 : a.name < b.name ? -1 : 0;
});
console.log(test);
var i=0;
test.forEach(function(st) {
console.log(st['name']);
var gen = (st['gender'] == 1) ? "blue" : (st['gender'] == 2) ? "pink" : NULL;
$('.alert-container').append('<div id="' + (i+1) + '" class="container-element sortable box box-' + gen + '" data-gender="' + st['gender'] + '" data-level="' + st['level'] + '" data-name="' + st['name'] + '"><h3>' + st['name'] + '</h3><div class="panel-body"><div class="col-xs-9"><i class="fa fa-quote-left fa-3x fa-pull-left fa-' + gen + '" aria-hidden=:true"></i>' + st['comment'] + '</div></div></div>');
i++;
});
// jump to the next tab
var $active = $('.wizard .nav-tabs li.active');
$active.next().removeClass('disabled');
nextTab($active);
}
})
// using the fail promise callback
.fail(function(data) {
// show any errors
// best to remove for production
console.log(data);
});
// stop the form from submitting the normal way and refreshing the page
event.preventDefault();
});
You are defining LdivList and GdivList inline with your code so they are defined on DOM ready. You have to wrap the definition of those inside a function and call it on click:
$(document).ready(function(){
$("button.LbtnSort").click(function() {
$("#sortThis").html(GenerateLdivList);
});
/* sort on button click */
$("button.GbtnSort").click(function() {
$("#sortThis").html(GenerateGdivList());
});
});
function GenerateLdivList(){
var LdivList = $(".box");
LdivList.sort(function(a, b){
return $(a).data("level")-$(b).data("level")
});
}
function GenerateGdivList(){
var GdivList = $(".box");
GdivList.sort(function(a, b){
return $(a).data("gender")-$(b).data("gender")
});
}
As #theduke said, the lists are probably empty at the time you sort them. Here's a simple change that will read and sort the lists when you click the buttons instead. (Not tested.)
var LdivList = function () {
return $(".box").sort(function(a, b){
return $(a).data("level")-$(b).data("level")
});
};
var GdivList = function () {
return $(".box").sort(function(a, b){
return $(a).data("gender")-$(b).data("gender")
});
};
/* sort on button click */
$("button.LbtnSort").click(function() {
$("#sortThis").html(LdivList());
});
/* sort on button click */
$("button.GbtnSort").click(function() {
$("#sortThis").html(GdivList());
});
Hello i have the following code with problems, i'm trying to make it when you click on the output to insert it into the input field. Can you help me please, been trying for hours without any luck.
<script type="text/javascript">
var input = $('#CompanyName');
var output = $('#output');
var timer;
input.on('keyup', function() {
delaySearch(this.value);
});
function delaySearch(keywords) {
clearTimeout(timer);
timer = setTimeout(function() {
performSearch(keywords);
}, 1000);
}
function performSearch(keywords) {
$.ajax({
type: "POST",
url: "/print/order/search",
data: { query: keywords },
cache: false,
dataType: "json",
async: true,
success: function(data) {
for(var key in data) {
output.append('<li onclick="fill('+ data[key].ClientName +')">' + data[key].ClientName) + '</li>';
}
}
});
}
function fill(thisValue) {
input.val(thisValue);
clearTimeout(timer);
}
</script>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label" for="CompanyName">Firma</label>
<div class="col-md-5">
<input id="CompanyName" onblur="fill();" name="CompanyName" type="text" placeholder="Firma" class="form-control input-md">
<ul id="output"></ul>
<span class="help-block"></span>
</div>
</div>
Uncaught ReferenceError: somevalue is not defined
Update:
After adding jquery ready function i noticed some errors around and fixed them here is an update on the code
<div class="form-group">
<label class="col-md-4 control-label" for="CompanyName">Firma</label>
<div class="col-md-5">
<input id="CompanyName" name="CompanyName" type="text" placeholder="Firma" class="form-control input-md">
<ul id="output"><li onclick="fill(Ionut)">Ionut</li></ul>
<span class="help-block">Nume Firma</span>
</div>
</div>
<script type="text/javascript">
$(document).ready(function() {
var input = $('#CompanyName');
var output = $('#output');
var timer;
input.on('keyup', function() {
delaySearch(this.value);
});
function delaySearch(keywords) {
clearTimeout(timer);
timer = setTimeout(function() {
performSearch(keywords);
}, 1000);
}
function fill(thisValue) {
input.val(thisValue);
}
function performSearch(keywords) {
$.ajax({
type: "POST",
url: "/print/order/search",
data: { query: keywords },
cache: false,
dataType: "json",
async: true,
success: function(data) {
for(var key in data) {
output.append('<li onclick="fill(' + data[key].ClientName + ')">' + data[key].ClientName) + '</li>';
}
}
});
}
});
</script>
onclick the error persists
Uncaught ReferenceError: fill is not defined
realseanp is onto the correct answer. I'll try to explain it a little better for you. When a browser starts processing and rendering a page, it loads top down. So your javascript scripts are being ran and evaluated before the DOM is created.
So your jquery selectors: var input = $('#CompanyName'); if you were to inspect them are going to be an empty array. They cannot find the #CompanyName element because it has not yet been rendered.
If you use jQuery's $(document).ready() function, then you can be assured that your code will not run until the dom is finished rendering, and therefore will find the elements as you intend them to. So in the end, your code will need to change to this:
$(document).ready(function(){
//Put your code in here.
//It will then fire once the dom is ready.
});
UPDATE:
Additionally, with your update. I'm noticing that the error is that 'fill' is not defined. fill being your onclick method. You have your js script evaluating after the dom is rendered. So at the time that the dom is rendered, and the tag with the onclick is rendered, no fill method yet exists. Two solutions:
Move the script above the dom, and place a var fill; outside of the $(document).ready so essentially this:
var fill;
$(document.ready(function(){
//your code
});
Don't use the onclick dom attribute, and instead use jquery to bind the event. So change
Ionut
to this:
<ul id="output"><li>Ionut</li></ul>
and inside the document.ready, add:
$('#output li').click(function(e) {
fill(/*your value/*)
});
You need to put your script below your HTML. That or wrap it in the jQuery Document ready function. And make sure you have jQuery loaded on the page, before your script
I have a structure as below:
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Tiger128 (v2)</h3>
</div>
<div class="panel-body">
<input class="form-control" id="tiger128v2" placeholder="String to Hash" type="text">
</div>
<div class="panel-footer">
<a class="btn btn-primary generate-hash" data-hash="tiger128v2">Generate Hash</a>
</div>
</div>
When a user presses the <a> it runs a jQuery function (using $('.generate-hash') selector) which makes a $.getJSON request passing data-hashtype. When it returns the JSON object I need it to append some text to <div class="panel-body"> however as you can see none of them have ID's.
What I have tried is something along the lines of:
$(this).parent().prev('.panel-body').append('Appending some text');
$(this).parent().parent().siblings(".panel-body").append('test');
But I cannot get it to work. Any suggestions?
$('.generate-hash').click(function (e) {
e.preventDefault();
var hashtype = $(this).data('hash');
var string = $(this).closest('.panel').find('input').val();
console.log('Started hash request for ' + hashtype + ' with a value of ' + string);
$.getJSON('ajax/hash.php', {
hashtype: hashtype,
string: string
})
.success(function(data) {
console.log('success function');
if(data.type == 'success'){
// Here is where i need to select the parents
$(this).parent().parent().siblings(".panel-body").append('test');
$(this).parent().prev('.body').append('<div class="alert alert-info"><strong>Generated Hash:</strong> <code>' + data.hash + '</code></div>');
console.log('success msg found');
}else{
$(this).parent().prev('.body').append('<div class="alert alert-' + data.type + '">' + data.msg + '</div>');
console.log('error msg found');
}
})
.fail(function() {
$(this).parent().prev('.body').append('<div class="alert alert-error"><strong>Error:</strong> We could not generate the hash for some reason. The details are below:</div>');
console.log('unable to find hash.php or other error');
});
});
You can use .prev() method:
$(this).parent().prev('.body').append('Something');
Or .closest() method:
$(this).closest('.box').find('.body').append('Something');
Edit:
You should cache the this object, within the context of the Deferred object's handlers this doesn't refer to the clicked element, also replace .success() with .done():
$('.generate-hash').click(function (e) {
// ...
var $this = $(this);
$.getJSON('ajax/hash.php', {
hashtype: hashtype,
string: string
})
.done(function(data) {
console.log('success function');
if(data.type == 'success'){
// Here is where i need to select the parents
$this.parent().parent().siblings(".panel-body").append('test');
// ...
})
.fail(function() {
$this.parent()...
});
});
use closest
closest() selects the first element that matches the selector, up from the DOM tree while parent() selects all the elements that are parent of another element in the DOM tree.
$(this).closest('.box').find('.body').append('Something');
reference closest
try something like this, try prev()
$(this).parent().prev().append('Something');
maybe pure javascript might work
var box = this.parentNode.parentNode,
body = box.getElementsByClassName('body')[0];
body.innerHTML += 'Something';
Try this,
$(this).parents('.box').find('.body').append('Something');
or
$(this).closest('.box').find('.body').append('Something');
Read parents() and closest()