I'm trying to work on having a epub / ebook show up in an html page and be able to store the user's progress on where they left off.
Currently, I can store the position where they were, when they use the left/right buttons to navigate. I can see that this shows up in localstorage.
However, it seems that whatever I try to set the book to display this position fails. I'm not skilled enough at javascript to understand where it's going wrong or why. I used chatgpt to get a lot of this built and have been triaging it from there as some of its info is out of date.
Currently, if i try to set the books position, on one epub file i have, it goes to the very first page. On another, it goes to what I believe is the very last page and then the navigation breaks; i'm not sure as i haven't really thumbed all the way through the other one.
Anyways, hopefully someone here knows an easy trick to this, sorry that i'm relatively uninformed on this library. I've tried looking at its documentation and frankly it's just too big with too many individual components for me to understand; i really have tried quite a few times and i feel that i'm stuck.
The part that i'm failing on is the function 'setBookToSavedState()'.
I've also tried setting the location, as in the documentation i can see that book.locations.currentLocation and rendition.currentLocation allows you to get or set it, but it seems like nothing happens when I do this.
I am going to try to just run some sort of loop where I call rendition.next until i get the start position to line up where I want, but it will be some really ignorant code and i'd like to get it done the right way here as i'm not sure if i'll be able to get that to work anyway.
Thanks in advance for any help you can offer!
<html>
<head>
<script src="https://futurepress.github.io/epubjs-reader/js/epub.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.5/jszip.min.js"></script>
<style>
#prev {
left: 0;
}
#next {
right: 0;
}
.arrow {
position: fixed;
top: 50%;
margin-top: -32px;
font-size: 64px;
color: #E2E2E2;
font-family: arial, sans-serif;
font-weight: bold;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
</style>
</head>
<body>
<a id="prev" href="#prev" class="arrow" style="visibility: visible;"><</a>
<a id="next" href="#next" class="arrow" style="visibility: visible;">></a>
<div id="area">
</div>
<script>
// Load the opf
var book = ePub("moby-dick.epub");
var rendition = book.renderTo("area", {
width: "100%",
height: 600,
spread: "always"
});
rendition.display();
book.ready.then(() => {
var next = document.getElementById("next");
next.addEventListener("click", function(e){
book.package.metadata.direction === "rtl" ? rendition.prev() : rendition.next();
e.preventDefault();
saveViewerState();
})
}, false);
var prev = document.getElementById("prev");
prev.addEventListener("click", function(e){
book.package.metadata.direction === "rtl" ? rendition.next() : rendition.prev();
e.preventDefault();
saveViewerState();
}, false);
var keyListener = function(e){
// Left Key
if ((e.keyCode || e.which) == 37) {
book.package.metadata.direction === "rtl" ? rendition.next() : rendition.prev();
}
// Right Key
if ((e.keyCode || e.which) == 39) {
book.package.metadata.direction === "rtl" ? rendition.prev() : rendition.next();
}
};
rendition.on("keyup", keyListener);
document.addEventListener("keyup", keyListener, false);
function saveViewerState() {
if (rendition && rendition.location && rendition.location.start) {
// get the current location
var currentLocation = rendition.currentLocation();
// save the current location in localStorage
localStorage.setItem("currentLocation", JSON.stringify(currentLocation));
}
}
// Retrieve the saved state from local storage and set the book to the saved page
function setBookToSavedState() {
// get the saved location from localStorage
var savedLocation = JSON.parse(localStorage.getItem("currentLocation"));
// display the saved location
rendition.display(savedLocation);
}
</script>
</body>
</html>
The documentation for Rendition.display states that it expects either a url or EpubCFI string. In your example, you would have to do the following:
// display the saved location
rendition.display(savedLocation.start.cfi);
Related
I am teaching myself JS. I am able to move a piece to a new location but for the 2nd piece the chess piece disappears. It seems that the addEventListener is going into a loop and I am not understanding why. Just need to understand what concept am I missing here:
My code below:
chess.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Chess board</title>
<style>
.cell {
height: 30px;
width: 30px;
border: 1.5px solid grey;
border-style: inset;
}
.greencell {
background-color: #AFE1AF;
}
.darkgreencell {
background-color: #097969;
}
.redcell {
background-color: red;
}
</style>
</head>
<body>
<script src="js/chess.js" defer></script>
</body>
</html>
js/chess.js
function movep(cbp,cbt,i,j) {
//Creating rules for pawn
console.log('P');
//console.log(cbp);
//refresh_colors(cbp,cbt);
if ((i>=0) & (i<=7)) {
if(cbp[i+1][j].length<2) {
//Based on player 1, if the location below the pawn is open then cbt masks that as 1
cbt[i+1][j]=1;
}
}
potential_moves(cbp,cbt,[i,j]);
update_board(cbp,cbt);
}
var possiblelocs=function(event,i,j,cbp,cbt) {
//based on string value of cbp (the chess piece of interest)
//I have to create rules for possible ways it can go
if (cbp[i][j].includes('P') ) {movep(cbp,cbt,i,j);}
//else if (cbp[i][j].includes('K')) {console.log('K');}
else if (cbp[i][j].includes('N')) {movep(cbp,cbt,i,j);}//using the function for pawn here for debugging purposes
//else if (cbp[i][j].includes('Q')) {console.log('Q');}
else if (cbp[i][j].includes('R')) {movep(cbp,cbt,i,j);}//using the function for pawn here for debugging purposes
//else if (cbp[i][j].includes('B')) {console.log('B');}
//console.log(cbp);
}
function update_board(cbp,cbt) {
//fills the board with all the proper pieces
var elem = document.getElementsByTagName('td');
//console.log(cbp);
for(var i=0;i<8;i++) {
for(var j=0;j<8;j++) {
elem[8*i+j].innerHTML=cbp[i][j];
if (elem[8*i+j].innerHTML.length > 1) {
//create a clickable EventListener if there is a string value >1 (i.e. not-empty)
elem[8*i+j].addEventListener( "click",possiblelocs.bind(event,'str',i,j,cbp,cbt) );
}
}
}
}
var movelocs=function(event,i,j,cbp,cbt,pc) {
//replace old location of string in cbp to the new one
cbp[i][j]=cbp[pc[0]][pc[1]];
cbp[pc[0]][pc[1]]='';
update_board(cbp,cbt);
}
function potential_moves(cbp,cbt,pc) {
//updates the board with possible location a specific piece can move (based on cbt)
//and makes the red cells clickable
var elem = document.getElementsByTagName('td');
for(var i=0;i<8;i++) {
for(var j=0;j<8;j++) {
if (cbt[i][j]==1) {
elem[8*i+j].setAttribute('class', 'cell redcell');
//once click move the board to the new location
elem[8*i+j].addEventListener( "click",movelocs.bind(event,'str',i,j,cbp,cbt,pc) );
}
}
}
}
I have tried to root cause it but I am unable to root cause even more.
Here is the behavior i see:
When the board starts up:
After I click 'R11' I see a red square show up down below to see where it can go as show below:
Once that is completed I see R11 pieces moves down 1 and I also click on N11 to see my available options (I know the rules are not right for those pieces). I then see the following image:
At this point I click on the red square below N11 and I see that N11 has completely gone.
My best understanding is that movelocs goes into a loop and deletes the piece I just moved. I am not sure why it does that.
If someone has any advice on how to debug this that would helpful as well.
I just went fast through your code and I see potential problem.
In update_board you add event listener to all fields. And in the event handler function possiblelocs you call movep in this function you call update_board again. This set another event listener.
So each time you click on field you basically double the number of listeners.
I have a set of buttons on my page, each of which calls a javascript function when clicked; when clicked, the active link color is lit, but when I click elsewhere on the page the active link color is cleared. I want it to stay lit unless I click on another button link.
Here is an example of how a link is constructed (there are 10 links):
<div class="C1"><br><button class="button_01" onclick="HideDropdown(); ShowPage(7);">FAQs</button></div>
Here's the css for the button and C1 classes:
.button_01 {
background-color: rgb(0,2,3);
border: none;
color: rgb(100,100,100);
font-family: camphorW01-Thin,calibri,arial;
text-align: left;
text-decoration: none;
font-size: 13pt;
cursor: pointer;
transition: all 150ms ease-in;
}
.button_01:hover { color: rgb(175,222,162); }
.button_01:active { color: rgb(175,222,162); }
.button_01:focus { color: rgb(175,222,162); }
.button_01:visited { color: rgb(175,222,162); }
.C1{
color:#DBDBDB;
font-family: sans-serif;
font-size: 14pt;
text-indent: 0px;
width: auto;
margin: auto;
}
I know the default behavior is for the active link color to clear when clicking elsewhere, but I should be able to use javascript or jquery to get the value of the active link and keep it the same color (unless I click on another link); I've found only two posts that come close but one is specific to list items (li), not a button class with an onclick handler (not an anchor tag) at How to get the ID of an active link. Another post at how to Keep the color of active link constant, until i press other link showed a jquery function specific to anchor tags; I modified it like this:
<script>
var items = $("button_01");
items.removeClass("active");
$(this).toggleClass("active");
});
<script>
That doesn't work and with that script in place the links do not work.
So my question is: how do I keep the active link color lit on a button that has an onclick handler to call javascript (versus a list item or an anchor tag)?
Thanks very much for any help on this.
EDIT: I solved this problem and posted the answer below.
assuming all you buttons have class="button_01"
$('.button_01').on('click', function(){
$('.button_01').removeClass('active');
$(this).addClass('active');
});
.active {
background:red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="button_01">Button 1</button>
<button class="button_01">Button 2</button>
<button class="button_01">Button 3</button>
you could use the .css() property within jquery if the active attribute is still clicking out on your site.
$('.button_class').on('click', function() {
$('.button_class').removeAttr('style');
$(this).css('backgroundColor', 'red');
});
I just made a quick fiddle with what I think is a possible solution to your problem. I've done in pureJs.
function ShowPage(e,page){
// do a function to reset colors to default
resetColors();
// call hide here, since you do it everytime you show a page
HideDropdown();
e.classList.add("active");
//do stuff here
}
function HideDropdown(){
// do stuff here
}
function resetColors(){
// do stuff here
}
.active{
color: red !important;
}
<div class="C1">
<button class="wathever" onclick="ShowPage(this,7);">A</button>
<button class="wathever" onclick="ShowPage(this,7);">B</button>
<button class="wathever" onclick="ShowPage(this,7);">C</button>
<button class="wathever" onclick="ShowPage(this,7);">D</button>
</div>
After much research and work, here's how I solved this problem.
Remember that I have 10 links, each with a unique ID number, so I loop through them 1-10 and create the ID name (e.g., btn04). In order to keep the current active link lit, I have to change the link color to the active link color when I click anywhere on the page except for another link of the same type (button_01 class). For that, I need to store the active element in a global var on each button click, so that on any subsequent click we know what the last active element was BUT the subsequent click will change the active element to the currently-clicked element. What to do? I set up another global var, LastActiveElement, which captures the most recently set active element. Now I know where the last click was -- if it was a hyperlink and the current click is not a hyperlink, I change the last clicked hyperlink color back to its active color, which has the effect of keeping it on the same color.
Add this to the body tag:
<body onload="ShowABC(1);" onclick="changeColor(event); getLastGAE(event); getFocusElement(event);">
<script>
function changeColor(event) {
for (i = 1; i < 11; i++) {
ID_Name = "btn0" + i.toString();
if (i >= 10){ID_Name = "btn" + i.toString();}
var elem = document.getElementById(ID_Name);
TargetClass = event.target.getAttribute('class');
TargetID = event.target.getAttribute('id');
var active = document.activeElement;
var equal = (LastActiveElement == ID_Name);
tfh = TargetID == "hamburger_container";
if ((equal == "true") && (TargetClass != "button_01") && (tfh == "false")){
var newColor = "rgb(175,222,162)";
elem.style.color = newColor; }
if (TargetClass == "button_01"){ elem.style.color = "rgb(100,100,100)"; }
if (TargetID == ID_Name){ elem.style.color = "rgb(175,222,162)"; }
}
}
</script>
<script>
var LastActiveElement;
function getLastGAE(event) {
LastActiveElement = GlobalActiveElement;
}
</script>
<script>
var GlobalActiveElement;
function getFocusElement(event) {
var active = document.activeElement;
TargetID = event.target.getAttribute('id');
GlobalActiveElement = TargetID;
}
</script>
With that, if I click anywhere on the page except another hyperlink of the same class, the active link color does not change.
Now I know some advise against global vars, but this is only two data elements added to the DOM so it takes up negligible space.
Of course, there may be other solutions but this is what I came up with.
Thanks to everyone who replied to this question.
I am trying to make webpage where there is a div in the center which is being changed, instead of going to different pages.
Ultimately, I would like to have the new div, when clicking on an arrow, to flow from right or left in to the center. But first I would like to make the divs appear and disappear when clicking on the arrows but unfortunately this doesn't work.
This is my javascript:
<script>
function changeToHome() {
document.getElementById("mainmain").style.display="block";
document.getElementById("mainmain2").style.display="none";
document.getElementById("mainmain3").style.display="none";
document.getElementById("mainmain4").style.display="none";
}
function changeToStudy() {
document.getElementById("mainmain").style.display="none";
document.getElementById("mainmain2").style.display="block";
document.getElementById("mainmain3").style.display="none";
document.getElementById("mainmain4").style.display="none";
}
function changeToJob() {
document.getElementById("mainmain").style.display="none";
document.getElementById("mainmain2").style.display="none";
document.getElementById("mainmain3").style.display="block";
document.getElementById("mainmain4").style.display="none";
}
function changeToContact() {
document.getElementById("mainmain").style.display="none";
document.getElementById("mainmain2").style.display="none";
document.getElementById("mainmain3").style.display="none";
document.getElementById("mainmain4").style.display="block";
}
function changePageRight() {
var displayValue5 = document.getElementById('mainmain').style.display;
var displayValue5 = document.getElementById('mainmain2').style.display;
var displayValue6 = document.getElementById('mainmain3').style.display;
var displayValue7 = document.getElementById('mainmain4').style.display;
if (document.getElementById('mainmain').style.display == "block") {
document.getElementById("mainmain").style.display="none";
document.getElementById("mainmain2").style.display="block";
}
else if (document.getElementById('mainmain2').style.display == "block") {
document.getElementById("mainmain2").style.display="none";
document.getElementById("mainmain3").style.display="block";
}
else if (document.getElementById('mainmain3').style.display == "block") {
document.getElementById("mainmain3").style.display="none";
document.getElementById("mainmain4").style.display="block";
}
else if (displayValue8 == block) {}
}
function changePageLeft() {
var displayValue = document.getElementById('mainmain').style.display;
var displayValue2 = document.getElementById('mainmain2').style.display;
var displayValue3 = document.getElementById('mainmain3').style.display;
var displayValue4 = document.getElementById('mainmain4').style.display;
if (displayValue == "block") { }
else if (displayValue2 == "block") {
document.getElementById("mainmain").style.display="block";
document.getElementById("mainmain2").style.display="none";
}
else if (displayValue3 == "block") {
document.getElementById("mainmain2").style.display="block";
document.getElementById("mainmain3").style.display="none";
}
else if (displayValue4 === "block") {
document.getElementById("mainmain3").style.display="block";
document.getElementById("mainmain4").style.display="none";
}
}
</script>
Now I have a few divs that look like this:
<div id="mainmain4">
<img style="width:400px;height:327px;margin-left:auto;margin-right:auto;display:block;" src="Untitled-22.png" />
<h2> My name </h2>
<p>Hi,</p>
<p>Some text</p>
</div>
With these css atributes:
#mainmain {
float: left;
width: 575px;
display: block;
position: relative;
}
And all other divs with display: none; so I can change this to block and the one that was block to none.
For some reason, after when I click on one button of the menu, which activates a changeToX() function, the arrows work great. But before that, when you first go to the website, it doesn't.
Can someone explain me what I do wrong?
You don't tell the browser which divs shall be displayed on load. You can use theonloadevent for this:
<body onload="changeToHome()">
One additional hint: you maybe don't want to use inline JavaScript and CSS.
jQuery is as this simple:
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
toggle!
<div id="mainmain">test text</div>
<script>
// you need this, only apply javascript when all html (dom) is loaded:
$(document).ready(function() {
$('.toggle-container').on('click', function(e) {
e.preventDefault(); // this prevents the real href to '#'
// .toggle() is like "on / off" switch for hiding and showing a container
$($(this).data('container')).toggle();
});
});
</script>
This function can be reused, because it is based on classes instead of id's.
Check this JSFiddle: http://jsfiddle.net/r8L6xg15/
Maybe this is of some use. I've tried to make a page control-like behaviour. You can select any container div and put elements in there that have the class 'page'. The JavaScript code will let you navigate those with buttons.
You can make it more fancy by adding the buttons through JavaScript. What you then have is basically a list of pages which are normally displayed as regular divs, but when the script kicks in, it changes them to a page control.
You can call this for any parent element, and in that sense it behaves a bit like a jQuery plugin. It is all native JavaScript, though. And not too much code, I hope. Like you said, I think it's good to learn JavaScript at first. It is very powerful by itself, and it's becoming increasingly powerful. jQuery adds a lot of convenience functions and provides fallbacks in case browser don't support certain features, or when implementations differ. But for many tasks, bare JavaScript will do just fine, and it certainly can't hurt to know your way around it.
Press the 'Run this snippet' button at the bottom to see it in action.
function Pages(element)
{
// Some initialization
var activePage;
// Find all pages within this element.
var pages = document.querySelectorAll('.page');
var maxPage = pages.length - 1;
// Function to toggle the active page.
var setPage = function(index)
{
activePage = index;
for (p = 0; p <= maxPage; p++)
{
if (p == activePage)
pages[p].className = 'page active';
else
pages[p].className = 'page inactive';
}
}
// Select the first page by default.
setPage(0);
// Handler for 'previous'
element.querySelector('.prev').onclick = function()
{
if (activePage == 0)
return;
setPage(activePage - 1);
}
// Handler for 'next'
element.querySelector('.next').onclick = function()
{
if (activePage == maxPage)
return;
setPage(activePage + 1);
}
// Add a class to the element itself. This way, you can already change CSS styling
// depending on whether this code is loaded or not. So in case of an error, the
// divs are just all show underneath each other, and the nav buttons are hidden.
element.className = element.className + ' js';
}
Pages(document.querySelector('.pages'));
.pages .page {
display: block;
padding: 40px;
border: 1px solid blue;
}
.pages .page.inactive {
display: none;
}
.pages .nav {
display: none;
}
.pages.js .nav {
display: inline-block;
}
<div class="pages">
<button class="nav prev">Last</button>
<button class="nav next">Next</button>
<div class="page">Page 1 - Introduction and other blah</div>
<div class="page">Page 2 - Who am I? Who are you? Who is Dr Who?</div>
<div class="page">Page 3 - Overview of our products
<ul><li>Foo</li><li>Bar</li><li>Bar Pro</li></ul>
</div>
<div class="page">Page 4 - FAQ</div>
<div class="page">Page 5 - Contact information</div>
</div>
To dos to make this a little more professional:
Add the navigation through JavaScript
Disable the buttons when first/last page has been reached
Support navigation by keys too (or even swipe!)
Some CSS transform (fade or moving) when toggling between pages
Smarter adding and removing of classes. Now I just set className, which sucks if someone would like to add classes themselves. jQuery has addClass and removeClass for this, which is helpful. there are also stand-alone libraries that help you with this.
Visible indication of pages, maybe with tabs at the top?
This is an odd request; However, what would be the best method to display the value of an HTML5 range-slider on the thumb of the slider?! the thumb will be animated onLoad so it must follow the thumb; moreover, this will be displayed for iPad
EXAMPLE:
<input class="range-consideration" type="range" name="points" min="1" max="10" ng-model="rangeConsiderations">
I too have had a lot of trouble figuring out a way to display the value of a range slider on its thumb. When working this out, I thought of three methods:
1. Combining multiple pseudo-elements
(Spoiler: Doesn't work - Read BoltClock's comment on the accepted answer of this thread)
CSS:
body {
--thumbNumber: "5"; // updates on slider input via JavaScript function
}
#slider {
-webkit-appearance: none;
outline: none;
width: 400px;
height: 3px;
background-color: #555;
}
#slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 30px;
height: 30px;
cursor: pointer;
background: #5a5;
}
#slider::-webkit-slider-thumb::before { // doesn't work (refer to above link)
content: var(--thumbNumber);
}
HTML:
<body>
<input type="range" id="slider" value="5" min="1" max="10" />
</body>
JavaScript:
window.onload = function () {
var slider = document.getElementById("slider");
slider.addEventListener("input", function () {
document.body.style.setProperty("--thumbNumber", "'" + this.value + "'");
});
// whenever this element receives input, change the value of --thumbNumber to this element's value
}
I thought I was pretty clever by using CSS variables to avoid the problem of not being able to directly change the properties of a pseudo-element in JavaScript. I learned, however, that this approach cannot work because you cannot use more than one pseudo-element selector in the same selection. (Refer to above link).
2. Dynamically changing the thumb's content property with JavaScript
This method is very similar to the previous one. The only difference is in the CSS. (Spoiler: This method also didn't work)
CSS:
#slider::-webkit-slider-thumb {
content: var(--thumbNumber);
}
The JavaScript is the exact same as before. The idea is that the event listener attached to the slider element listens for input events, and reacts by changing the value of the CSS variable --thumbNumber. This causes the content property of the thumb to update and display the change. This doesn't work, however, because CSS doesn't seem to let you change the content property of the thumb.
3. Using CSS Variables and... images...
This is basically as bad as it gets when it comes to dirty solutions. This is completely unscalable, but it's only a little bit godawful when working with less than 10 numbers on an input range.
For this example, I made 10 png files in photoshop in about 10 minutes. Each file is a 30px by 30px image of a number on a transparent background. Their filenames are 1.png, 2.png, 3.png, and so on until 10.png. Each file is stored in a folder called png. This folder is located in the same folder as the html document.
CSS:
body {
--sliderImage: url("png/5.png");
}
#slider::-webkit-slider-thumb {
background-image: var(--sliderImage);
}
JavaScript:
window.onload = function () {
var slider = document.getElementById("slider");
slider.addEventListener("input", function () {
document.body.style.setProperty("--sliderImage", "url('png/" + this.value + ".png')");
});
}
This example is obviously terrible in so many ways, but it's all I've been able to try that has worked.
Basically, this makes it so that when the slider element receives input, the value of the CSS variable --sliderImage is changed to, for example, url('png/6.png') when the slider thumb is dragged onto the value 6. This causes the background-image property of the thumb to update to a picture that correctly represents the value of the slider.
I am still searching for and trying to come up with an actual, reasonable answer to this problem. It puzzles me how such a seemingly simple task can, in reality, be very layered and difficult. I'll edit my answer as soon as I figure it out.
You cannot display value in the thumb but you can attach a bubble to the thumb which will contain and display the value as you slide. Refer to this detailed article Show slider value.
Here's a demo.
You create a div that will follow the handle.
Then, every time the handle is moved/changed (onslide/onchange), put the value of the handler to the div and also modify the current position of the div the same with the handle so that it will follow the handle.
or maybe, if possible, on onchange method of the slider get the id of the handle and just put
$('#handle').text($(this).val());
I'm just giving you ideas here. I'm sure there will be better answers.
Webkit allows you to target the thumb with css :
.range-consideration::-webkit-slider-thumb::before {
content: "test";
}
That will print test on the thumb. However, using attr(value) doesn't seem to work, so I don't know how to show the value. Maybe you can find a way by playing with css variables and changing it with javascript.
The equivalent for other browsers are ::-moz-range-thumb and ::-ms-thumb.
EDIT : This is really (really !) a dirty way, but it works and it's doable as long as you have a relatively limited range of values.
HTML :
<input class="range-consideration" type="range" min="1" max="10" value="3" onchange="this.setAttribute('value', this.value);" />
CSS :
.range-consideration[value="1"]::-webkit-slider-thumb::before { content: "1"; color: black; }
.range-consideration[value="2"]::-webkit-slider-thumb::before { content: "2"; color: black; }
.range-consideration[value="3"]::-webkit-slider-thumb::before { content: "3"; color: black; }
.range-consideration[value="4"]::-webkit-slider-thumb::before { content: "4"; color: black; }
.range-consideration[value="5"]::-webkit-slider-thumb::before { content: "5"; color: black; }
.range-consideration[value="6"]::-webkit-slider-thumb::before { content: "6"; color: black; }
.range-consideration[value="7"]::-webkit-slider-thumb::before { content: "7"; color: black; }
.range-consideration[value="8"]::-webkit-slider-thumb::before { content: "8"; color: black; }
.range-consideration[value="9"]::-webkit-slider-thumb::before { content: "9"; color: black; }
.range-consideration[value="10"]::-webkit-slider-thumb::before { content: "10"; color: black; }
This "workaround" has lots and lots of lipstick on it... but it is possible with a automated derivation of Sebastien C. answer http://codepen.io/anon/pen/xbQoLj
$(document).ready(function() {
var $daFoo = $('#foo');
var rule = "<style>";
// get mix & max of the slider
for(var i= parseInt($daFoo.attr("min")); i<= parseInt($daFoo.attr("max")); i++)
// for each value possible in this SINGLE!! slider - push a rule
rule += 'input[type=range][data-value="' + i + '"]::-webkit-slider-thumb::after { content: "' + i + '"; }';
$('head').append(rule + "</style>");
$daFoo.on('input', function() { $daFoo.attr('data-value', $daFoo.val()) });
});
Pleasantries
I've been playing around with this idea for a couple of days but can't seem to get a good grasp of it. I feel I'm almost there, but could use some help. I'm probably going to slap myself right in the head when I get an answer.
Actual Problem
I have a series of <articles> in my <section>, they are generated with php (and TWIG). The <article> tags have an image and a paragraph within them. On the page, only the image is visible. Once the user clicks on the image, the article expands horizontally and the paragraph is revealed. The article also animates left, thus taking up the entire width of the section and leaving all other articles hidden behind it.
I have accomplished this portion of the effect without problem. The real issue is getting the article back to where it originally was. Within the article is a "Close" <button>. Once the button is clicked, the effect needs to be reversed (ie. The article returns to original size, only showing the image, and returns to its original position.)
Current Theory
I think I need to retrieve the offset().left information from each article per section, and make sure it's associated with its respective article, so that the article knows where to go once the "Close" button is clicked. I'm of course open to different interpretations.
I've been trying to use the $.each, each(), $.map, map() and toArray() functions to know avail.
Actual Code
/*CSS*/
section > article.window {
width:170px;
height:200px;
padding:0;
margin:4px 0 0 4px;
position:relative;
float:left;
overflow:hidden;
}
section > article.window:nth-child(1) {margin-left:0;}
<!--HTML-->
<article class="window">
<img alt="Title-1" />
<p><!-- I'm a paragraph filled with text --></p>
<button class="sClose">Close</button>
</article>
<article class="window">
<!-- Ditto + 2 more -->
</article>
Failed Attempt Example
function winSlide() {
var aO = $(this).parent().offset()
var aOL = aO.left
var dO = $(this).offset()
var dOL = dO.left
var dOT = dO.top
var adTravel = dOL-aOL
$(this).addClass('windowOP');
$(this).children('div').animate({left:-(adTravel-3)+'px', width:'740px'},250)
$(this).children('div').append('<button class="sClose">Close</button>');
$(this).unbind('click', winSlide);
}
$('.window').on('click', winSlide)
$('.window').on('click', 'button.sClose', function() {
var wW = $(this).parents('.window').width()
var aO = $(this).parents('section').offset()
var aOL = aO.left
var pOL = $(this).parents('.window').offset().left
var apTravel = pOL - aOL
$(this).parent('div').animate({left:'+='+apTravel+'px'},250).delay(250, function() {$(this).animate({width:wW+'px'},250); $('.window').removeClass('windowOP');})
$('.window').bind('click', winSlide)
})
Before you go scratching your head, I have to make a note that this attempt involved an extra div within the article. The idea was to have the article's overflow set to visible (.addclass('windowOP')) with the div moving around freely. This method actually did work... almost. The animation would fail after it fired off a second time. Also for some reason when closing the first article, the left margin was property was ignored.
ie.
First time a window is clicked: Performs open animation flawlessly
First time window's close button is clicked: Performs close animation flawlessly, returns original position
Second time SAME window is clicked: Animation fails, but opens to correct size
Second time window's close button is clicked (if visible): Nothing happens
Thank you for your patience. If you need anymore information, just ask.
EDIT
Added a jsfiddle after tinkering with Flambino's code.
http://jsfiddle.net/6RV88/66/
The articles that are not clicked need to remain where they are. Having problems achieving that now.
If you want to go for storing the offsets, you can use jQuery's .data method to store data "on" the elements and retrieve it later:
// Store offset before any animations
// (using .each here, but it could also be done in a click handler,
// before starting the animation)
$(".window").each(function () {
$(this).data("closedOffset", $(this).position());
});
// Retrieve the offsets later
$('.window').on('click', 'button.sClose', function() {
var originalOffset = $(this).data("originalOffset");
// ...
});
Here's a (very) simple jsfiddle example
Update: And here's a more fleshed-out one
Big thanks to Flambino
I was able to create the effect desired. You can see it here: http://jsfiddle.net/gck2Y/ or you can look below to see the code and some explanations.
Rather than having each article's offset be remembered, I used margins on the clicked article's siblings. It's not exactly pretty, but it works exceptionally well.
<!-- HTML -->
<section>
<article>Click!</article>
<article>Me Too</article>
<article>Me Three</article>
<article>I Aswell</article>
</section>
/* CSS */
section {
position: relative;
width: 404px;
border: 1px solid #000;
height: 100px;
overflow:hidden
}
article {
height:100px;
width:100px;
position: relative;
float:left;
background: green;
border-right:1px solid orange;
}
.expanded {z-index:2;}
//Javascript
var element = $("article");
element.on("click", function () {
if( !$(this).hasClass("expanded") ) {
$(this).addClass("expanded");
$(this).data("originalOffset", $(this).offset().left);
element.data("originalSize", {
width: element.width(),
height: element.height()
});
var aOffset = $(this).data("originalOffset");
var aOuterWidth = $(this).outerWidth();
if(!$(this).is('article:first-child')){
$(this).prev().css('margin-right',aOuterWidth)
} else {
$(this).next().css('margin-left',aOuterWidth)
}
$(this).css({'position':'absolute','left':aOffset});
$(this).animate({
left: 0,
width: "100%"
}, 500);
} else {
var offset = $(this).data("originalOffset");
var size = $(this).data("originalSize");
$(this).animate({
left: offset + "px",
width: size.width + "px"
}, 500, function () {
$(this).removeClass("expanded");
$(this).prev().css('margin-right','0')
$(this).next().css('margin-left','0')
element.css({'position':'relative','left':0});
});
}
});