Bug with draggable/sortable options - javascript

Background: I am working with an e-learning platform that asks students a series of questions with 4 options to determine what career path would suit them best. We recently decided to move towards a configuration where, instead of putting them through the difficult decision of choosing just one #1 option, we would allow them to sort a drag-and-drop list to order the 4 options in their order of preference.
The catch is, the system is designed to still only care about whatever they placed first. Call it reverse psychology, if you will. Each option has a weight of 1-4 - and they don't always show up in that order - so you reorder them, it adds the weight to your running total score, and later on there's ranges of scores that determine where you fit.
The system works great, even if my methods of handling the data are unorthodox (keeping a running total and feeding it back into the URL to be carried along - I'm not worried about tampering because the accuracy of the numbers is for the student's benefit. If they want to tinker with it, they can blame themselves for false results :D)
Here's the bug: If the student does not touch the sorted list - i.e. decides that they like the order of the options as they loaded on the page - the line to update the running total score doesn't run. It only runs if they move something around. Even if the #1 option stays in place but other stuff is moved around, it still works fine - but if they just do not touch it at all, it keeps the value from the previous question, effectively adding "0" to their score and this does affect results.
Code after the jump, but first I figured I would cite specifically the line that updates the total:
.done(function(data) {
var runningTotalNew = parseInt(runningTotalCurrent) + parseInt(data);
document.getElementById("runningTotal").value = parseInt(runningTotalNew);
});
To be completely honest, I jerry-rigged it so bad that I don't even really fully understand how "data" knows what the first option is, but that's where the action happens and it resets a hidden input (that's later passed into the URL) with the new score. It's fairly near the top of the full code:
<script type="text/javascript">
$(document).ready(function(){
$(function() {
$("#qoptions ul").sortable({ opacity: 0.8, cursor: 'move', update: function() {
var order = $(this).sortable("serialize");
var runningTotalCurrent = "<?php echo $_GET['rt']; ?>";
$.get("/logic_getTopRankedItem.php", { rand: Math.random(), sortableData: encodeURIComponent(order) })
.done(function(data) {
var runningTotalNew = parseInt(runningTotalCurrent) + parseInt(data);
document.getElementById("runningTotal").value = parseInt(runningTotalNew);
});
}
});
});
});
</script>
<script type="text/javascript">
function nextQuestion() {
// Some info to figure this out
var totalQuestions = 15;
var currentQuestion = <?php echo $_GET['page']; ?>;
var thisPage = "/questionnaires/skills/";
var completePage = "/questionnaires/skills/complete/";
var userSalt = <?php echo $_GET['salt']; ?>;
// The logic
if(currentQuestion <= totalQuestions) {
var nextQuestion = currentQuestion + 1;
if(nextQuestion <= totalQuestions) {
window.location = thisPage + userSalt + "/" + nextQuestion + "/" + parseInt(document.getElementById("runningTotal").value);
}
if(nextQuestion > totalQuestions) {
window.location = completePage + parseInt(document.getElementById("runningTotal").value);
}
}
if(currentQuestion > totalQuestions) {
// Not possible, 404 it out
window.location = "/404";
}
}
</script>
<?php
// Only show "let's get started" if they're on the first question.
if($_GET['page'] == "1")
{
echo "<div align=\"center\">\r\n";
echo "<h1>let's get started.</h1>\r\n";
echo "</div>\r\n";
}
// Load question/weight data from data file
include($_SERVER['DOCUMENT_ROOT']."/data/questionnaire_skills.php");
// Determine current question from page number
$currentQ = $_GET['page'];
// Determine current running total, which the website sets to 0 at survey start
$runningTotal = $_GET['rt'];
?>
<div style="padding-top: 15px;"></div>
<div style="width: 610px; margin: auto;">
<p style="font-size: 16px;" align="center">
For each option set, <strong>rank the options from <i>most</i> preferred to <i>least</i> preferred</strong> to indicate the skill you'd like to use most in your next career move.
</p>
</div>
<table border="0" align="center" width="625" height="175">
<tr>
<td style="width: 75px; background: url('/images/pref_spec.png') no-repeat;" valign="top"><!-- Preference Spectrum --></td>
<td style="width: 550px;">
<!-- Begin options drag n drop -->
<div id="qoptions">
<ul>
<li id="option_<?php echo $weight[$currentQ][1]; ?>"><?php echo $option[$currentQ][1]; ?>
<div class="clear"></div>
</li>
<li id="option_<?php echo $weight[$currentQ][2]; ?>"><?php echo $option[$currentQ][2]; ?>
<div class="clear"></div>
</li>
<li id="option_<?php echo $weight[$currentQ][3]; ?>"><?php echo $option[$currentQ][3]; ?>
<div class="clear"></div>
</li>
<li id="option_<?php echo $weight[$currentQ][4]; ?>"><?php echo $option[$currentQ][4]; ?>
<div class="clear"></div>
</li>
</ul>
</div>
<!-- End options drag n drop -->
<!-- Start options form handler -->
<form name="optionsHandler">
<input type="hidden" name="runningTotal" id="runningTotal" value="<?php echo $runningTotal; ?>" />
</form>
<!-- End options form handler -->
<!-- Button to push all this data into the next question in an informal cookie -->
<p align="center">
continue ยป
</p>
</td>
</tr>
</table>
Don't worry about the PHP file that it references in /data/ - that's just containing the weights and options and it's just a bunch of arrays. I think this is an issue with the jQuery handling of the sortable data. FWIW, this uses Bootstrap and it's my first foray into both Bootstrap and an application so dense into data processing.

Related

Auto scroll a webpage with rss-feed, item by item

I'm making a wallboard (monitor with a newsfeed on it). I'm new to javascript but managed to find a few sources that helped me to put an RSS-feed on a PHP-page and format it with CSS.
So far so good!
To turn this page into a wallboard, I'd like to show one feed-item for a few seconds and then have to page automatically scroll to the next item. Each feed-item is in a <div class="post">.
Could someone please help me with some sample autoscroll code?
I've tried to scroll continuously through the page, but that is not the effect that I'm looking for.
This is the code of a <div> that shows a post:
<div class="post">
<div class="post-head">
<h2><a class="feed_title" href="<?php echo $link; ?>"><?php echo $title; ?></a></h2>
<span><?php echo $pubDate; ?></span>
</div>
<div class="post-content">
<?php echo implode(' ', array_slice(explode(' ', $description), 0, 20)) . "..."; ?> Read more
</div>
</div>
To scroll, I currently use a script from javascriptkit.com:
<script language="JavaScript1.2">
var currentpos=0,alt=1,curpos1=0,curpos2=-1
function initialize(){
startit()
}
function scrollwindow(){
if (document.all)
temp=document.body.scrollTop
else
temp=window.pageYOffset
if (alt==0)
alt=1
else
alt=0
if (alt==0)
curpos1=temp
else
curpos2=temp
if (curpos1!=curpos2){
if (document.all)
currentpos=document.body.scrollTop+1
else
currentpos=window.pageYOffset+1
window.scroll(0,currentpos)
}
else{
currentpos=0
window.scroll(0,currentpos)
}
}
function startit(){
setInterval("scrollwindow()",100)
}
window.onload=initialize
</script>
The page that I have now is available here:
https://app-storage.org/mrm/rsstest.php
The effect I'm looking for is:
1. Show the first news item
2. wait a few seconds
3. smoothly scroll to the second news item
4. at the end of the page, return to the first item
Any help is greatly appreciated!!!

Links not working in a Repeat Region in JQuery

I recently download this Slideshow with jmpress.js for a website. I'm using PHP and a database in MYSQL. I used a recordset in Dreamweaver to get the data from database to the site. Then I tried to create a Repeat Region to show the latests posts in the slider.
Headings, images and text are working fine. The problem is on the href (links), which are not changed and remain the same (the ID of the latest post) for all records.
In the head I have these files:
SlideshowJmpress/css/style.css
http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js
SlideshowJmpress/js/jmpress.min.js
SlideshowJmpress/js/jquery.jmslideshow.js
SlideshowJmpress/js/modernizr.custom.48780.js
Here is the HTML Code:
<section id="jms-slideshow" class="jms-slideshow">
<?php do { ?>
<div class="step" data-color="color-1">
<div class="jms-content">
<h3><?php echo $row_slider['packageTitle']; ?></h3>
<p><?php echo $row_slider['packageDescription']; ?></p>
<a class="jms-link" href="article.php?ID=<?php echo $row_slider['ID']; ?>">Read more</a>
</div>
<img src="<?php echo $row_slider['packageGraphic']; ?>" width="300px;" height="300px;" />
</div>
<?php } while ($row_slider = mysql_fetch_assoc($slider)); ?>
</section>
<script type="text/javascript">
$(function() {
$( '#jms-slideshow' ).jmslideshow({
});
});
</script>
Here is the php code for the recordset:
$maxRows_slider = 4;
$pageNum_slider = 0;
if (isset($_GET['pageNum_slider'])) {
$pageNum_slider = $_GET['pageNum_slider'];
}
$startRow_slider = $pageNum_slider * $maxRows_slider;
mysql_select_db($database_nipvlach, $nipvlach);
$query_slider = "SELECT * FROM pages ORDER BY `date` DESC";
$query_limit_slider = sprintf("%s LIMIT %d, %d", $query_slider, $startRow_slider, $maxRows_slider);
$slider = mysql_query($query_limit_slider, $nipvlach) or die(mysql_error());
$row_slider = mysql_fetch_assoc($slider);
if (isset($_GET['totalRows_slider'])) {
$totalRows_slider = $_GET['totalRows_slider'];
} else {
$all_slider = mysql_query($query_slider);
$totalRows_slider = mysql_num_rows($all_slider);
}
$totalPages_slider = ceil($totalRows_slider/$maxRows_slider)-0;
Error is either in IDs in your DB or in the slideshow script i think.
Look at source of your page. Are the links right ?
I think that the problem is in the script or in the jmpress.min.js
When I changed slider, everything worked fine!

Sorting HTML divs

I have a database table with a list of images. Each image also has data attached, like a name and value. Using this table, I create HTML code with PHP to make a grid on the screen, each with an image/name/value from the table.
This is my PHP that generates the HTML:
//Makes a div for each item in table
Echo "<li id=div" . $i . ">";
//Content for single grid block
Echo '<center><h3 id="credits' . $i . '">' . $credit_value . " credits" . '</h3></center>';
Echo "<img id='item" . $i ."' src= '$new_link' title='$row[Item_Name]' class='clickableImage' alt='$just_name' data-creditvalue='" . $credit_value . "' data-imagenumber='" . $i . "'border=0 style='position: center; top: 0; left: 0;'/>";
Echo '<center><h3 id="quality">' . $quality . '</h3></center>';
Echo '</li>';
This makes each div named "div1", "div2" etc. In the //content section, printed with the image is data-imageName=$imageName and data-imageValue=$value, though I should be able to attach those to the divs holding the content as well.
What I want to do is add buttons at the top of my page which will sort the image grid by categories. It is currently loaded in order of the items in the database table, but for example, I would have a button that could be clicked after the grid is loaded, which changes the orders of all the divs, so they are in alphabetical order, or lowest->highest valule.
How can I do this?
EDIT: Here is an example of the html generated by the above code.
<li id="div2">
<center>
<h3 id="credits2">108 credits</h3>
</center>
<div style="position: relative; left: 0; top: 0;">
<img id="item2" src="http://steamcommunity-a.akamaihd.net/economy/image/fWFc82js0fmoRAP-qOIPu5THSWqfSmTELLqcUywGkijVjZYMUrsm1j-9xgEObwgfEh_nvjlWhNzZCveCDfIBj98xqodQ2CZknz56P7fiDz9-TQXJVfdSXfgF9gT5DBg-4cBrQJnv8eMDKgnutIGTZeEpYt8dH5LTU_ePNwj-uE9s1aZVepTb9Czu33zpJC5UDL2Z8FjG/155fx118f" title="AK-47 | Blue Laminate (Minimal Wear)" class="clickableImage" alt="AK-47 | Blue Laminate " data-creditvalue="108" data-imagenumber="2" border="0" style="position: center; top: 0; left: 0;">
<img src="images/tick.png" id="tick2" class="hidden" style="position: absolute; top: 0px; left: 70%;">
</div>
<center>
<h3 id="quality">Minimal Wear</h3>
</center>
</li>
It's pretty easy to sort nodes with jQuery (and in pure JS actually too). You need to use sort methods which delegates to Array.prototype.sort, just provide custom comparator functions.
In your case you want to be able to sort by string title as well as by number, so I would create two separate functions and use them depending on what button was clicked:
<button onclick="sort('title', 'string')">Sort by name</button>
<button onclick="sort('data-creditvalue', 'number')">Sort by value</button>
and sort function will be
var comparators = {
string: function(a, b) {
return a.localeCompare(b);
},
number: function(a, b) {
return Number(a) - Number(b);
}
};
function sort(attr, type) {
var $container = $('ul'),
$li = $container.find('li');
$li.sort(function(a, b) {
var aVal = $(a).find('img').attr(attr),
bVal = $(b).find('img').attr(attr);
console.log(aVal)
return comparators[type](aVal, bVal);
}).appendTo($container);
}
Demo: http://plnkr.co/edit/lLJ0AWlLvwDeInBEIYCb?p=info
Depends where you want to have the sorting done.
Do you want the sorting done on the server or on the client?
SERVER SIDE: Your column headers would need to link back to the server and pass a SQL parameter to update your SQL query. This wouldn't be the prefered way. It would refresh the page every time you sorted.
CLIENT SIDE: You could use a jQuery plugin, like DataTable, to do the sorting.
CLIENT SIDE (Ajax): Here is some good detail already published

How do I move a php call to another div and have it still work

I've looked around a bit, and found issues similar to mine, but not quite the same. I have a page template that is laid out in 4 columns in HTML. Inside column 4, I have a php function echoing a gallery of images.
<div id="homepage-layout">
<div id="homepage-column-1">
<div class="homepage-small"><?php echo get_homepage_img_small_1(); ?></div>
<div class="homepage-small"><?php echo get_homepage_img_small_2(); ?></div>
<div class="homepage-small"><?php echo get_homepage_img_small_3(); ?></div>
</div><!-- End Column 1 -->
<div id="homepage-column-2">
<div class="homepage-large-left"><?php putRevSlider("homepage"); ?></div>
</div><!-- End Column 2 -->
<div id="homepage-column-3">
<div class="homepage-med"><?php echo get_homepage_img_med_1(); ?></div>
<div class="homepage-med"><?php echo get_homepage_img_med_2(); ?></div>
</div>
<div id="homepage-column-4">
<div class="homepage-large-right"><?php putRevSlider("home_small"); ?></div>
</div><!-- End Column 4 -->
</div>
I'm using jQuery to change the layout of the site based on the width of the window.
var column3 = $("#homepage-column-3").contents();
var column4 = $("#homepage-column-4").contents();
var swapped = false;
$(window).on('resize', function() {
if( $(window).width() <= 700 && !swapped){
$("#homepage-column-3").html(column4);
$("#homepage-column-4").html(column3);
swapped = true;
} else if( $(window).width() > 700) {
$("#homepage-column-3").html(column3);
$("#homepage-column-4").html(column4);
swapped = false;
}
});
However, when the window size triggers the jQuery, the RevSlider from Column 4 freezes on the image it was displaying at the time, and the only way to get it running again is to refresh the page.
Any thoughts? Or, if this question has been answered already, please point me in the right direction.
Thanks!
jeroen had the right idea. I was able to take the 4 columns, and split them into 2 chunks. I was then able to make them both flex containers, and with a of media query, was able to make the columns swap places while keeping the RevSlider initialized.
Thanks to all for your help!

JQuery click slidetoggle only working for the first DIV of PHP repeated DIVS

I have DIVs which are repeated for records in a database via PHP, and I have jquery script which on click should expand a div bellow out.
This is the simple code for the slide, generic stuff:
<script src =
"http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js">
</script>
<script>
$(document).ready(function(){ // Line A
$("#flip").click(function(){ // Line B
$("#panel").slideToggle("slow"); // Line C
});
});
</script>
<body>
<div style="background:red" id="flip">Click to slide the panel down or up</div>
<div style="background:green" id="panel">Oh Hello There</div>
#panel {
display:none;
}
Fiddle: http://jsfiddle.net/hpvgp/3/
However when I try and apply this to my code where I have the divs looped out, it only works for the first div.
Current code:
<?php
$webserver = 'localhost';
$administrator = 'root';
$password = '';
$db_name = 'cdb';
$db = mysqli_connect($webserver, $administrator, $password, $db_name)
or die('Error connecting');
if( isset($_REQUEST['page'])) {
$_SESSION['page'] = $_REQUEST['page'];
}else{
$_SESSION['page'] = 1;
}
$records_per_page = 8;
$query = "SELECT * FROM cars";
$result = mysqli_query($db, $query)
or die("Error in query: '$query'");
$row = mysqli_fetch_assoc($result);
$i = 0;
$start = ($_SESSION['page'] - 1) * $records_per_page;
$end = ($_SESSION['page']) * $records_per_page;
while($row = mysqli_fetch_assoc($result) and $i < $end) {
$i++;
if( $i > $start )
{;
echo'
<div><br><br>
<div id="flip" class="navbar navbar-inverse" style ="margin-top:-40px; height:128px">
<div style="float:left; height:100px; width:200px">
<img src="'.$row['logo'].'" style="margin:10px; float:left" height="100"/></div>
<div style="float:left"><h2>'.$row['make'].' '.$row['model'].'</h2></div>
</div>
</div>
<div><img id="panel" style="padding-bottom: 100px" src="images/cars/'.$row['carIndex'].'.jpg" height="300"/><div> ';
}
}
I'm currently using Bootstrap, not sure if that would matter.
I find it really strange that this would work for the first DIV but not any of the following ones. Do I perhaps need to place the script somewhere else? I'm really lost on this.
Any help appreciated -Tom
IDs are expected to be unique. Never use the same id more than once in a document. Change your code to:
while($row = mysqli_fetch_assoc($result) and $i < $end) {
$i++;
if( $i > $start )
{
echo'
<div><br><br>
<div class="flip navbar navbar-inverse" data-panel="panel_' + $i . '" style ="margin-top:-40px; height:128px">
<div style="float:left; height:100px; width:200px">
<img src="'.$row['logo'].'" style="margin:10px; float:left" height="100"/></div>
<div style="float:left"><h2>'.$row['make'].' '.$row['model'].'</h2></div>
</div>
</div>
<div><img id="panel_' . $i . '" style="padding-bottom: 100px" src="images/cars/'.$row['carIndex'].'.jpg" height="300"/><div> ';
}
}
And now chang your javascript to:
<script>
$(document).ready(function(){
$(".flip").click(function(){
var panelId = $(this).attr('data-panel');
$('#' + panelId).slideToggle("slow")
});
});
</script>
This way your IDs are unique, and each panel is related to its 'flip' div via a custom attribute (data-panel) that contains the panel's ID.
You're assigning the same ID to multiple divs. An ID is, by definition, unique. If you set the same ID on more than one element then your HTML will be invalid and all bets are off. jQuery will use GetElementByID () to select which element to grab when you pass it an ID, and the behaviour of GetElementByID when there's more than one element with the same ID is undefined. It could in theory do anything (throw an exception, return the first matching element, select the last matching element, select a single matching element at random, make demons fly out of your nose, etc).
As you want your code to affect more than one element, then all those elements have something in common. Therefore they all belong to the same class of elements, and you should use class="someclass" to indicate which elements belong to that class. HTML like:
<div class="flip navbar navbar-inverse" style ="margin-top:-40px; height:128px">
JS like:
$(".flip").click(function(){ // Line B
As an advice that works at least for me, when I'm using classes for bind functions, I like to difference that class to the others, so I add the prefix "js-" to the class. That way I'll know that it's not a design class, that an event is bind to that class and that class should not be removed. So I'll use "js-flip" instead "flip". But it is just and advice as good practises and some people cannot be agree with me, but it has been quite useful in projects where more than one person were touching the same code. Also I will not add css styles to that class because the porpoise of it is only programmatic, so it will be transparent for the designers.

Categories

Resources