Interaction Level in EPUBs - epub

Is it possible to add some questions after each chapter in a epub and somehow restrict user not to move forward to the next chapter in the book until he has answered the end chapter questions.
I have seen a implementation and was wondering how did they achieved it.

I assume you have access to Javascript to handle the end-of-chapter tests. Store the results in local storage. Then, when you're loading a page to which access might be restricted, consult local storage to see if the user is allowed to see it, and based on the result do something like adding a class to the body tag to display a non-accessible message:
<script>
window.onLoad(function(){
var can_access=get_accessibility(location.href);
if (!can_access) { document.body.className="no-access";}
};
</script>
#no-access-msg { display: none; }
body.noaccess #no-access-msg { display: block; }
<body>
<div id="no-access-msg">You can't access this page yet, take the test first.</div>
You get the idea.

Related

How to query for an element inside a dom-repeat

I have been scouring the web for a clear answer on how to query for an element generated by a dom-repeat element from Dart code.
sample.html
<dom-module id="so-sample>
<style>...</style>
<template>
<template is="dom-repeat" items="[[cars]] as="car>
...
<paper-button on-click="buttonClicked">Button</paper-button>
<paper-dialog id="dialog">
<h2>Title</h2>
</paper-dialog>
</template>
</template>
sample.dart
I'll omit the boilerplate code here, such as imports or the query to my database to fill the cars property ; everything works fine.
...
#reflectable
void buttonClicked(e, [_])
{
PaperDialog infos = this.shadowRoot.querySelector("#dialog");
infos.open();
}
This generates the following error :
Uncaught TypeError: Cannot read property 'querySelector' of undefined
I have tried several 'solutions', which are not, since nothing works.
The only thing I saw on quite a lot of threads is to use Timer.run() and write my code in the callback, but that seems like a hack. Why would I need a timer ?
I understand my problem may be that the content of the dom-repeat is generated lazily, and I query the items 'before' they are added to the local DOM.
Another advice I didn't follow is to use Mutation Observers. I read in the polymer API documentation that the observeNodes method should be used instead, as it internally uses MO to handle indexing the elements, but it again seems a bit complicated just to open a dialog.
My final objective is to bind the button of each generated model to a dedicated paper-dialog to display additional information on the item.
Has anyone ever done that ? (I should hope so :p)
Thanks for your time !
Update 1:
After reading Gunter's advices, although none of them actually worked by themselves, the fact that the IDs aren't mangled inside a dom-repeat made me think and query paper-dialog instead of the id itself, and now my dialog pops up !
sample.dart:
PaperDialog infos = Polymer.dom(root).querySelector("paper-dialog");
infos.open();
I now hope that each button will call the associated dialog, since I'll bind data inside the dialog relative to the item I clicked ~
Update 2:
So, nope, the data binding didn't work as expected: All buttons were bound to the item at index 0, just as I feared. I tried several ways to query the correct paper-dialog but nothing worked. The only 'workaround' I found is to query all the paper-dialog into a list and then get the 'index-th' element from that list.
#reflectable
void buttonClicked(e, [_])
{
var model = new DomRepeatModel.fromEvent(e);
List<PaperDialog> dialogs = Polymer.dom(this.root).querySelectorAll("paper-dialog");
dialogs[model.index].open();
}
This code definitely works, but it feels kind of a waste of resources to get all the elements when you really only need one and you already know which one.
So yeah, my initial problem is solved, but I still wonder why I couldn't query the dialogs from their id:
...
<paper-dialog id="dialog-[[index]]">
...
</paper-dialog>
#reflectable
void buttonClicked(e, [_])
{
var model = new DomRepeatModel.fromEvent(e);
PaperDialog dialog = Polymer.dom(this.root).querySelector("dialog-${model.index}");
dialog.open();
}
With this code, dialog is always null, although I can find those dialogs, correctly id-ied, in the DOM tree.
You need to use Polymers DOM API with shady DOM (default). If you enable shadow DOM your code would probably work as well.
PaperDialog infos = new Polymer.dom(this).querySelector("#dialog")

How to highlight/indicate a link without mouseover?

I'm almost done with my new website and I figured I could do some improvements here and there before launching. One thing that bothers me is the fact that links are not underlined or highlighted in any way. It's okay, I want it that way. But I'm afraid certain links won't be noticed or might disappear completely next to more prominent features.
So I was wondering if there is a way to make the user notice the link without underlining/coloring/alternating it permanently. I was thinking of e.g. flashing an underscore/underline every 10 seconds (or randomly) or changing color every few seconds for a tiny bit. All without the need of the user's engagement (hovering over it). When you look at the page there should be some movement/change to help the user notice the link.
I hope you guys get what I mean. I am no expert by any means. If anyone could point me in the direction of any scripts/tutorials or whatever I'd be grateful. I tried googling around but couldn't come up with anything useful.
Thanks!
I think this is bad, you should use hover for a better user experience, or add an underline by default to make the link more recognizable because most users will know this is a link.
Anyways if you want to do this, you will need javascript in your website.
You can try the following.
var links = document.querySelectorAll("a")
setInterval(function() {
for (var i = 0; i < links.length; i++) {
var link = links[i]
link.classList.toggle("toggled")
}
}, 1000)
a {
text-decoration: none;
color: blue;
}
a.toggled {
color: green;
}
<h1>Hello</h1>
This is a link!
<p>Some text</p>
This is another link!

Rendering dynamic scss-files with ajax, rails

As the title suggests, my main objective is to render a dynamic scss(.erb) file after an ajax call.
assets/javascripts/header.js
// onChange of a checkbox, a database boolean field should be toggled via AJAX
$( document ).ready(function() {
$('input[class=collection_cb]').change(function() {
// get the id of the item
var collection_id = $(this).parent().attr("data-collection-id");
// show a loading animation
$("#coll-loading").removeClass("vhidden");
// AJAX call
$.ajax({
type : 'PUT',
url : "/collections/" + collection_id + "/toggle",
success : function() {
// removal of loading animation, a bit delayed, as it would be too fast otherwise
setTimeout(function() {
$("#coll_loading").addClass("vhidden");
}, 300);
},
});
});
});
controller/collections_controller.rb
def toggle
# safety measure to check if the user changes his collection
if current_user.id == Collection.find(params[:id]).user_id
collection = Collection.find(params[:id])
# toggle the collection
collection.toggle! :auto_add_item
else
# redirect the user to error page, alert page
end
render :nothing => true
end
All worked very smooth when I solely toggled the database object.
Now I wanted to add some extra spices and change the CSS of my 50+ li's accordingly to the currently selected collections of the user.
My desired CSS looks like this, it checks li elements if they belong to the collections and give them a border color if so.
ul#list > li[data-collections~='8'][data-collections~='2']
{
border-color: #ff2900;
}
I added this to my controller to generate the []-conditions:
def toggle
# .
# .
# toggle function
# return the currently selected collection ids in the [data-collections]-format
#active_collections = ""
c_ids = current_user.collections.where(:auto_add_item => true).pluck('collections.id')
if c_ids.size != 0
c_ids.each { |id| #active_collections += "[data-collections~='#{id}']" }
end
# this is what gets retrieved
# #active_collections => [data-collections~='8'][data-collections~='2']
end
now I need a way to put those brackets in a scss file that gets generated dynamically.
I tried adding:
respond_to do |format|
format.css
end
to my controller, having the file views/collections/toggle.css.erb
ul#list<%= raw active_collections %> > li<%= raw active_collections %> {
border-color: #ff2900;
}
It didn't work, another way was rendering the css file from my controller, and then passing it to a view as described by Manuel Meurer
Did I mess up with the file names? Like using css instead of scss? Do you have any ideas how I should proceed?
Thanks for your help!
Why dynamic CSS? - reasoning
I know that this should normally happen by adding classes via JavaScript. My reasoning to why I need a dynamic css is that when the user decides to change the selected collections, he does this very concentrated. Something like 4 calls in 3 seconds, then a 5 minutes pause, then 5 calls in 4 seconds. The JavaScript would simply take too long to loop through the 50+ li's after every call.
UPDATE
As it turns out, JavaScript was very fast at handling my "long" list... Thanks y'all for pointing out the errors in my thinking!
In my opinion, the problem you've got isn't to do with CSS; it's to do with how your system works
CSS is loaded static (from the http request), which means when the page is rendered, it will not update if you change the CSS files on the server
JS is client side and is designed to interact with rendered HTML elements (through the DOM). This means that JS by its nature is dynamic, and is why we can use it with technologies like Ajax to change parts of the page
Here's where I think your problem comes in...
Your JS call is not reloading the page, which means the CSS stays static. There is currently no way to reload the CSS and have them render without refreshing (sending an HTTP request). This means that any updating you do with JS will have to include per-loaded CSS
As per the comments to your OP, you should really look at updating the classes of your list elements. If you use something like this it should work instantaneously:
$('li').addClass('new');
Hope this helps?
If I understood your feature correctly, actually all you need can be realized by JavaScript simply, no need for any hack.
Let me organize your feature at first
Given an user visiting the page
When he checks a checkbox
He will see a loading sign which implies this is an interaction with server
When the loading sign stopped
He will see the row(or 'li") he checked has a border which implies his action has been accepted by server
Then comes the solution. For readability I will simplify your loading sign code into named functions instead of real code.
$(document).ready(function() {
$('input[class=collection_cb]').change(function() {
// Use a variable to store parent of current scope for using later
var $parent = $(this).parent();
// get the id of the item
var collection_id = $parent.attr("data-collection-id");
show_loading_sign();
// AJAX call
$.ajax({
type : 'PUT',
url : "/collections/" + collection_id + "/toggle",
success : function() {
// This is the effect you need.
$parent.addClass('green_color_border');
},
error: function() {
$parent.addClass('red_color_border');
},
complete: function() {
close_loading_sign(); /*Close the sign no matter success or error*/
}
});
});
});
Let me know if my understanding of feature is correct and if this could solve the problem.
What if, when the user toggles a collection selection, you use jquery change one class on the ul and then define static styles based on that?
For example, your original markup might be:
ul#list.no_selection
li.collection8.collection2
li.collection1
And your css would have, statically:
ul.collection1 li.collection1,
ul.collection2 li.collection2,
...
ul.collection8 li.collection8 {
border-color: #ff2900;
}
So by default, there wouldn't be a border. But if the user selects collection 8, your jquery would do:
$('ul#list').addClass('collection8')
and voila, border around the li that's in collection8-- without looping over all the lis in javascript and without loading a stylesheet dynamically.
What do you think, would this work in your case?

Javascript - Detect is user is subscribed to youtube channel

I use Youtube subscribe button (https://developers.google.com/youtube/youtube_subscribe_button ).
Is there any way, while user clicks on it, to initiate my javascript function too?
You just need to add a data element with the function that you need to run.
<script>
function myEvent(e)
{
// do something here
}
</script>
<div class="g-ytsubscribe" data-channel="GoogleDevelopers" data-onytevent="myEvent"></div>
https://developers.google.com/youtube/youtube_subscribe_button#Handling_Events_Demo
just put the name of your function in an onclick like so:
<div id="whatever they make you call it" onclick="yourFunction();" />
or do this in javascript
document.getElementById("whatever it's called").onclick = "yourFunction();";
I'm not saying it's a good idea, and it will probably violate their terms of use, but this is what I would try.

Google AdWords: remove iframe added by tracking conversion code

I want to add Google AdWords to my site but the script I'm supposed to add creates an iframe in the dom. This iframe is visible and moves down 13px (its height) all my page.
Is there any way to avoid this? If not, can I hide it without affecting Google AdWords functionality? (It is an empty iframe).
There's an easy fix that doesn't affect the functionality of the code snippet. I've done this with no adverse effects. Just place the script within a hidden div like below and it should do the trick:
<div style="display:none">
<script type="text/javascript" src="//www.googleadservices.com/pagead/conversion.js">
</script>
</div>
#Mario is correct that there is a setting that will allow you to turn this display off. However, this setting doesn't seem to exist on the Google UI for remarketing tags, even though they do display this iframe (I think this is a bug on Google's end, as I imagine the "google_remarketing_only = true" flag was supposed to turn this iframe off and isn't working correctly).
I found out that you can also set this in the tracking JS by manually adding the flag "google_conversion_format = 3", like so:
<script type="text/javascript">
/* <![CDATA[ */
var google_conversion_id = 0123456789,
google_conversion_label = "XXXXXXXX",
google_custom_params = window.google_tag_params,
google_remarketing_only = true,
google_conversion_format = 3;
/* ]]> */
</script>
<script type="text/javascript" src="//www.googleadservices.com/pagead/conversion.js"></script>
This might be easier that regenerating the tags for some people, and solves the problem in the case that the UI doesn't support setting this option when generating the tags.
I normally add this CSS(3) rule to the stylesheet:
iframe[name=google_conversion_frame]
{
display: none !important;
}
Hope it helps.
you can also set max-height: 0; instead of display:none;
Not sure of implications of display none on the iframe.
This works back to ie6.
iframe[name="google_conversion_frame"] {
display: block;
max-height: 0;
}
The best and simplest solution that I have come across for this issue is simply to remove the frame from the document flow by adding the following code to the css stylesheet:
iframe[name="google_conversion_frame"]{
position:fixed;
}
Hope this helps

Resources