HTML javascript bookmark user viewing position - javascript

I have a html webpage. It contains a very long text passage.
User can use internet browser to read the passage. Since the passage is long, users need to scroll the page to read the whole passage.
I would like to add a floating box button for user to click. After clicking the button, I can capture the current viewing portion of the passage.
So that users can log in later and continue reading.
I think I need to add some javascript, but after hours of online searching, I failed to find relevant information.
Please kindly suggest the possible solution to do so?

Use a position:fixed box/button. Clicking it stores (or updates, if it already exists) a localStorage item or cookie with the current position. On page load, if that item exists, ask the user if they want to return to that spot.
There are two ways of doing this:
Save position as a pixel value. This works perfectly if the user's display will not change size (switching computers, switching monitors, changing screen resolution setting, etc.). However, if any of those changes do occur, the absolute pixel value of the saved position is not consistent with the position on the page.
Demo. To run: click "Save position", scroll anywhere, then reload the page.
Save position as a percentage of total height. This works exactly as solution (1), but handles all cases in which screen size changes as well. (Use this one.) The code provided below pertains to this solution.
Demo. To run: click "Save position"; change the width of the "Result" quadrant; without changing any code, click "Run" again.
The saved item is removed regardless of the user's choice (to return to the last spot or not), so that the prompt doesn't show up every time the page is reloaded (which can get annoying).
This is pure JS; it's very modular; it demonstrates simple usage of localStorage and falls back gracefully to cookies.
HTML
<div id="save">
<button id="saveButton">Save position</button>
<span id="saved">Saved!</span>
</div>
<div id="content">
<!-- Disgustingly long content -->
</div>
CSS
#save {
position:fixed;
top:30px;
left:10px;
width:20%;
}
#saved {
visibility:hidden;
color:green;
}
#content {
width:60%;
margin:auto;
}
JS
function checkStorageSupport() {
var test = "test";
try {
localStorage.setItem(test, test);
localStorage.removeItem(test);
return true;
} catch(e) {
return false;
}
}
function getTotalHeight() {
return document.body.clientHeight;
}
function getSavedPercent() {
var percent = storageSupported ? loadFromStorage() : loadFromCookie();
return (percent == null || percent == "") ? 0 : percent;
}
/******* Save *******/
function saveInStorage() {
localStorage.setItem("scrollPercent", (document.documentElement.scrollTop / getTotalHeight()));
}
function saveCookie() {
var expDate = new Date();
expDate.setDate(expDate.getDate() + 7); // start over if it's been more than ___ days
document.cookie = "scrollPercent=" + (document.documentElement.scrollTop / getTotalHeight())
+ "; " + expDate;
}
/******* Load *******/
function loadFromStorage() {
return localStorage.getItem("scrollPercent");
}
function loadFromCookie() {
return document.cookie.replace(/(?:(?:^|.*;\s*)scrollPercent\s*\=\s*([^;]*).*$)|^.*$/, "$1");
}
/******* Remove *******/
function removeFromStorage() {
localStorage.removeItem("scrollPercent");
}
function removeCookie() {
document.cookie = "scrollPercent=''";
}
/******* Handler *******/
var saveButton = document.getElementById("saveButton"),
saved = document.getElementById("saved");
saveButton.onclick = function() {
storageSupported ? saveInStorage() : saveCookie();
saved.style.visibility = "visible";
setTimeout(function() {
saved.style.visibility = "hidden";
}, 1500);
};
/******* Logic *******/
var storageSupported = checkStorageSupport(),
percent = getSavedPercent();
if (percent > 0) {
if (confirm("Would you like to continue reading where you left off?")) {
document.documentElement.scrollTop = percent * getTotalHeight();
}
storageSupported ? removeFromStorage() : removeCookie();
}
Note: to get back to the code that produces solution (1), copy the code from that demo. In the case JSFiddle goes down, here are manual instructions:
Remove every instance of / getTotalHeight() in a "Save" function
In the "Logic" section, replace position * getTotalHeight() with position
Remove getTotalHeight(), since it's not used
Replace instances of "percent" with "position" to be more semantically accurate

Basic idea is..
create a floating bookmark button on the page like:
#mybookmark{
position: fixed;
top:30px;
right:0;
}
Now in jquery.. retrieve the position of this floating bookmark when clicked on it..
$('#mybookmark').click(function(){
var bookmark_loc = $('#mybookmark').offset().top();
});
Store this bookmark_loc data in your preferred storage for the user.
then when they clicks on a button, you can scroll to the stored offset value in your storage.. in jquery
$('#scroll_to_bookmark').click(function{
var bookmark_loc = //Fetch bookmark
$('body').animate({ scrollTop: bookmark_loc; });
});

Related

How do I recalculate the section's height on scroll for a fixed navigation that changes class past certain anchors?

Context
I am working on a one-page website where the fixed navigation's class changes as it scrolls through the different sections in order to match the section's background color. To achieve this effect, I used and modified the 2nd solution listed here.
Issue
While it works great most of the time, the navigation code breaks when I resize the browser (or leave the page and click back). More specifically, the navigation's background color changes too early or too late and no longer matches the section's background.
I'm guessing that this happens because the section's height are calculated on page load. Ideally, they would be recalculated on every scroll - but I am a novice and that's just a guess. Any help to solve this issue would be appreciated.
JavaScript
FYI: there are four sections in the websites (Hero, Work, About, Contact). Navigation's bg color should be transparent in Hero, white in Work and Contact, and teal in About.
var afterhero = $('#hero-section').offset().top + $('#hero-section').height();
var afterwork = afterhero + $('#work-section').height();
var afterabout = afterwebsites + $('#about-section').height();
$(window).on('scroll', function() {
stop = Math.round($(window).scrollTop());
if (stop > afterabout) {
$('header').removeClass('teal');
$('header').addClass('white');
} else if (stop > afterwork) {
$('header').addClass('teal');
} else if (stop > afterhero) {
$('header').removeClass('teal');
$('header').addClass('white');
} else {
$('header').removeClass('teal');
$('header').removeClass('white');
}
});
Just try adding all your size variables into your scroll event handler:
$(window).on('scroll', function() {
var afterhero = $('#hero-section').offset().top + $('#hero-section').height();
var afterwork = afterhero + $('#work-section').height();
var afterabout = afterwebsites + $('#about-section').height();
stop = Math.round($(window).scrollTop());
if (stop > afterabout) {
$('header').removeClass('teal');
$('header').addClass('white');
} else if (stop > afterwork) {
$('header').addClass('teal');
} else if (stop > afterhero) {
$('header').removeClass('teal');
$('header').addClass('white');
} else {
$('header').removeClass('teal');
$('header').removeClass('white');
}
});
Now afterhero, afterwork and afterabout should all be recalculated on a page scroll.

Adding and removing classes at different heights on page using jQuery

I want to remove/add classes when the user is at different distances from the top by using jQuery.
I have successfully done it, and it works fine, but I think I'm doing it wrong, and I would like your help to optimize the code.
The html is simple, basically the sections(including the header), have 100% width. and different colors. I want to make the header change color when its over the first section(for aesthetical purposes).
And I also want it to have a shadow when the page has been scrolled more than 1 pixel.
I'm doing it by adding/removing classes.
When I use one big else if statement it doesn't work well because whenever any any condition is matched js stops checking for other matches, so it doesn't apply all the classes needed.
The next code works, however as I said, I think that it's not optimal/bad written.
Here is the HTML markup:
<header class="dark no-shadow">
Header
</header>
<section class="blue">
Please Scroll Down to see the header changes...
</section>
<section>
The header color Should change when you pass through me.
</section>
And here is the jQuery code:
var header = $('header'),
blueSection = $('section.blue'),
// Calculate when to change the color.
offset = blueSection.offset().top + blueSection.height() - header.height();
$(window).scroll(function(){
var scroll = $(window).scrollTop();
// Remove Class "dark" after scrolling over the dark section
if (scroll >= offset) {
header.removeClass('dark');
} else {
header.addClass('dark');
}
// Remove Class "no-shadows" whenever not on the top of the page.
if (scroll >= 1) {
header.removeClass('no-shadow');
} else {
header.addClass('no-shadow');
}
});
And for those of you who like to use jsfiddle(like me!):
https://jsfiddle.net/shock/wztdt077/6/
Thanks ahead guys!
Here is what I've come up with:
var header = $('header'),
blueSection = $('section.blue'),
// Calculate when to change the color.
offset = blueSection.offset().top + blueSection.height() - header.height();
var add = function(obj, cls) {obj.addClass(cls);}
var remove = function(obj, cls) {obj.removeClass(cls);}
var stylePoints = [offset, 1, 100, 200];
var styleTo = ['dark', 'no-shadow', 'blue', 'tall'];
var styleType = [add, add, remove, remove];
$(window).scroll(function() {
var scroll = $(window).scrollTop();
for (i = 0; i < stylePoints.length; i++) {
var func = styleType[i];
if (scroll >= stylePoints[i])
(styleType[i] == add) ? remove(header, styleTo[i]) : add(header, styleTo[i]);
else func(header, styleTo[i]);
}
});
It's not that much longer than your current jQuery, and allows for (theoretically) an infinite number of style changes without having to add a million long if/else statements. To add a new style change, you have to add a value to the end of each of the three arrays. stylePoints specifies the scrollTop() value at which a style should either be added or removed. styleTo specifies the class to be added or removed. styleType specifies whether this class should be added or removed when the user is scrolled above the corresponding stylePoints value. The opposite will occur when the user is scrolled below or at the corresponding stylePoints value. For instance, you can see from the code that the tall class will be removed from the header when the user is scrolled above 200, and added when the user is scrolled below or at 200.

Frame location change bug

I have successfully changed the location of frames on my page using commands like:
parent.bottomframe.location = 'interstitial.html';.
This one particular call, however, isn't working. I am calling a js function from the 'onclick' of a button in html. This function does most of its work successfully, and also successfully changes the text of my top frame using
parent.topframe.document.getElementById("tutorial").innerHTML = text;
But the bottom frame doesn't change. Could it be that I'm losing permission to change this property? If I put an alert() after the location-change line, I get back the old location, not the new one.
Any ideas?
(Here's the code for this function:)
function GetInterstitial(found_target)
{
parent.topframe.current_trial++;
if (found_target)
{
parent.topframe.correct++;
}
if (parent.topframe.current_trial==parent.topframe.total_trials)
{
UpdateData(parent.topframe.output);
setTimeout(function()
{SendAllData();}, 0
);
parent.topframe.document.getElementById("tutorial").innerHTML = thank_you_text[0]+parent.topframe.correct.toString()+'/'+parent.topframe.total_trials.toString()+thank_you_text[1];
parent.bottomframe.location.href = 'http://urlname..../js/thankyou.html?c='+parent.topframe.userID;
}
else
{
if (found_target)
text = "<p>Great job; you found the hidden number! Now click 'next' to play the next round.</p>";
else
text = "<p>Hmm... it seems you didn't find the hidden number. Click 'next' to play the next round.</p>";
}
setTimeout(function()
{
UpdateData(parent.topframe.output);
},600);
parent.topframe.document.getElementById("tutorial").innerHTML = text; //interstitial_text;
parent.bottomframe.location = 'interstitial.html';
parent.topframe.trial++;
parent.topframe.document.getElementById("nextbutton").style.display="block";
}

jQuery document ready buggyness with social sharing services

I have a strange issue that might have to do with jQuery document ready. Below is an html and script block that contains the usual social networking scripts. The Javascript block below displays the dd_outer div on the left edge of the body div, and when the browser window is shrunk, the div is faded out and the dd_footer div is faded in. The fadein and fadeout between the two divs works OK.
The problem is two fold: one issue is when the browser window is full width (1200px+), the Facebook script will not load and display consistently; it sometimes appears and sometimes doesn't, sometimes after a page reload and sometimes doesn't. (No browser or .htaccess caching is involved). Only the Facebook share fails to show consistently; all other services show OK.
The second problem that when the browser window is narrow - 650 px or so, when the dd_outer div is not displayed and the dd_footer div is - the footer div will not show on a page reload until the browser window is moved the smallest amount. Then the the div will display, Facebook share and all. For a mobile device, this is a problem because the browser window will be narrow to begin with and shouldn't need to be "nudged" to make the dd_footer div display.
This problem may have come into play because I have adapted this code from a WordPress plugin that used options to set the position of the dd_outer div and scroll height. That's the reason for the variables above the document ready call.
Is this the issue with what seems to be a document ready issue?
How can the variables be integrated into the script itself? It doesn't matter if they are hardcoded; I can change them when needed.
I'd throw this in a jsfiddle to demo but the divs won't realistically float with the window resizing.
I haven't included the CSS for clarity.
This is the html and social script block:
<div class='dd_outer'><div class='dd_inner'><div id='dd_ajax_float'>
<div class="sbutton"><script src="http://connect.facebook.net/en_US/all.js#xfbml=1"></script><fb:like layout="box_count" show_faces="false" font=""></fb:like></div>
<div class="sbutton">
Tweet<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script></div>
<div class="sbutton"><script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script><g:plusone size="tall"></g:plusone></div>
<div class="sbutton"><script src="http://platform.linkedin.com/in.js" type="text/javascript"></script>
<script type="IN/Share" data-counter="top"></script></div>
</div></div></div>
In the footer is <div id="dd_footer">that contains the same social scripts as above</div> and are faded in and out by the script below:
This is the jQuery that positions the dd_outer social services to the left and fades it out and fades in the dd_footer div.
<script type="text/javascript">
var dd_top = 0;
var dd_left = 0;
var dd_offset_from_content = 70; var dd_top_offset_from_content = 10;
jQuery(document).ready(function(){
var $floating_bar = jQuery('#dd_ajax_float');
var $dd_start = jQuery('#dd_start');
var $dd_end = jQuery('#dd_end');
var $dd_outer = jQuery('.dd_outer');
// first, move the floating bar out of the content to avoid position: relative issues
$dd_outer.appendTo('body');
dd_top = parseInt($dd_start.offset().top) + dd_top_offset_from_content;
if($dd_end.length){
dd_end = parseInt($dd_end.offset().top);
}
dd_left = -(dd_offset_from_content + 55);
dd_adjust_inner_width();
dd_position_floating_bar(dd_top, dd_left);
$floating_bar.fadeIn('slow');
if($floating_bar.length > 0){
var pullX = $floating_bar.css('margin-left');
jQuery(window).scroll(function () {
var scroll_from_top = jQuery(window).scrollTop() + 30;
var is_fixed = $dd_outer.css('position') == 'fixed';
if($dd_end.length){
var dd_ajax_float_bottom = dd_end - ($floating_bar.height() + 30);
}
if($floating_bar.length > 0)
{
if(scroll_from_top > dd_ajax_float_bottom && $dd_end.length){
dd_position_floating_bar(dd_ajax_float_bottom, dd_left);
$dd_outer.css('position', 'absolute');
}
else if ( scroll_from_top > dd_top && !is_fixed )
{
dd_position_floating_bar(30, dd_left);
$dd_outer.css('position', 'fixed');
}
else if ( scroll_from_top < dd_top && is_fixed )
{
dd_position_floating_bar(dd_top, dd_left);
$dd_outer.css('position', 'absolute');
}
}
});
}
});
jQuery(window).resize(function() {
dd_adjust_inner_width();
});
var dd_is_hidden = false;
var dd_resize_timer;
function dd_adjust_inner_width() {
var $dd_inner = jQuery('.dd_inner');
var $dd_floating_bar = jQuery('#dd_ajax_float')
var width = parseInt(jQuery(window).width() - (jQuery('#dd_start').offset().left * 2));
$dd_inner.width(width);
var dd_should_be_hidden = (((jQuery(window).width() - width)/2) < -dd_left);
var dd_is_hidden = $dd_floating_bar.is(':hidden');
if(dd_should_be_hidden && !dd_is_hidden)
{
clearTimeout(dd_resize_timer);
dd_resize_timer = setTimeout(function(){ jQuery('#dd_ajax_float').fadeOut(); }, -dd_left);
jQuery('#dd_footer').fadeIn();
}
else if(!dd_should_be_hidden && dd_is_hidden)
{
clearTimeout(dd_resize_timer);
dd_resize_timer = setTimeout(function(){ jQuery('#dd_ajax_float').fadeIn(); }, -dd_left);
jQuery('#dd_footer').fadeOut();
}
}
function dd_position_floating_bar(top, left, position) {
var $floating_bar = jQuery('#dd_ajax_float');
if(top == undefined) top = 0 + dd_top_offset_from_content;;
if(left == undefined) left = 0;
if(position == undefined) position = 'absolute';
$floating_bar.css({
position: position,
top: top + 'px',
left: left + 'px'
});
}
</script>
jQuery .ready() does not wait for iframes and other external media to load. These social buttons tend to work by inserting an iframe. The load event does wait for iframes etc, so you could try using that event instead, i.e.
jQuery(window).load(function () {
/* put the code you had inside .ready() here */
});
The problem comes with your idea: $(document).ready() fires when the DOM is ready, not when all scripts are ready!
an idea would be to search for trigger of that social-APIs you are using or just delay your calculations (e.g. via setTimeout).
Keep in mind that they are asyncron, even if you specify "async" on the script-tag to be false, you still dont know when they will activate or are finished.
I suggest to use the standard DOM event window.onload if you want to make sure that all the external assets, scripts, images, etc. are loaded first before you do something:
window.onload = function () {
// your script that needs to run after all the external assets are loaded
}
Reference: https://developer.mozilla.org/en-US/docs/DOM/window.onload
I just ran into similar problems with the facebook script... I just used the integration in the HEAD-section with javascript and added an "asynchronous"-attribute to the javascript-embedding script which then fires an asynchronous "heeey, facebook is ready now, too"-event to my jQuery-eventqueue...
I can't help you in detail, because I don't totally understand what you WANT to do and would reorganize the whole code A LOT... so - contact me private (email/skype) or try figuring out... I used that lines of code: pastie.org/private/9m4b9eet1dzzkl6duqpkrg

JavaScript progress bar

I want to have a progress bar which should show when I click on a button, e.g. "validate now". My requirement is to check 2000 URLs whether they are working or not. This was taking a lot of time while executing in program. So I need to show a progress bar to the user to know the status. How can I do this using JavaScript?
you could use the jQuery UI Progress bar simple, good looking and easy to implement, you just need to update the value every second or two.
$("#progressbar").progressbar({
value: 37
});
You would have to use Ajax and hit the server/ database every 2-3 second and fetch the status and display on web page. To display progress bar you can use table with different tds and set the background color of these td cells with the status result.
For progress bar create a table with 10 cells of equal width and say the status is 40% then you will set background of first 4 cells indicating 40%.
You could use ProgressBar.js. No dependencies, easy API and supports major browsers.
var line = new ProgressBar.Line('#container');
line.animate(1);
See more examples of usage in the demo page.
Pure JavaScript is not possible, you need to use Ajax to get the current status which requires Server-Side Scripting (I guess PHP in your case).
Store the total and completed URLs (or their counts) in the database or in a session and use get the percentage of completed URLs from there in PHP, called by a JavaScript Ajax request. Then give the percentage to the jQuery bar as Prutswonder suggested in another answer.
I suggest using JSON or simply Plaintext to receive the Data in JavaScript, XML would be unneccessary overhead (so it's actually AJAJ or AJAP, not Ajax).
I found a pop up Javascript bar. Might need some modifications to fit what you have in mind, but looks promising.
code is
<style>
<!--
.hide { position:absolute; visibility:hidden; }
.show { position:absolute; visibility:visible; }
-->
</style>
<SCRIPT LANGUAGE="JavaScript">
//Progress Bar script- by Todd King (tking#igpp.ucla.edu)
//Modified by JavaScript Kit for NS6, ability to specify duration
//Visit JavaScript Kit (http://javascriptkit.com) for script
var duration=3 // Specify duration of progress bar in seconds
var _progressWidth = 50; // Display width of progress bar.
var _progressBar = "|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||"
var _progressEnd = 5;
var _progressAt = 0;
// Create and display the progress dialog.
// end: The number of steps to completion
function ProgressCreate(end) {
// Initialize state variables
_progressEnd = end;
_progressAt = 0;
// Move layer to center of window to show
if (document.all) { // Internet Explorer
progress.className = 'show';
progress.style.left = (document.body.clientWidth/2) - (progress.offsetWidth/2);
progress.style.top = document.body.scrollTop+(document.body.clientHeight/2) - (progress.offsetHeight/2);
} else if (document.layers) { // Netscape
document.progress.visibility = true;
document.progress.left = (window.innerWidth/2) - 100+"px";
document.progress.top = pageYOffset+(window.innerHeight/2) - 40+"px";
} else if (document.getElementById) { // Netscape 6+
document.getElementById("progress").className = 'show';
document.getElementById("progress").style.left = (window.innerWidth/2)- 100+"px";
document.getElementById("progress").style.top = pageYOffset+(window.innerHeight/2) - 40+"px";
}
ProgressUpdate(); // Initialize bar
}
// Hide the progress layer
function ProgressDestroy() {
// Move off screen to hide
if (document.all) { // Internet Explorer
progress.className = 'hide';
} else if (document.layers) { // Netscape
document.progress.visibility = false;
} else if (document.getElementById) { // Netscape 6+
document.getElementById("progress").className = 'hide';
}
}
// Increment the progress dialog one step
function ProgressStepIt() {
_progressAt++;
if(_progressAt > _progressEnd) _progressAt = _progressAt % _progressEnd;
ProgressUpdate();
}
// Update the progress dialog with the current state
function ProgressUpdate() {
var n = (_progressWidth / _progressEnd) * _progressAt;
if (document.all) { // Internet Explorer
var bar = dialog.bar;
} else if (document.layers) { // Netscape
var bar = document.layers["progress"].document.forms["dialog"].bar;
n = n * 0.55; // characters are larger
} else if (document.getElementById){
var bar=document.getElementById("bar")
}
var temp = _progressBar.substring(0, n);
bar.value = temp;
}
// Demonstrate a use of the progress dialog.
function Demo() {
ProgressCreate(10);
window.setTimeout("Click()", 100);
}
function Click() {
if(_progressAt >= _progressEnd) {
ProgressDestroy();
return;
}
ProgressStepIt();
window.setTimeout("Click()", (duration-1)*1000/10);
}
function CallJS(jsStr) { //v2.0
return eval(jsStr)
}
</script>
<SCRIPT LANGUAGE="JavaScript">
// Create layer for progress dialog
document.write("<span id=\"progress\" class=\"hide\">");
document.write("<FORM name=dialog id=dialog>");
document.write("<TABLE border=2 bgcolor=\"#FFFFCC\">");
document.write("<TR><TD ALIGN=\"center\">");
document.write("Progress<BR>");
document.write("<input type=text name=\"bar\" id=\"bar\" size=\"" + _progressWidth/2 + "\"");
if(document.all||document.getElementById) // Microsoft, NS6
document.write(" bar.style=\"color:navy;\">");
else // Netscape
document.write(">");
document.write("</TD></TR>");
document.write("</TABLE>");
document.write("</FORM>");
document.write("</span>");
ProgressDestroy(); // Hides
</script>
<form name="form1" method="post">
<center>
<input type="button" name="Demo" value="Display progress" onClick="CallJS('Demo()')">
</center>
</form>
Text link example
<p align="center">This free script provided by<br />
<a href="http://www.javascriptkit.com">JavaScript
Kit</a></p>
found here code
You can make the progress bar by increasing the div width at some interval of time.
For example, you may increase the 1px width of div at each 50 milliseconds like,
var width = 1
function render (){
if(width <=100){
// apply width to div for progress bar
div.style.width = width + "px";
setTimeout(
function (){
render();
width++;
},50
);
}
}
render();

Categories