jQuery - How to not execute a single line command - javascript

I am relatively new in web development, so not sure I am explaining myself with the right terms, but have come to this little issue in a small project of mine:
I have a navigation menu with functionality (mainly style related) added through a switch. When browsing the mobile version of the site, the elements inside the menu change differently and as that I want that some of the commands listed on the switch case to not be executed. Here's a simplified example in the code I have:
//main (desktop) site functionality
$(".nav").click(function(event) {
var what = event.target.className;
switch (what){
case "item 1":
if ($(".text1").css("display") == "none"){
$(".text1").css("display", "block");
//this below is the line I don't want it to execute when on mobile
$(".item5").css("margin-top","196px");
} else {
$("text1").css("display", "none");
$(".item5").css("margin-top","0");
}
break;
});
//This is the function I use to know you are browsing on a mobile,
//where the main change inside the CSS media query is not displaying the "scroller"
function checkif(){
if ($(".scroller").css("display") == "none" ){
$(".item5").insertAfter($(".item12"));
if($(".text1").css("display") == "block"){
$(".item5").css("margin-top","0");
}
}
});
This is simplified, the changes within cases are more than just a margin, I change color, text, display of multiple elements. As that I don't want it to just not execute the switch, just to read the properties of the checkif() function and execute those instead of the ones in the switch when overlapping (as in the margin).
Thank you!

Seems like the key to your solution is to find whether the user is using a mobile device or not.
Let me suggest you another way to detect mobile device instead of your checkif() function.
function isMobile(){
// Get device user agent string
var userAgent = navigator.userAgent;
// Regular expression to sniff user agent
var regexp = /mobi/i;
// Test if the regexp exists in the user agent string
return regexp.test(userAgent);
}
console.log(isMobile());
More notes on this here.
Anyway, you should be doing what you need to do in CSS instead of JavaScript unless you have a good justification.

Related

JavaScript works in 2.0 version of Anki but not in 2.1 version

I'm using Anki to help my 5th grade son study Math. My son has reached a point where he needs to review how to approach specific problems and not simply memorize facts.
For example imagine you are trying to learn how to multiply fractions...
.45 X .61 = ?
I needed a way to put random math questions into an Anki card. This means that one day the card would display
.45 X .61 = ?
The next time the card is displayed the question might be..
.34 X .12 = ?
After scouring the internet I was able to find an article that explains how to do this...
https://yingtongli.me/blog/2015/03/15/random-question-generator-on-anki-using.html
I'm not a JavaScript developer, but I was able to get this technique to work after banging my head on my keyboard for a week.
Let me explain how you do this and then I'll explain where the problem is.
Install the 2.0 version of Anki.
Open Anki and create a new deck.
Add a new card to the deck.
The add card dialog box appears. At the very top are two long buttons... "Type" & "Deck". Click on the "Type" button to the left.
The Choose note type dialog box appears.
Click on the "Manage" button at the bottom of the dialog box.
Select the Basic note type and click the "Add" button to the left.
The first option should be "Add Basic". Click the "ok" button at the bottom of the dialog box.
Name your new note type.. "Scripting Note Type" then click "OK"
Hit escape.
Now select your new note type ("Scripting Note Type") then click "Choose".
Now we want to add an additional field. By default this note type has fields named "Front" & "Back". We want to add a third field named "Script".
There is a button labeled "Fields" near the top left. Click on this button.
Another dialog box appears. To the left is an Add button. Click it.
Enter Script in the dialog box that appears and click the "OK" button.
Now close the current dialog box by clicking the close button near the button toward the right side.
Let set up our cards now. Near the top left of the current screen are two buttons. The button to the right is labeled "Cards". Click this button.
This is where we set up the card template. One part of the JavaScript code goes into the Front Template. There should be the following text here currently...
{{Front}}
Delete everything and paste the following code.
<script>
function persist(cb) {
window.setTimeout(function() {
// Determine whether to use Anki's Bridge object (Desktop) or sessionStorage (AnkiDroid) to store data across sides.
var dummy = {};
var mode = "dummy";
if (typeof(py) !== "undefined") {
mode = "py";
py.data = py.data || {};
} else if (typeof(sessionStorage) !== "undefined") {
mode = "sessionStorage";
}
var dataObj = {
setItem: function(key, val) {
if (mode === "dummy") {
dummy[key] = val;
} else if (mode === "py") {
py.data[key] = val;
} else if (mode === "sessionStorage") {
sessionStorage.setItem(key, val);
}
},
getItem: function(key, def) {
var val = undefined;
if (mode === "dummy") {
val = dummy[key];
} else if (mode === "py") {
val = py.data[key];
} else if (mode === "sessionStorage") {
val = sessionStorage.getItem(key);
}
if (val == null) {
return def;
} else {
return val;
}
},
tryItem: function(key, val) {
var currVal = dataObj.getItem(key, undefined);
if (currVal == null) {
dataObj.setItem(key, val);
return val;
} else {
return currVal;
}
},
clear: function() {
if (mode === "dummy") {
dummy = {};
} else if (mode === "py") {
window.py.data = {};
} else if (mode === "sessionStorage") {
sessionStorage.clear();
}
}
};
if (!document.getElementById("back")) {
dataObj.clear();
}
cb(dataObj);
}, 0); //Execute after Anki has loaded its Bridge object.
}
</script>
<script>
var code = (function () {/* {{Script}} */}).toString();
code = code.replace(/^[^\/]+\/\*!?/, "").replace(/\*\/[^\/]+$/, ""); //Strip beginning/ending comments.
code = code.replace(/<div>/g, "\n").replace(/<\/div>/g, "\n").replace(/<br \/>/g, "\n"); //Strip HTML.
code = code.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&"); //Replace special symbols.
eval(code);
</script>
<div id="front">{{Front}}</div>
This JavaScript takes the JavaScrippt code that we will place in the "Script" field and executes it with an eval statement.
Notice in this code the persist function that keeps values consistent between the front and back of the Anki card. My issue is related to this persist function.
In the bottom third of the screen is a "Back Template". Delete everything and paste the following code...
{{FrontSide}}
<hr id=answer>
<div id="back">{{Back}}</div>
On the lower right click the "Close" button.
Now we need to fill in the fields. The values in front and back do not matter. I put "Front" in Front and "Back" in back.
For the Script field we want to add some code. We need to open the HTML view before we paste code. Make sure your cursor is in the Script fields.
In the 2.0 version of Anki there is a button with a picture of a down arrow in the upper right portion of the screen. The 2.1 version has "..." instead of the down arrow button.
Click this arrow and a menu appears. Select the "Edit HTML" menu option.
Paste the following code...
window.setTimeout(function(){
persist(function(data) {
var num1 = data.tryItem("num1",Math.random());
document.getElementById("front").innerHTML = num1;
document.getElementById("back").innerHTML = num1;
});
},0);
This works in the 2.0 version of Anki.
Here is the Question
And here is the Answer...
We generated a random number in JavaScript and that value is persisted between the front and back of an Anki Card. This opens up worlds of possibility for Math and Science Anki cards using random numbers.
However, all is not well in Whoville.
When you run this code on the 2.1 version of Anki it does not work.
The script is bypassed and the static values of the Front and Back fields are displayed.
JavaScript is still being executed. I could put an alert statement in the Script field and it works.
The problem is in the Persist function. The newer version of Anki does not like something about the Persist function.
Can anyone point me in the right direction.
I figured it out...
The issue is that Anki 2.1 eliminated the ability to store persistent data in the window.py object.
Now you might be asking (as I did) what is the window.py object? I'm not 100% sure but I believe its an internal Python object that allows some interactions between JavaScript and Python. Anki is written in Python.
In any case window.py is no longer accessible in Anki 2.1 which mean the technique I described in my question (see above) no longer works in Anki 2.1.
Fortunately, the author the article (referenced above in the question) mentioned that the window object is shared across invocations of an Anki card question and answer.
I started playing around with the window object in Javascript and I was able to get data persistence working in Anki 2.1 (This also works in Anki 2.0)
In the question above I go into great depth into to this technique to generate persistent random values in your Anki cards. This means you generate a random number in the question and that same values is available in the answer. I won't go into as much detail here, but refer the the question above for more details.
In the "Front Template" here is the code.
<script>
var code = (function () {/* {{Script}} */}).toString();
code = code.replace(/^[^\/]+\/\*!?/, "").replace(/\*\/[^\/]+$/, ""); //Strip beginning/ending comments.
code = code.replace(/<div>/g, "\n").replace(/<\/div>/g, "\n").replace(/<br \/>/g, "\n"); //Strip HTML.
code = code.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&"); //Replace special symbols.
eval(code);
</script>
<div id="front">{{Front}}</div>
This is the same code from the article. I have simply removed some code that no longer works.
Here is the card script code...
window.setTimeout(function(){
var windowsref = window; //Set a reference to the window object
if ((("num1" in windowsref) == false) || windowsref.cnt != 1) {
windowsref.num1 = Math.random();
windowsref.cnt = 0;
}
windowsref.cnt++;
document.getElementById("front").innerHTML = windowsref.num1;
document.getElementById("back").innerHTML = windowsref.num1;
},0);
This code works only in Anki 2.1 desktop. I also tested iOS and it works there as well.
I hope this helps someone else.
check out
https://github.com/SimonLammer/anki-persistence
It uses different approaches to achieve persistence.
But Anki environment is somewhat unpredictable, and you need to check if the code has succesfuly initialized.
glad to hear that you found a solution, codingguy.
For further information, I want to add
I have dabbled with the same idea around 2014.
I got it to work using JavaScript and Cookies in my note templates.
For that, I had to modify Anki with a plugin (to monkey-patch creation of the Qt Web View) which I shared as Anki 2.0 plugin JS Booster.
Adapted for Anki 2.1, another developer published the same solution here Anki 2.1 plugin Cookie Monster.

Click counter without javascript

I'm using this javascript for a click counter in my blogger blog:
function clickCounter() {
if(typeof(Storage) !== "undefined") {
if (sessionStorage.clickcount) {
sessionStorage.clickcount = Number(sessionStorage.clickcount)+1;
} else {
sessionStorage.clickcount = 1;
}
document.getElementById("result").innerHTML = "Correct! " + sessionStorage.clickcount + " Smart answers 'til now.";
} else {
document.getElementById("result").innerHTML = "Sorry, your browser does not support this quiz...";
}
}
<button onclick="clickCounter()" type="button">Suspension</button>
Is there any way to create something similar through a non javascript method?
Can you help me triger an event (extra text message through popup or within the page) every 5, 10, 20, 100 clicks?
Thank you very much
HTML, and the Web in general, was designed to be stateless.
When you pull up a page, it should be like the first time -- and every time -- you pull up the page.
Since then, people have come up with a number of techniques to add state -- to save data, but they all involved one of two methods -- or sometimes both.
Method 1: Store state on the server.
This method uses HTML forms or cookies to slip information to the server when you load and reload a page.
Method 2: Store state in the client
While there are some older versions of Internet Explorer that can be coded in VBA, we are going to ignore that. The only "real" way to run any kind of code on the client, to store any data, is to use JavaScript.
Method 3: Use the client to talk to the server
Using Ajax, you can let your client talk to the server, but without doing a page reload. This still uses JavaScript.
So, to answer your question:
Without a server
Without JavaScript
No, you cannot save or store anything.
I have not tried this but...
What if you put multiple buttons positioned on top of each other. As each one is clicked, it can be made to vanish with something like
a:visited { display: none; }
The ones that need to display a message (5th, 10th, etc.) have different behavior attached.
See on click hide this (button link) pure css

Creating a "try it" code editor- any security issues?

I'm creating a simple "try it yourself" code editor like the one found on W3schools. Looking at the source, it seems all that one does is use JavaScript's document.write() command to write whatever is entered into the textarea on the left into the iframe on the right, without any sanitation:
function submitTryit() {
var text = document.getElementById("textareaCode").value;
var ifr = document.createElement("iframe");
ifr.setAttribute("frameborder", "0");
ifr.setAttribute("id", "iframeResult");
document.getElementById("iframewrapper").innerHTML = "";
document.getElementById("iframewrapper").appendChild(ifr);
var ifrw = (ifr.contentWindow) ? ifr.contentWindow : (ifr.contentDocument.document) ? ifr.contentDocument.document : ifr.contentDocument;
ifrw.document.open();
ifrw.document.write(text);
ifrw.document.close();
}
Is that secure in itself, or am I missing something inside the w3school's editor that does something more for security's sake?
Thanks,
If that javascript is not persisted in such a way that it can render to browsers of users other than its authors, you should be fine. Otherwise you are creating a platform that can be used to propagate XSS worms. Just allowing a user to execute arbitrary javascript on their own page is no less secure than that most modern browsers having a debugging console that lets a user execute javascript.

Getting Backbutton to work in single page website and implementing "speaking" URLs

I have a single page website and would like to achieve the following:
back button working as if it was a normal website
and instead of say,
www.mysite.com/index.php?p=#this-is-a-great-product
I'd like to have this url
www.mysite.com/this-is-a-great-product
while still having back button working properly.
Regarding 1.) I use the following code ive found which works great:
<!-- Getting BackButton to work properly -->
<script type="text/javascript">
var times = 0;
function doclick() {
times++;
}
function doclick() {
times++;
location.hash = times;
}
window.onhashchange = function() {
if (location.hash.length > 0) {
times = parseInt(location.hash.replace('#',''),10);
} else {
times = 0;
}
}
</script>
…but of course it just changes any anchors to /#1, then /#2 and so forth ro get the backbutton to work. But as I'm not a programmer I don't know how to change it… :(
Regarding 2.) i can add in htaccess this:
>RewriteEngine On
>RewriteRule ^([^/.]+)/?$ /index.php?page=$1
and this changes /index.php?p=products to /products.
So how do I change the above code (under 1.) so it doesn't change all anchors to #1, #2, etc. but instead references / uses the urls I achieved under 2, like
www.mysite.com/this-is-a-great-product
And (probably a very dumb question, but a very important one) -given I use only the new url links on my site- is there any danger that this still might result in duplicate content in any way?
Regarding this, should I (for that reason or any other) sefreferential my single page index.php to itself using rel canonical link=index.php?
Thanks so much in advance!
As mentioned, you will want to use the HTML5 History API. Please note, this API is relatively new and therefore browser support is a concern. At the time of writing, approximately 71% of global Internet users have support for it (see http://caniuse.com/#feat=history for browser support information). Therefore, you will want to ensure you have a fall-back solution for this. You will likely want to use the older #! solution that was popular before the HTML 5 History API was adopted.
If you use the history API to replace, for example, example.com/#!settings with example.com/settings and a user bookmarks that nicer URL, then when they go to visit it, their browser will make a request to the server for /settings (which doesn't actually exist in the web server's context). Therefore, you will need to make sure your web server has some redirection rules (i.e. RewriteEngine) such that it can take the pretty URLs and redirect them to the #! version (and then if the user's browser supports the history API it can replace that with the nice URL).
If you aren't very comfortable programming yourself, I'd recommend using a JavaScript library that does a lot of the work for you. I did some quick searching and discovered the following, though there might be better ones out there: https://github.com/browserstate/history.js
Basically i have created a small prototype on jsfiddle which tracks all the urls accessed via ajax calls.
Also contains navigation to access links back and forth .
How It Actually Works:
I have created a global array called history, which keeps track of all urls accessed via ajax in sequence.
also there a global index defined to keep track of the url being accessed when navigating back and forth the links in history array.
There is History section at the bottom of the jsfiddle, which shows the sequence in which the links are accessed by capturing the link names and posting them in the order in which they were accessed.
JS Code:
$(function () {
var history = [];
var index = 0;
$('.links').on('click', function () {
$('#history').append($(this).text());
var address = $(this).attr('data-ref');
index += 1;
history[index] = address;
$('.links').attr('disabled', 'disabled');
loadExternalPage(address);
console.log('list:' + history);
});
$('#back').on('click', function () {
console.log(index);
index -= 1;
console.log(index);
console.log(history[index]);
loadExternalPage(history[index]);
});
$('#forward').on('click', function () {
console.log(index);
index += 1;
console.log(index);
console.log(history[index]);
loadExternalPage(history[index]);
});
var loadExternalPage = function (address) {
console.log(history[index]);
$('#result-section').load(address, function () {
console.log('data-loaded');
$('.links').removeAttr('disabled');
});
};
});
Live Demo # JSFiddle:http://jsfiddle.net/dreamweiver/dpwmcu0b/8/
Note: This solution is far from being perfect, so dont consider it as final solution but rather use it as a base to build upon
On using BACK and FORWARD functions in the browser top-left button:
In principle, there is no great problem with this as long as you work with the existing storage object (a stack) for previously visited web pages on your browser. This object is the history object and you can see what is in it anytime by right-clicking and selecting "Inspect", then selecting the "Console" tab, then enter window.history and enter.
Check out the Browser Object Model (BOM) section of Pro Java For Web Developers (Frisbee) for the background to the history object. (Just a few pages, an easy read, don't worry.) Just remember that in this process you are storing the new page that you move to, not the old page that you are leaving !
For a simple SPA example, look at this example. codepen.io/tamjk/pen/NWxWOxL
In regard to the URL, the method that the history object uses to load a new page state into the history stack, i.e. pushState(...), has an optional third parameter for associating a dummy URL for each web page that is stored.
Personally, when I first sorted out the BACK & FORWARD functions, I did not use dummy URLs as the browser was being confused by them and I had enough to do sorting out the history sequence using just the first two parameters, i.e.
the state object - a JSON holding enough data to recreate the page stored
a title for the page I expect that you could also use a dummy URL but I will leave that to the student as an exercise, as they say.
But you can add the URL of the new page if you want to.
In the example above, for the state object I just used the IDs of the page's nav link and its content element.
For the title, I programmatically changed the HTML's page title element with each change of page. I did this after noticing that the browser listed the previous pages according to the title element in the HTML code.
Unfortunately, this title does not show up on CodePen when you right-click on the browser BACK and FORWARD buttons due to CodePen's system not allowing it. But it will show on your own sites.
It's important that whatever method you use to store current web page states when using the navbar links to navigate, you DO NOT ADD page states to the browser history when you arrive at them using BACK or FORWARD buttons. Otherwise your history stack will have repetitions of entries going back and deletion of entries going forward.
In the CodePen, this was achieved by having the addToHistory(..) function separate to and outside the scope of the switchPage(...) function. This allows you use of the switchPage function in both normal navbar navigation and browser BACK/FORWARD navigation. The third parameter of switchPage(...) is a boolean indicating if the page is to be stored in history or not.
Anyway, this is just something to get you started.

Chrome JavaScript location object

I am trying to start 3 applications from a browser by use of custom protocol names associated with these applications. This might look familiar to other threads started on stackoverflow, I believe that they do not help in resolving this issue so please dont close this thread just yet, it needs a different approach than those suggested in other threads.
example:
ts3server://a.b.c?property1=value1&property2=value2
...
...
to start these applications I would do
location.href = ts3server://a.b.c?property1=value1&property2=value2
location.href = ...
location.href = ...
which would work in FF but not in Chrome
I figured that it might by optimizing the number of writes when there will be effectively only the last change present.
So i did this:
function a ()
{
var apps = ['ts3server://...', 'anotherapp://...', '...'];
b(apps);
}
function b (apps)
{
if (apps.length == 0) return;
location.href = apps[0]; alert(apps[0]);
setTimeout(function (rest) {return function () {b(rest);};} (apps.slice(1)), 1);
}
But it didn't solve my problem (actually only the first location.href assignment is taken into account and even though the other calls happen long enough after the first one (thanks to changing the timeout delay to lets say 10000) the applications do not get started (the alerts are displayed).
If I try accessing each of the URIs separately the apps get started (first I call location.href = uri1 by clicking on one button, then I call location.href = uri2 by clicking again on another button).
Replacing:
location.href = ...
with:
var form = document.createElement('form');
form.action = ...
document.body.appendChild(form);
form.submit();
does not help either, nor does:
var frame = document.createElement('iframe');
frame.src = ...
document.body.appendChild(frame);
Is it possible to do what I am trying to do? How would it be done?
EDIT:
a reworded summary
i want to start MULTIPLE applications after one click on a link or a button like element. I want to achieve that with starting applications associated to custom protocols ... i would hold a list of links (in each link there is one protocol used) and i would try to do "location.src = link" for all items of the list. Which when used with 'for' does optimize to assigning only once (the last value) so i make the function something like recursive function with delay (which eliminates the optimization and really forces 3 distinct calls of location.src = list[head] when the list gets sliced before each call so that all the links are taken into account and they are assigned to the location.src. This all works just fine in Mozilla Firefox, but in google, after the first assignment the rest of the assignments lose effect (they are probably performed but dont trigger the associated application launch))
Are you having trouble looping through the elements? if so try the for..in statement here
Or are you having trouble navigating? if so try window.location.assign(new_location);
[edit]
You can also use window.location = "...";
[edit]
Ok so I did some work, and here is what I got. in the example I open a random ace of spades link. which is a custom protocol. click here and then click on the "click me". The comments show where the JSFiddle debugger found errors.

Categories

Resources