Inputting JSON Data In Element's Containing Same ID - Troubleshooting - javascript

Currently I have JSON data that is being inputted/entered into all articles containing the ID #viewed. While the JSON data is showing that it is being inputted into all article's with the ID, the function checkViewers() is only functioning correctly for the first article ID #viewed.
Ideally, the checkViewers() function should make all #viewed IDs appear as the first article ID #viewed is currently appearing (e.g. First name Last name and Remaining Number of People have viewed this post.) However, the total number of people remaining is incorrect in the first article, as it is gathering all the repeated data. It should only be gathering the data once and totaling that number per article ID.
What is the best fix for this situation? I am guessing the checkViewers() function is gathering all the data on the page and only needs to be gathering the data from it's parent section?
A sample of the current code:
//Content Viewer Information
function checkViewers() {
//Base Variables
//var viewer = $('#viewed span.user');
//var totalViews = $('#viewed span.user').length;
//var shortenViews = $('#viewed span.user').length -1;
var viewer = $("#viewed span[class^='user']");
var totalViews = $("#viewed span[class^='user']").length;
var shortenViews = $("#viewed span[class^='user']").length -1;
if (totalViews === 0) {
($('#viewed').html('<span> 0 people have viewed your post.</span>'));
}
if (totalViews === 1) {
$('<span> has viewed your post.</span>').insertAfter(viewer.last());
}
if (totalViews === 2) {
$('<span> and </span>').insertAfter(viewer.first());
$('<span> have viewed your post.</span>').insertAfter(viewer.last());
}
if (totalViews >= 3) {
viewer.slice(1).hide();
$('<span> and </span>').insertAfter(viewer.first());
$('<span class="user count"></span>').insertAfter(viewer.eq(2));
$('.count').html(shortenViews + ' more people');
$('<span> have viewed your post.</span>').insertAfter(viewer.last());
}
}
The function is then being called with the updated content.
//Update Page With New Content
var viewerSection = $("article[id^='viewed']");
viewerSection.html(newViewers);
checkViewers();
Edits: I ended up changing the IDs #viewed to the Class .viewed, as IDs should be unique. However, I am still having the same problem as before.
View the current and complete code at Plunker.

Firstly, now you are correctly using classes, you shouldn't check the class attribute like this:
var viewerSection = $("div[class^='viewed']");
Just use the basic jQuery class selector:
var viewerSection = $("div.viewed");
You should also do the same for the other 3 selectors at the top of your script as you have commented out.
Now the main problem is that you are not restricting your checks in checkViewers to each individual item, so it is applying it globally.
You need to loop through each .viewed element and apply your logic to each. The jQuery selector method takes a second argument which is an element to search within for matches. As you are in a jQuery each() method, you can just pass this as the second argument:
function checkViewers() {
$('div.viewed').each(function() {
var viewer = $("span.user", this);
// no need to re-select, just work them out based on viewer
var totalViews = viewer.length;
var shortenViews = viewer.length -1;
if (totalViews === 0) {
$(this).html('<span>0 people have viewed your post.</span>');
}
else if (totalViews === 1) {
$(this).append('<span> has viewed your post.</span>');
}
else if (totalViews === 2) {
$('<span> and </span>').insertAfter(viewer.eq(0));
$(this).append($('<span> have viewed your post.</span>'));
}
else if (totalViews >= 3) {
viewer.slice(1).hide();
$('<span> and </span>').insertAfter(viewer.eq(0));
$('<span class="user count">' + shortenViews + ' more people</span>').insertAfter(viewer.eq(2));
$(this).append($('<span> have viewed your post.</span>'));
}
});
}
Updated Plunker

Related

How to combine two javascripts?

Would anyone be so kind as to advise me how to amend this JavaScript please? I'll admit I don't have much experience working with JavaScript and I've tried myself but ended up a bit lost.
To explain, WooCommerce outputs products on my site in .columns-3 and .columns-4, and assigns .first and .last classes accordingly.
If the site is loaded on mobile, the script below will remove the .first and .last tags, and re-assign them to display the products in two columns.
The script currently only targets .columns-3 within function defaultProductRows and function adjustProductRows. I need to also target .columns 4 within the same script, but I'm not sure how to go about adding it.
<script>
$(window).on('load resize', function (){
var windowWidth = $(window).width();
if(windowWidth < 753){ // this is my screen size break point for the rows
adjustProductRows(); // call the function to adjust add last and first classes
} else {
defaultProductRows(); // else if large screen size then get everything back to defalut
}
});
function defaultProductRows(){
var products = $('ul.products.columns-3 li.type-product');
products.each(function(idx, li) {
var product = $(li);
// remove all classes we added
$('ul.products li.adjusted-row.first').removeClass('adjusted-row first');
$('ul.products li.adjusted-row.last').removeClass('adjusted-row last');
if(idx == 0) { // make sure first li tag gets first class
product.addClass('first');
}
else if((idx+1) % 3 == 0) //this will make sure we have 3 rows by adding last classes after each 3 products
{
product.addClass('last');
}
else if(idx % 3 == 0)
{
product.addClass('first'); // make sure all products divided by 3 will have first class
}
else
{
console.log(idx); // just checking for the index
}
});
}
function adjustProductRows() {
var products = $('ul.products.columns-3 li.type-product');
products.each(function(idx, li) {
var product = $(li);
if(idx % 2 == 0) // we are even
{
product.addClass('adjusted-row first');
product.removeClass('last');
}
else // we are odd
{
product.addClass('adjusted-row last');
product.removeClass('first');
}
});
}</script>
Change your selector to include columns-4
From:
var products = $('ul.products.columns-3 li.type-product');
To:
var products = $('ul.products.columns-3 li.type-product, ul.products.columns-4 li.type-product');
This tells jQuery to select li.type-products that are part of either columns-3 or columns-4

JSON - If Statement Troubleshooting

What is causing the if statement (totalViews === 0) to not function properly?
The statement should be displaying a span tag with in the "div.viewed" class. The span's inner text should read "0 people have viewed your post" if the "totalViews" equals 0 (no span tags to begin with). However, a span tag is not being inputted at all in to the "div.viewed" class.
The remaining if statements seem to be functioning properly.
A sample of the current code:
function checkViewers() {
$('div.viewed').each(function() {
//Base Variables
var viewer = $('span.user', this);
var totalViews = viewer.length;
var shortenViews = viewer.length -1;
var viewerCount = $('span', this);
if (totalViews === 0) {
$('div.viewed', this).append('<span> 0 people have viewed your post.</span>');
}
if (totalViews == 1) {
$('<span> has viewed your post.</span>').insertAfter(viewer.last());
}
if (totalViews == 2) {
$('<span> and </span>').insertAfter(viewer.first());
$('<span> have viewed your post.</span>').insertAfter(viewer.last());
}
if (totalViews >= 3) {
$('<span> and </span>').insertAfter(viewer.first());
$('<span class="user count"></span>').insertAfter(viewerCount.eq(1));
$('.count', this).html(shortenViews + ' more people');
$('<span> have viewed your post.</span>').insertAfter(viewer.last());
viewer.slice(1).hide();
}
});
}
View the current and complete Plunker.
Your problem is in your traversal. $('div.viewed', this) doesn't exist.
Using the context argument of $(selector,context) is the same as writing:
$(this).find('div.viewed'); //look for descendant of "this"
Change:
$('div.viewed').each(function() {
/* "this" is an instance of div class= viewed*/
/* look for a div WITHIN "this" with class=viewed" --BUT no such descendant*/
$('div.viewed', this).append(..;
});
TO
$('div.viewed').each(function() {
/* I'm already here as "this" */
$(this).append(..;
});
DEMO
$('div.viewed', this).append('<span> 0 people have viewed your post.</span>');
here $('div.viewed', this) will return an empty array,
instead you might have to do it like
$('div.viewed').append('<span> 0 people have viewed your post.</span>');

javascript loop through li in div of paginate

Below is the code I'm using. The top part $('div.pagination... works fine, I can alert(length) and it gives me the correct value of pages in the pagination section. The bottom part appears not to work. This is for a scraper that will open each page on a forum. If I leave the loop out of it it successfully retreaves the url for page here. The length -=2 is to remove the next/previous li from the total count.
$('div.pagination').each(function() {
var length = $(this).find('li').length;
length -= 2;
});
for (var i = 0; var <= length; i++) {
var pageToOpen = 'http://someWebsite.com/index/page:' + i;
alert(pageToOpen);
page.open(pageToOpen, function (status) {
if (status == 'success') {
logAuctions();
}
}});
}
Define your var length outside (before) the.each()
Using .lentgh method you might miss the real page indexes. So I would suggest to grab the real anchor hrefs.
FIDDLE DEMO
var pages = [];
// skipping the "Next" and "Last" get all A ahchors
$('div.pagination li').slice(0,-2).find('a').each(function(){
pages.push( $(this).attr('href') );
});
$.each(pages, function(i, v){
$('<div>'+ ("http://someWebsite.com"+v) +'</div>').appendTo('#output');
});
/* WILL RESULT IN:
http://someWebsite.com/auctions/index/page:2
http://someWebsite.com/auctions/index/page:3
http://someWebsite.com/auctions/index/page:4
*/

How to count dynamically created divs

I'm trying to write a form builder where users can generate a signup form. I need to limit the amount of items that the user can create however they also need to delete the items.
Originally I had
var limit = 5;
var counter = 0;
if (counter == limit) {
However when the user deleted items the counter remained the same and so they couldnt replace the deleted form element with a new item. So what I want to do is count how many items are currently active. I tried to do this by giving each new element a class (.kid) and then counting the amount of divs with that class but it didnt work.
Could anyone point me in the right direction? This is what I have so far however it doesn't work.
var limit = 6;
var num = $('.kid').length;
function addAllInputs(divName, inputType){
if (num == limit) {
alert("You have all ready added 6 form items");
}
else {
var newdiv = document.createElement('div');
newdiv.setAttribute('id', 'child' + (counter + 1));
newdiv.setAttribute('class', 'kid' );
Cheers all!
You need to capture the current counter in a closure. Decrease the counter when the user deletes an item and increase it after an item is created. Your code sample doesn't reveal how you handle the deletion, but I'll try to illustrate what I mean with a small code snippet:
$(document).ready(function () {
var limit = 5;
var counter = $('.kid').length;
$('#triggers_delete').click(function () {
/* delete the item */
counter--;
});
$('#triggers_creation').click(function () {
if (counter == limit) {
alert('Limit reached');
return false;
}
/* somehow determine divName and inputType
and create the element */
addAllInputs(divName, inputType);
counter++;
});
});
function addAllInputs(divName, inputType) {
/* just create the item here */
}
Is there any reason an approach like this won't work? Every time you go to add a new DIV, the length of the current collection is examined to see if it meets or exceeds the limit. Of course, you may need to refine the scope of your selector if there could be other DIVs of the form with the same class ID.
var limit = 6;
function addAllInputs(divName, inputType){
if ( $('.kid').length >= limit ) {
alert("You have all ready added 6 form items");
}
else {
var newdiv = document.createElement('div');
newdiv.setAttribute('id', 'child' + (counter + 1));
newdiv.setAttribute('class', 'kid' );
}
Edit: Just a note, I am assuming that you are either removing the deleted items from the DOM or differentiating them from active items with a different class or attribute. Otherwise, the approach I suggested will return a count that includes the deleted items as well.
The only real issue is that your num variable is being defined outside of the function. It will get the number of .kid elements at the time the page loads and will not update. Simply move this line inside the function:
var limit = 6;
function addAllInputs(divName, inputType){
var num = $('.kid').length;
...
Try this
var limit = 6;
function addAllInputs(divName, inputType){
if ($('.kid').length == limit) {
alert("You have all ready added 6 form items");
}
else {
var newdiv = $('div', { 'id': 'child' + (counter + 1), 'class': 'kid' } );
$("inputContainerSelector").append(newdiv);
}

Filtering the list of friends extracted by Facebook graph api ( more of a JavaScript/Jquery question than Facebook API question)

Hello there JavaScript and Jquery gurus, I am getting and then displaying list of a facebook user's friend list by using the following code:
<script>
function getFriends(){
var theword = '/me/friends';
FB.api(theword, function(response) {
var divInfo = document.getElementById("divInfo");
var friends = response.data;
divInfo.innerHTML += '<h1 id="header">Friends/h1><ul id="list">';
for (var i = 0; i < friends.length; i++) {
divInfo.innerHTML += '<li>'+friends[i].name +'</li>';
}
divInfo.innerHTML += '</ul></div>';
});
}
</script>
graph friends
<div id = divInfo></div>
Now, in my Facebook integrated website, I would eventually like my users to choose their friends and send them gifts/facebook-punch them..or whatever. Therefore, I am trying to implement a simple Jquery filter using this piece of code that manipulates with the DOM
<script>
(function ($) {
// custom css expression for a case-insensitive contains()
jQuery.expr[':'].Contains = function(a,i,m){
return (a.textContent || a.innerText || "").toUpperCase().indexOf(m[3].toUpperCase())>=0;
};
function listFilter(header, list) { // header is any element, list is an unordered list
// create and add the filter form to the header
var form = $("<form>").attr({"class":"filterform","action":"#"}),
input = $("<input>").attr({"class":"filterinput","type":"text"});
$(form).append(input).appendTo(header);
$(input)
.change( function () {
var filter = $(this).val();
if(filter) {
// this finds all links in a list that contain the input,
// and hide the ones not containing the input while showing the ones that do
$(list).find("a:not(:Contains(" + filter + "))").parent().slideUp();
$(list).find("a:Contains(" + filter + ")").parent().slideDown();
} else {
$(list).find("li").slideDown();
}
return false;
})
.keyup( function () {
// fire the above change event after every letter
$(this).change();
});
}
//ondomready
$(function () {
listFilter($("#header"), $("#list"));
});
}(jQuery));
</script>
Now, This piece of code works on normal unordered list, but when the list is rendered by JavaScript, it does not. I have a hunch that it has to do something with the innerHTML method. Also, I have tried putting the JQuery filter code within and also right before tag. Neither seemed to work.
If anyone knows how to resolve this issue, please help me out. Also, is there a better way to display the friends list from which users can choose from?
The problem is here:
$(list).find("a:not(:Contains(" + filter + "))").parent().slideUp();
$(list).find("a:Contains(" + filter + ")").parent().slideDown();
Since you're rendering this:
divInfo.innerHTML += '<li>'+friends[i].name +'</li>';
There is no anchor wrapper, the text is directly in the <li> so change the first two lines to look in those elements accordingly, like this:
$(list).find("li:not(:Contains(" + filter + "))").slideUp();
$(list).find("li:Contains(" + filter + ")").slideDown();
You could also make that whole section a bit faster by running your Contains() code only once, making a big pact for long lists, like this:
$(input).bind("change keyup", function () {
var filter = $(this).val();
if(filter) {
var matches = $(list).find("li:Contains(" + filter + ")").slideDown();
$(list).find("li").not(matches).slideUp();
} else {
$(list).find("li").slideDown();
}
});
And to resolve those potential (likely really) innerHTML issues, build your structure by using the DOM, like this:
function getFriends(){
var theword = '/me/friends';
FB.api(theword, function(response) {
var divInfo = $("#divInfo"), friends = response.data;
divInfo.append('<h1 id="header">Friends/h1>');
var list = $('<ul id="list" />');
for (var i = 0; i < friends.length; i++) {
$('<li />', { text: friends[i].name }).appendTo(list);
}
divInfo.append(list);
});
}
By doing it this way you're building your content all at once, the <ul> being a document fragment, then one insertion....this is also better for performance for 2 reasons. 1) You're currently adding invalid HTML with the .innerHTML calls...you should never have an unclosed element at any point, and 2) you're doing 2 DOM manipulations (1 for the header, 1 for the list) after the much faster document fragment creation, not repeated .innerHTML changes.

Categories

Resources