Aylien News API & Swiper - Pulling content in realtime without slider messing up - javascript

I am currently using the Aylien News API (https://newsapi.aylien.com/docs/introduction) and Swiper.js slideshow (http://idangero.us/swiper/#.WZwPaZOGPao) to create a sliding news ticker.
The problem I have is that the slider runs through all of the divs one after the other, but when new content comes in, the slider starts jumping between divs and not running through them.
My HTML consists of 3 divs - #aylien-output, then #results, then #story all nested within each other. Each news post is contained within its own #story div which gets created as the data gets pulled in.
At the moment, my Javascript looks like this:
$(document).ready(function(){
var divListChildren = '';
$('results').find('a').each(function(index,ele){
divListChildren += ele.innerHTML + '';
})
console.log(divListChildren);
})
function getData() {
fetch('http://localhost:3010/test', myInit).then(function(response) {
return response.json();
}).then(function(res) {
fetchedStories = res.stories.filter(
function(story) {
return ! fetchedStories.find (function(fetchedStory) {
return story['id'] === fetchedStory['id'];
});
}
).concat(fetchedStories);
res.stories.map(makeStory);
runSlider();
});
}
getData();
const timeOutTime = 60000;
setTimeout(function doSomething() {
console.log('getting new stuff!');
getData();
setTimeout(doSomething, timeOutTime);
}, timeOu
tTime);
Is there any way of being able to refresh the content in realtime without the slider skipping? Or waiting for the slider to loop through the last div and wait for new content until it starts from the beginning again?
I don't want to refresh the page every time as I want it to look seamless.
Thank you!

Related

Trying to move an element created after document load into another element

I'm working on this website: https://nuebar.com/ and I've just installed an app by shopify the places their currency selector right at the very bottom of the site, and I'm trying to move it into a div class .CSPosition that's been placed in a list item next to the current currency selector in the header.
Doing this because none of my international customers are finding the selector at the bottom of the site and the one at the top of the site doesn't change checkout currency, so will be removing it once I can move the one at the bottom successfully.
I've written this:
document.getElementsByClassName("locale-selectors__container").addEventListener("load", moveUp);
function moveUp () {
"use strict";
$(".locale-selectors__container").insertBefore(".CSPosition");
}
to try and move it once it's been loaded since it's being loaded by a third party but it's not working.
I've also tried:
$(".locale-selectors__container").load(function(){
$(".locale-selectors__container").insertBefore(".CSPosition");
});
I've also tried doing it when everything has loaded:
$( window ).on(function(){
$(".locale-selectors__container").insertBefore(".CSPosition");
});
Is it because of limitations with elements inserted by a third party of am I making stupid mistakes? Is there something wrong with my two methods?
setTimeout example
document.addEventListener('DOMContentLoaded', (event) => {
waitForSource();
});
function waitForSource(){
setTimeout(function(){
const source = document.querySelector('.locale-selectors__container');
if (!source) {
waitForSource();
}else{
const destination = document.querySelector('.CSPosition');
destination.append(source);
}
}, 350);
}
setInterval example
let checkSourceInterval = null;
document.addEventListener('DOMContentLoaded', (event) => {
checkSourceInterval = setInterval(() => {
const source = document.querySelector('.locale-selectors__container');
if (!!source) {
const destination = document.querySelector('.CSPosition');
destination.append(source);
clearInterval(checkSourceInterval);
}
}, 350);
});
I ended up with this:
setTimeout(function(){
console.log('DOM fully loaded and parsed');
const source = document.querySelector('.locale-selectors__container');
const destination = document.querySelector('.CSPosition');
destination.append(source);
}, 5000);
The setTimeout was the only way to circumvent and document/window/DOM loads that the app has on their end.
Now onto styling.

start second page where first page end in javascript

I have some issue with the window scroll.
I created a news feed like page using angularjs with database and pagination and ng-repeat.
I want that when user click on load more button window will scroll to last position of the page 1 and then page 2 start, but I tried all option but it not working like what I want.
But it always send me last of the document.
Here is the code for reference...
$scope.feedCopy=function(){
$scope.loader=false;
$http.get('http://web.com/app/get_feeds/1/'+$scope.member_id+'?page='+$scope.feeds_current_page).then(function(response){
$scope.feeds = $scope.feeds.concat(response.data.data);
$scope.feeds2=angular.copy($scope.feeds);
$current_index=$scope.feeds2[0].feed_id;
var i=0;
$scope.showLess=true;
$scope.showMore=false;
for(i;i<$scope.feeds2.length;i++)
{
$scope.feeds2[i].show_less=1;
if($scope.feeds[i].feed_text!=null)
{
$scope.original_length=$scope.feeds2[i].feed_text.length;
$scope.feeds2[i].feed_text=$scope.feeds[i].feed_text.substring(0,40);
$scope.temp_length=$scope.feeds2[i].feed_text.length;
if($scope.temp_length>=$scope.original_length)
{
$scope.feeds2[i].show_less_button=1;
}
}
else
{
$scope.feeds2[i].show_less=-1;
}
}
$scope.feeds_total_page=response.data.total_pages;
if($scope.feeds_total_page>$scope.feeds_current_page)
{
$scope.feeds_current_page+=1;
$scope.load_more=true;
}
$scope.feed_condition_current_page++;
});
$scope.iframeHeight = document.documentElement.scrollHeight;
window.scrollTo(0,($scope.iframeHeight))
//alert($scope.iframeHeight);
}
Inject $window service in to your controller.
$window.open("your_path/", "_blank")

Node / Express - Append information to list when user hits bottom of page using Snoocore (Reddit API wrapper)

I am working on a private application of a food sub reddit, what I'm retrieving some images witha limit of 20 images per view and when the user reach the bottom of the page it loads more information and appends it, like ajax; I'm doing the request server side and using Snoocore which is an API wrapper for Reddit. Snoocore actually provides a method to fetch information of the next page. From Snoocore's documentation:
// Instead of an HTTP verb, use a Snoocore helper `.listing`
// It will return a "slice" for a listing that has various
// helpers attached to it.
reddit('/r/askreddit/hot').listing().then(function(slice) {
// This is the front page of /r/askreddit
// `slice.children` contains the contents of the page.
console.log(slice.children);
// Get a promise for the next slice in this
// listing (the next page!)
return slice.next();
}).then(function(slice) {
// This is the second page of /r/askreddit
console.log(slice.children);
});
And this is my code:
reddit('/r/food/hot').listing({limit: 10}).then(result => {
for (var x in result.children){
if (result.children[x].data.link_flair_text === '[Homemade]' || result.children[x].data.link_flair_text === '[homemade]') {
if (result.children[x].data.domain !== 'imgur.com') {
if (result.children[x].data.post_hint !== "rich:video") {
titles.push({
'title': he.decode(result.children[x].data.title),
'imgurl': he.decode(result.children[x].data.url),
'user': result.children[x].data.author,
'submission': result.children[x].data.permalink
});
}
}
}
}
console.log(result.children[4].data);
res.render('index', {
'titleArr': titles
});
});
on my Pug template I have:
extends layout
block content
div#wrap
each i in titleArr
figure.figure
img.img-fluid.img-rounded(src= i.imgurl)
figcaption.figure-caption.text-center
p.text-muted #[i #{i.title}]
p
| #[i ―by ]
a(href="http://reddit.com" + i.submission, target="_blank")
| #[i /u/#{i.user}]
listing({limit: 10}) here is important because, well, it limits the post retrieved; actually I'm retrieving only 10, by default it retrieves 25; anyway, I'm looking for a way, how i said that when the user reach bottom of the page it loads more post; do I have to do this via client side? Or can I do this via server side in some way? Tell me if I need to explain my issue more in depth.
What you are looking for is "infinite scrolling", you basically want to do another request when your user reaches the bottom of the page that has been rendered.
This is going to require you to fire an ajax request when your browser scroll reaches the bottom of the page. If you were to use jQuery, your code would look something like this:
$(document).ready(function() {
var win = $(window);
var parts = location.pathname.split("/");
// assuming a url like domain.com/reddit/10 where 10
// is the total number of posts you're showing
var pageNumber = parts[3]; //this gives you access to the `10`
// then add 10 to the current page number and
pageNumber += 10
// use that for your ajax request
var url = "http://domain.com/reddit/"+pageNumber;
// Each time the user scrolls
win.scroll(function() {
// End of the document reached?
if ($(document).height() - win.height() == win.scrollTop()) {
$('#loading').show();
$.ajax({
url: url,
dataType: 'json',
success: function(json) {
// do something with your json of your posts
// then hide the loading gif or image.
$('#loading').hide();
}
});
}
});
});
This will fire the ajax request whenever your user reaches the bottom of the page, and paginate correctly. This is only the browser side, though.
You will need to edit your server endpoint slightly as well.
For example, you need to increase your limit dynamically
//you could use the req.params method from express to accomplish this
reddit('/r/food/hot').listing({limit: req.params.limit}).then(result => {
for (var x in result.children){
if (result.children[x].data.link_flair_text === '[Homemade]' || result.children[x].data.link_flair_text === '[homemade]') {
if (result.children[x].data.domain !== 'imgur.com') {
if (result.children[x].data.post_hint !== "rich:video") {
titles.push({
'title': he.decode(result.children[x].data.title),
'imgurl': he.decode(result.children[x].data.url),
'user': result.children[x].data.author,
'submission': result.children[x].data.permalink
});
}
}
}
}
console.log(result.children[4].data);
res.render('index', {
'titleArr': titles
});
});
Accessing the req.params.limit variable should work. You will need to edit your endpoint definition as well. I'm not sure how you have your server's routing setup right now, but this would be one way you could do it:
app.get('/reddit/posts/:limit', function(req, res) {
console.log('limit is: ', req.params.limit);
})
The idea here is that you want to increase the number of items you're fetching with each jQuery request. this is a pretty quick and dirty way of doing so, and I'm making some assumptions about how you're handling the server and what you're using, but this should get you 90% of the way there. Hope this helps!

trigger functions no longer working after implement ajax scroll pagination

I have developed an audio platform similar to Soundcloud and it all works(ed) perfectly! Until I decided to create an Ajax scroll pagination.
Both the pagination and Ajax work fine. However, I have noticed some JavaScript that used to work before implementing Ajax, which no longer does.
The script has a pretty simple duty; play a track when the user clicks the play button (or pause when the user clicks on it again). Then, once the track has finished, move on to the next track until it finally reaches the end.
What happens now is, when the page first loads up (along with the 10 tracks that load with the page), the script will work as it is supposed to. But, when the user scrolls down to get more results to load, if the user clicks on one of the newly loaded tracks play button, the track either won't play, or it will play over the other track which is supposed to pause (and then all the buttons just completely stop working).
Here is all of the feed.js (removed as much bloat code as possible, and placed comments):
$(document).ready(function(){ // on page load
var tp = 1; // set track page equal to one
loadTracks(tp); // then load all of the tracks
jQuery(function($) {
$('.f-outer-container').on('scroll', function() { // when the user scrolls to the bottom of the page load more tracks
if($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight) {
tp++;
loadTracks(tp);
}
});
});
function loadTracks(track_page){
$.post('/spectrum-rr/core/_func/functions/loadTrack.php', {'page': tp}, function(data){ // get send and get data from loadTrack.php
$("#f-ap__aj").append(data); // append those tracks in the feed
// player functions
$(".track-w__trigger").click(function(){ // when the play button is clicked
var tid = $(this).attr('aria-trackid'), // get its track id
tiW = 'w' + tid + 'w',
tiW = eval(tiW); // set the waveform object
playPauseButton(this, tiW, tid);
});
});
}
});
// player functionality
function playPauseButton(button, wave, trackID){ // once the function has been called
pausePrevious(button); // pause the previous track (this doesn't work when more ajax results are loaded)
var button = $(button);
if(wave.isPlaying()){ // if the wave is playing; stop it
button.removeClass("playing");
wave.pause();
} else { // vice versa
button.addClass("playing");
wave.play();
}
var waveDuration = wave.getDuration();
var nextTrack = ++trackID;
var checkAudioFinished = setInterval(function(){ // check if the audio has finished playing every second
if(wave.getCurrentTime() >= waveDuration){ // if it has
button.removeClass("playing"); // remove it's buttons "playing" class
$('#w' + nextTrack + 'w-trigger').trigger('click'); // play the next song on the playlist by incrementing the id
clearInterval(checkAudioFinished);
}
}, 1000);
}
function pausePrevious(b){
$(".playing").not(b).each(function(){ // when this function is triggered
$(".playing").trigger('click'); // pause all of the other tracks (by simulating the click of their pause buttons
$(".playing").removeClass("playing"); // remove it's class too
});
}
I feel these problems are occurring due to the use of $(document).ready();. Forcing these functions to only be available to those tracks that were already loaded. However, I am not sure.
Here is the HTML that gets sent back from each request (in 10s):
<div class="f-wave-send f-waveform-container">
<div aria-trackid="1" class="track-w__trigger" id="w1w-trigger"></div> <!-- the "1" is generated by PHP. it is incremented for every div !-->
<div class="f-waveform-outer-container">
<div aria-trackid="1" class="track-w__waveform" id="w1-w"></div>
<script>
var w1w = WaveSurfer.create({ // wavesurfer script (this is the "wave" object that is being triggered inside the playPauseButton() function !-->
container: '#w1-w',
barWidth: 2,
});
w1w.load('./player/audio/sampleaudio.mp3');
</script>
</div>
</div>
If anyone could give me insight as to what might be going on (or any tips in improving my code for that matter), it would be greatly appreciated!
Try this,
You should not bind an click event each time load happens and it should be moved out of your loadTracks, instead you shuold apply event delegation.
// player functions
$(document).on('click', '.track-w__trigger', function(){ // when the play button is clicked
var tid = $(this).attr('aria-trackid'), // get its track id
tiW = 'w' + tid + 'w',
tiW = eval(tiW); // set the waveform object
playPauseButton(this, tiW, tid);
});
Change your code to this
$(document).ready(function(){ // on page load
var tp = 1; // set track page equal to one
loadTracks(tp); // then load all of the tracks
jQuery(function($) {
$('.f-outer-container').on('scroll', function() { // when the user scrolls to the bottom of the page load more tracks
if($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight) {
tp++;
loadTracks(tp);
}
});
});
function loadTracks(track_page){
$.post('/spectrum-rr/core/_func/functions/loadTrack.php', {'page': tp}, function(data){ // get send and get data from loadTrack.php
$("#f-ap__aj").append(data); // append those tracks in the feed
});
}
// player functions
$(".track-w__trigger").on("click",function(){ // when the play button is clicked
var tid = $(this).attr('aria-trackid'); // get its track id
tiW = 'w' + tid + 'w';
tiW = eval(tiW); // set the waveform object
playPauseButton(this, tiW, tid);
});
});
Just move out $(".track-w__trigger").click outside $.post and change it to $(".track-w__trigger").on("click",function()

Preventing reloading Ajax data when back-button is clicked in both Chrome and IE

I am currently working on a website in which the home page displays the most recent 10 blog entries. When I scroll down, and when I reach almost the end of the last item on screen, another 10 blog entries are automatically loaded, and so on (this is the infinite scrolling feature).
If a user clicks on any blog entry, then he/she is taken to another page to display the details about that blog entry. When the user clicks the back button, he/she is taken to the home page that has the entries displayed.
Please note, the home page loads the data using Ajax.
Assume the following scenario:
A user goes to the site, and entries 1 to 10 are loaded (via Ajax).
The user scrolls down, and the next 10 entires, specifically entries 11 to 20 are loaded (via Ajax as well). Note that the page now has entires 1 to 20 displayed.
The user scrolls further down, and now entries 21 through 30 are loaded, for a total of 1 through 30 blog entries being displayed on the page.
The user clicks on entry 25, and the page for entry 25 is displayed.
The user clicks the back button, and all items, 1 through 30 are displayed.
Now, if the users uses FireFox, Opera, or Safari, and when that user performs step 5 (i.e, clicks the back button to go back to the home page) then the blog entries are just displayed on screen, and without being re-loaded. However using IE and on Chrome, when the user clicks on the back button, the page is reloaded, and only items 1 through 10 are displayed.
I do not like the IE and Chrome behaviour. The user should see items 1 though 30. How can I ensure all browsers behave like FireFox?
Thanks.
Update
Here is the code I am using
First, hers is my html
<html>
<body>
<section id="content">
<section id="articles">
<!-- This section is filled by jQuery/Ajax -->
</section>
<section id="loading-spinner">
<img src="ajax-loader.gif" />
</section>
</section>
</body>
</html>
And here is my jQuery
/**
*
* This file uses a bunch of programming concepts, but the most important one is ensuring ajax calls run as a critical section
* ... (this means if ajax is already called, then another instance of JavaScript cannot get into the critical section)
*
* .. For more details, please read: http://stackoverflow.com/questions/22150960/critical-section-in-javascript-or-jquery
*
*/
load_more_posts = function () {
// If we almost reach the bottom of the document, then load more posts
if ( jQuery(window).scrollTop() >= jQuery(document).height() - jQuery(window).height() - 300) {
// If the value of the promise is not pending, then we can call the load_posts function (the load_posts function will change the status to pending when it is executing the ajax request)
if (ajax_status.state() !== "pending") {
load_posts();
}
}
};
function load_posts() {
ajax_status = jQuery.ajax({
type: 'post',
dataType: 'json',
beforeSend: function(xhr) {
if(jQuery.data(document.body, 'load_page') == false) {
xhr.abort();
}
else {
// Show the spinner
jQuery('#loading-spinner').visible();
}
},
url: '../link/to/get_poasts.php',
data: {
action: 'load_posts',
js_query_data: query_data,
js_page: jQuery.data(document.body, 'page_to_load')
},
success: function (response) {
if (response.isSuccessful) {
var number_of_post_items = response.posts_array.length;
for (var i = 0; i < number_of_post_items; i++) {
// If the item is already returned from the database and posted. then we skip it, otherwise, keep insert a new record
if (jQuery('#articles').find('#article-' + response.posts_array[i].post_id).length == 0) {
// Add 'article'
jQuery('#articles').append('<article id="article-' + response.posts_array[i].post_id + '"></article>');
// More code here to add details about each article, such as title, excerpt...etc.
}
}
// Increase the value of the page to load by 1, and save it.
page = jQuery.data(document.body, "page_to_load");
page = page + 1;
jQuery.data(document.body, "page_to_load", page);
jQuery(window).on('scroll', load_more_posts);
}
else {
// Display error message
jQuery('#articles').append('<div>' + response.message + '</div>');
// Make sure no further AJAX requests are made
jQuery.data(document.body, 'load_page', false);
}
}
}).always(function() {
// Hide the spinner
jQuery('#loading-spinner').invisible();
});
return ajax_status;
}
// Create a new promise. This will be used to ensure that no two calls hit the critical section at the same time
// ... (the critical section in this case is the time when we retrieve data from the database. We only want one call at a time)
var ajax_status = new jQuery.Deferred();
jQuery(document).ready(function() {
// Hide the loading spinner first
jQuery('#loading-spinner').invisible();
// We resolve the promise, making sure it is ready (this is an intial state)
ajax_status.resolve();
// Initial values that are used
jQuery.data(document.body, 'page_to_load', 1);
// This parameter is used to stop loading pages when no more items are available to be displayed
jQuery.data(document.body, 'load_page', true);
// Initial loading of poasts
load_posts();
// Enable on scrolling to load more pasts (to allow infinite scrolling)
jQuery(window).on('scroll', load_more_posts);
});
You'll find some information about how it works in the "good" browsers in this answer:
Is there a cross-browser onload event when clicking the back button?
Here you'll find a way to emulate it in IE:
Differences in Internet Explorer and Firefox when dynamically loading content then going forward and back
Not quite sure why it doesn't work in Chrome though.
Just so everyone knows, here is the solution I came up with that is consistent in all browsers. Unfortunately this solution requires a reloead/refresh button to reload the data. I tried to avoid that but could not. Until both IE and Chrome tackle the bfcache issue, I will stick to this solution.
First, here is the new html
<html>
<body>
<section id="content">
<a id="refresh">
<img src="link/to/refresh.png" title="Refresh" alt="refresh" />
</a>
<section id="articles">
<!-- This section is filled by jQuery/Ajax -->
</section>
<section id="loading-spinner">
<img src="ajax-loader.gif" />
</section>
</section>
</body>
</html>
And this is the javascript
/**
*
* This file uses a bunch of programming concepts, but the most important one is ensuring ajax calls run as a critical section
* ... (this means if ajax is already called, then another instance of JavaScript cannot get into the critical section)
*
* .. For more details, please read: http://stackoverflow.com/questions/22150960/critical-section-in-javascript-or-jquery
*
*/
load_more_posts = function () {
// If we almost reach the bottom of the document, then load more posts
if ( jQuery(window).scrollTop() >= jQuery(document).height() - jQuery(window).height() - 300) {
// If the value of the promise is not pending, then we can call the load_posts function (the load_posts function will change the status to pending when it is executing the ajax request)
if (ajax_status.state() !== "pending") {
load_posts();
}
}
};
function load_posts() {
ajax_status = jQuery.ajax({
type: 'post',
dataType: 'json',
beforeSend: function(xhr) {
if(jQuery.data(document.body, 'load_page') == false) {
xhr.abort();
}
else {
// Show the spinner
jQuery('#loading-spinner').visible();
}
},
url: '../link/to/get_poasts.php',
data: {
action: 'load_posts',
js_query_data: query_data,
js_page: sessionStorage.getItem("page_to_load")
},
success: function (response) {
if (response.isSuccessful) {
var number_of_post_items = response.posts_array.length;
for (var i = 0; i < number_of_post_items; i++) {
// If the item is already returned from the database and posted. then we skip it, otherwise, keep insert a new record
if (jQuery('#articles').find('#article-' + response.posts_array[i].post_id).length == 0) {
// Add 'article'
jQuery('#articles').append('<article id="article-' + response.posts_array[i].post_id + '"></article>');
// More code here to add details about each article, such as title, excerpt...etc.
var history_session = get_history_session_name();
var history = sessionStorage.getItem(history_session);
var article_content = jQuery('#articles').find('#aarticle-' + response.posts_array[i].post_id)[0].outerHTML;
sessionStorage.setItem(history_session, history + article_content);
}
}
// Increase the value of the page to load by 1, and save it.
page = parseInt(sessionStorage.getItem("page_to_load"));
page = page + 1;
sessionStorage.setItem("page_to_load", page);
jQuery(window).on('scroll', load_more_posts);
}
else {
// Display error message
jQuery('#articles').append('<div>' + response.message + '</div>');
// Make sure no further AJAX requests are made
jQuery.data(document.body, 'load_page', false);
}
}
}).always(function() {
// Hide the spinner
jQuery('#loading-spinner').invisible();
});
return ajax_status;
}
function get_history_session_name () {
session_name = 'history___' + escape(location.href);
return session_name;
}
// Create a new promise. This will be used to ensure that no two calls hit the critical section at the same time
// ... (the critical section in this case is the time when we retrieve data from the database. We only want one call at a time)
var ajax_status = new jQuery.Deferred();
jQuery(document).ready(function() {
// Hide the loading spinner first
jQuery('#loading-spinner').invisible();
// We resolve the promise, making sure it is ready (this is an intial state)
ajax_status.resolve();
// This parameter is used to stop loading pages when no more items are available to be displayed
jQuery.data(document.body, 'load_page', true);
// Get the name of the history session
var history_session = get_history_session_name();
if (sessionStorage.getItem(history_session) === null) {
// Set the history session storage
sessionStorage.setItem(history_session, "");
// Initial values that are used
sessionStorage.setItem("page_to_load", 1);
// Load the posts
load_posts();
}
// Load from history when the back button is clicked
else {
jQuery('#articles').append(sessionStorage.getItem(history_session));
}
// Enable on scrolling to load more pasts (to allow infinite scrolling)
jQuery(window).on('scroll', load_more_posts);
// Reload data when the refresh button is clicked
// ... We are using a refresh button because if we go to a page that already had history, ...
// ... and even though we went to that page without using the back button (i.e, via links or directly via the url), ...
// ... then the history session will be displayed. ...
// ... Therefore a reload button is needed to overcome this problem if you like to reload data
jQuery("#refresh").click(function () {
// Reset/clear the articles section first
jQuery('#articles').html("");
// reset the 'load_page' variable
jQuery.data(document.body, 'load_page', true);
// Reset/clear the history session storage
sessionStorage.setItem(history_session, "");
// Start with loading page 1
sessionStorage.setItem("page_to_load", 1);
// Load the posts
load_posts();
});
});
Hope this helps.

Categories

Resources