How do adjust html output based on operating system? - javascript

Problem definition
I need to put text similar to the following on a web page:
If the user is operating on a Windows based system, I want them to see:
Look for the file C:\Users\yourname\dir1\dir2\filename on your Windows PC
If the user is operating on an OS X based system, I want them to see:
Look for the file /Users/yourname/dir1/dir2/filename on your Macintosh PC
If the user is operating on[any other|Linux] based system, I want them to see:
Look for the file /home/yourname/dir1/dir2/filename on your Linux PC
Research
I have managed to create three almost identical javascripts that partially work. One script gives me the value of the osHomeDir, another osType, and the other gives me the value of the osSeparator. My problem is that I don't know how to get these values out of the java script and into my webpage consistently. What I get is that the first substitution works, but I can't repeat it - for instance, on my Mac I see:
Look for the file /Users/yournamedir1dir2filename on your Macintosh PC rather than the desired:
Look for the file /Users/yourname/dir1/dir2/filename on your Macintosh PC
Note how only the FIRST substitution of osSeparator worked.
Desired outcome
I'm hoping someone can show me two things:
How to make the scripts more efficient - three almost identical
scripts could surely be changed into one that returned three values
The ability to be able to use the result more than once (as happened
in my example)
Notes
Don't get too concerned about THIS example - I have simplified it to make it readable. I have the same problem in many places. I know I COULD solve this one my returning the whole sentence in the one script, but that would miss the point entirely.
My code
<!DOCTYPE html>
<html>
<body>
<p> Look for the file <a id="osHomeDirId"></a><a id="osSeparatorId"></a>yourname<a id="osSeparatorId"></a>dir1<a id="osSeparatorId"></a>dir2<a id="osSeparatorId"></a>filename on your <a id="osTypeId"></a> PC</p>
<script>
var osHomeDir = "/home";
if (window.navigator.platform.indexOf("Win") != -1) osHomeDir="C:&#92Users";
if (window.navigator.platform.indexOf("Mac")!=-1) osHomeDir="/Users";
document.getElementById("osHomeDirId").innerHTML=osHomeDir;
</script>
<script>
var osType = "Linux";
if (window.navigator.platform.indexOf("Win") != -1) osType="Windows";
if (window.navigator.platform.indexOf("Mac")!=-1) osType="Macintosh";
document.getElementById("osTypeId").innerHTML=osType;
</script>
<script>
var osSeparator = "/";
if (window.navigator.platform.indexOf("Win") != -1) osSeparator="&#92";
document.getElementById("osSeparatorId").innerHTML=osSeparator;
</script>
</body>
</html>

You use document.getElementById() which only gets the first element with the specified id. Also you shouldn't use id's multiple times. You should make it a class instead. You need to use document.getElementsByClassName() which returns an array, then loop through all the elements of that array and set the innerHTML properties to osSeparator.
var separators=document.getElementsByClassName("osSeparatorId");
for(var i=0;i<separators.length;i++){
separators[i].innerHTML=osSeparator;
}
Then you need to change the html to <a class="osSeparatorId">

Working Answer:
I changed approach to use functions instead of getElementsByClassName
I can now get the "desired" outcome, but I'm sure there could be some more efficiencies added to the script to save the if...if else...if else construction at the end.
Here's the code that works:
<script>
function localised() {
var printItem=arguments[0];
var osHomeDir="/home";
var osSeparator="/";
var osType="Linux";
if (window.navigator.platform.indexOf("Win") != -1) {
osHomeDir="C:&#92Users";
osType="Windows";
osSeparator="&#92"
}
if (window.navigator.platform.indexOf("Mac")!=-1) {
osHomeDir="/Users";
osType="Macintosh";
}
if (printItem=="osHomeDir") document.write(osHomeDir);
else if (printItem=="osType") document.write(osType);
else if (printItem=="osSeparator") document.write(osSeparator);
}
</script>
<p>Look for the file <script>localised("osHomeDir");</script><script>localised("osSeparator");</script>yourname<script>localised("osSeparator");</script>dir1<script>localised("osSeparator");</script>dir2<script>localised("osSeparator");</script>filename on your <script>localised("osType");</script> PC</p>
</body>
</html>

Not an answer...yet
Clearly someone has got #Max Meijer's answer working (because it has been marked up) but when I try it, it still doesn't work. #Max (or anyone else) I invite you to edit this answer until it does work.
Remember: Desired output (eg on OS X)=>
Look for the file /Users/yourname/dir1/dir2/filename on your Macintosh PC
Output using Max's code above=>
Look for the file /Usersyournamedir1dir2filename on your Macintosh PC
Note that in this case not even a single instance of osSeparatorId is displayed.
And I still have 3 separate scripts to do pretty much the same job.
Proof that I've implemented Max's code exactly as described:
<!DOCTYPE html>
<html>
<body>
<!--Note that the following line is now using <a class="osSeparatorId"> instead of <a id="osSeparatorId"> -->
<p> Look for the file <a id="osHomeDirId"></a><a class="osSeparatorId"></a>yourname<a class="osSeparatorId"></a>dir1<a class="osSeparatorId"></a>dir2<a class="osSeparatorId"></a>filename on your <a id="osTypeId"></a> PC</p>
<script>
var osHomeDir = "/home";
if (window.navigator.platform.indexOf("Win") != -1) osHomeDir="C:&#92Users";
if (window.navigator.platform.indexOf("Mac")!=-1) osHomeDir="/Users";
document.getElementById("osHomeDirId").innerHTML=osHomeDir;
</script>
<script>
var osType = "Linux";
if (window.navigator.platform.indexOf("Win") != -1) osType="Windows";
if (window.navigator.platform.indexOf("Mac")!=-1) osType="Macintosh";
document.getElementById("osTypeId").innerHTML=osType;
</script>
<!--Note that the following is cut and pasted from Max's answer-->
<script>
var separators=document.getElementsByClassName("osSeparatorId");
for(var i=0;i<separators.length;i++){
separators[i].innerHTML=osSeparator;
}
</script>
</body>
</html>

Related

Need JS to modify the element following the script

I have a report authoring tool that lets me add descriptive text in the report header, prior to a table containing the data. The examples included with the tool show how include Javascript in the description for various special effects. I would like to change certain cells in the table into links to other reports. Here's the HTML produced by the reporting tool.
<div class="element-info">
<div class="description">My Description</div>
<div class="properties">
<table>...</table>
</div>
</div>
I have tried replacing "My Description" with the following, but (perhaps unsurprisingly) it's changing something other than the table.
<div>My Description
<script type="text/javascript">
// currentScript is supported in my version of Firefox.
var me = document.currentScript;
// go up two levels to get the enclosing div
var element_info = me.parentElement.parentElement;
// from there we want the properties div, then the table
var mytable = element_info.lastChild.firstChild;
mytable.style.color = "red";
</script>
</div>
I expect that the problem is that when the script runs, the HTML in the following div has not yet been parsed. Mozilla says that the defer attribute will be ignored in scripts without a src= attribute, and I've verified that it does nothing.
Although my example code is using plain Javascript, the authoring tool is based on jQuery, so it's full repertoire is available if needed.
If the problem involves the fact that the html hasn't yet been parsed, you can immediately gain a reference to the script, but only later utilize it, once the document is loaded. It would look like this:
<div>My Description
<script type="text/javascript">
// Get the reference immediately...
var script_of_interest = document.currentScript;
// And only use it once everything is loaded:
window.onload = function() {
var element_info = script_of_interest.parentElement.parentElement;
var mytable = element_info.lastChild.firstChild;
mytable.style.color = "red";
};
</script>
</div>
Make sure your the node your script operates on is loaded before the execution, otherwise your the node may be undefined or just nothing. You may try wrap your code with
$(document).ready(function(){
//your code
}).

Try It Yourself Editor JS

I have created a very simple editor that has been working great. However, I just tried to put JavaScript into it and I can't get it to work.
The code for the editor:
<div id="buttoncontainer">
<input id="button" onclick="update();" type="button" value="Update page">
</div>
<div id="tryitcontainer">
<textarea id="codebox"></textarea>
<iframe id="showpage"></iframe>
</div>
The JavaScript for the editor:
<script>
function update() {
var codeinput = document.getElementById('codebox').value;
window.frames[0].document.body.innerHTML = codeinput;
}
</script>
I just wanted to run some simple JavaScript that changes an image when it is clicked. This code works fine when I run it in a full browser, so I know its the editor thats the problem.
Is there a simple fix for this that I'm missing?
The button is not finding the update() method. You need that function to be globally available:
http://jsfiddle.net/t5swb7w9/1/
UPDATE: I understand now. Internally jQuery basically evals script tags. There's too much going on to be worth replicating yourself... either use a library to append, or eval the code yourself. Just a warning that eval'ing user input is rarely a good thing and is usually a welcome mat for hackers.
window.myScope = {
update: function() {
var div = document.createElement('div'),
codeinput = document.getElementById('codebox').value,
scriptcode = "";
div.innerHTML = codeinput;
Array.prototype.slice.apply(div.querySelectorAll("script")).forEach(function(script) {
scriptcode += ";" + script.innerHTML;
div.removeChild(script);
});
window.frames[0].document.body.appendChild(div);
// hackers love to see user input eval'd like this...
eval(scriptcode);
}
};
And then you would update your button like so:
<input id="button" onclick="myScope.update();" type="button" value="Update page">
Or, even better, use addEventListener and forget the onclick part altogether. I'll let you do that research on your own ;)
JavaScript inserted via innerHTML will not be executed due to security reasons:
HTML5 specifies that a <script> tag inserted via innerHTML should not execute.
from MDN: Element.innerHTML - Security considerations, see also: W3: The applied innerHTML algorithm.
A possible solution using the jQuery method .append() works around that, as it somehow evals the content. But this will still not solve your problem, as the JavaScript code is executed in the current scope.
Here's a test scenario:
function update() {
var codeinput = document.getElementById('codebox').value;
$(window.frames[0].document.body).append(codeinput);
}
Try it here
Try to insert this script:
<script>
alert( document.getElementById('tryitcontainer') );
</script>
and this one:
<p id="test">Test</p>
<script>
window.frames[0].document.getElementById('test').innerHTML = 'updated';
</script>
The first one will return a [object HTMLDivElement] or similar. Here you can see, that you're still in the same scope as the parent frame. The second one will correctly update the content within the iframe. Keep that in mind, when experimenting with those things.
Maybe Executing elements inserted with .innerHTML has some more infos for you.

How to get Text of Nested Span HTML element?

I want to get text from Nested SPAN element in Following HTML code:
<span id='result_box'>
<span class="hps">text_content</span>
</span>
I want to get "text_content" value using JavaScript.
I have tried this but have a problem:
var resBox=document.getElementById('result_box');
var strTrans=resBox.getElementsByTagName('span')[0].innerHTML;
alert(strTrans);
EDIT: Actually i want to do this from Online Page
your code works fine. i guess your problem is you are executing these code when DOM not loaded completely.if you are testing something, you can try this.
window.onload = function () {
//put your code here,it will alert when page loaded completely.
};
or put the script after your span element. like this.
<span id='result_box'>
<span class="hps">text_content</span>
</span>
<script type='text/javascript'>
var resBox=document.getElementById('result_box');
var strTrans=resBox.getElementsByTagName('span')[0].innerHTML;
alert(strTrans);// it will alert
</script>
you get the element by classname.. document.getElementsByClassName() and then grabbing the first item off the resulting node list
window.onload = function () {
document.getElementsByClassName("hps")[0].innerHTML
};
jsfiddle
var resBox=document.getElementById('result_box');
var strTrans=document.getElementsByTagName('span')[0].innerText;
alert(strTrans);​
or better
strTrans = document.querySelector(".hps").innerText ;
got you, i guess you embed a link in your html page,then you wanna manipulate DOM in the page you embed,right? if so, you can check browser same origin policy.
if you wanna implement online translation via google, you can google 'google translate api', google provides a api to others for implementing online translation in their own applications.
it seems like bing also provides a api.i'm not sure.

Widget for webmasters in JavaScript and ID-collision

I would like to create a widget that could be placed in another websites/forums.
<script type="text/javascript" src="http://example.com/x.js" />
<div id="myid"></div>
But there is a problem - when someone put on one website two or more such widgets they will not work correctly because of the ID.
What can I do to prevent such situation?
I can't use class because I need to have access to this div from JS.
I thought about adding a random-generated number to the end of ID, but there will be still possibility of ID-collision (small, but there will be).
The best way is to be more flexible. So, instead of having a defined html id and force the user to have it, you should provide a way to let user choose its id. And so, a call must be done.
Something like this is cleaner :
<script type="text/javascript" src="http://example.com/x.js"></script>
<script type="text/javascript">
X.callMethod("myId");
</script>
<div id="myid"></div>
This method has two advantages :
it lets user define its own id
the user specifies what behavior he wants from your script. With, that, you could add other methods which can be used in same way : user doesn't have the feeling that your code is intrusive, it's him who decides if he wants a feature or not.
You could just include the script... which at processing time generates a div and assigns it a unique ID. (Within that logic, you could check for duplicates)
Presuming you don't expect your widget to be included 1,000's of times... something like this (untested) should work.
var uniqueID;
var foundUniqueID = false;
var idx = 0;
while(foundUniqueID != true){
uniqueID = 'myID_' + idx;
if(document.getElementById(uniqueID)){
idx++;
} else {
foundUniqueID = true;
}
}
//create your DIV with your uniqueID etc.

How write to page without document.write length limitation

I'm very much not a javascript/html/css person.
Even so I find myself in the position of having to do a webpage. It's static the only thing you can do on it is click on flags to change dispay-language based on a cookie.
If the cookie says "this" I write text in one language, and if the cookie says "that" I write in another language. This works perfectly but I have to use lots of document.write statements and it's ugly and cumbersome.
Right now I type the text I want and use a macro in emacs to fold the text at about
80 chars and put document.write(" in the beginning of each line and "); at the end. I then paste it into the web page in a if(cookie_this) { } else { }.
There must be a better way to do it... Please?
Edit:
I was looking workaround for the limitations in document.write
Constraints:
No server side magic, that means no ruby/php/perl
One page only, or rather only one visible url
The solution should be simpler than the working one I have
Expanding on artlung's answer:
You can display or hide things given a lang attribute (or any other criteria, such as a class name). In jQuery and HTML:
<p>Language:
<select id="languageSelector">
<option value="en">English</option>
<option value="es">Español</option>
</select>
</p>
<div lang="en-us">
Hello
</div>
<div lang="es">
Hola
</div>
<script type="text/javascript">
var defaultLanguage = 'en';
var validLanguages = ['en', 'es'];
function setLanguage(lang, setCookie) {
if(!$.inArray(languages, lang))
lang = defaultLang;
if(typeof(setCookie) != 'undefined' && setCookie) {
$.cookie('language', lang);
}
// Hide all things which can be hidden due to language.
$('*[lang]').filter(function() { return $.inArray(languages, $(this).attr('lang')); }).hide();
// Show currently selected language.
$('*[lang^=' + lang + ']).show();
}
$(function() {
var lang = $.cookie('language'); // use jQuery.cookie plugin
setLanguage(lang);
$('#languageSelector').change(function() {
setLanguage($(this).val(), true);
});
});
</script>
jQuery can do this with I lot less ease, but you could create an element then set that elements innerHTML property. You may have to change your call slightly so that you append the child element. See createElement function for more info. For example
<script type="text/javascript">
function writeElement(language, elementId) {
var newElement = document.createElement("span");
if (language = "this") {
newElement.innerHTML = "text for this";
}
else {
newElement.innerHTML = "text for that";
}
var element = document.getElementById(elementId);
element.appendChild(newElement);
}
</script>
Usage
<span id="data1"></span>
<script type="text/javascript">
writeElement("this", "data1")
</script>
Add a comment if you can support jQuery and you want a sample of that instead.
I think that the right way to approach this is to parse the Accept-Language header, and do this server-side.
But in the instance that you are stuck with client-side scripting. Say your content was marked like this
<script type="text/javascript">
if(cookie_this) {
document.getElementById('esContent').style.display = 'block';
} else {
document.getElementById('enContent').style.display = 'block';
}
</script>
<div id="esContent" style="display:none">
Hola, mundo.
</div>
<div id="enContent" style="display:none">
Hello, world.
</div>
This does not degrade for people with CSS enabled, and JavaScript disabled. Other approaches might include using Ajax to load content based on a cookie value (you could use jQuery for this).
If you just want one visible URL, but can host multiple pages on the server you could also try XHR. I use jQuery because I am most familiar with it although it would be possible to implement in javascript alone:
<html>
<head>
<script type="text/javascript" src="path/to/jquery.js"> </script>
<script type="text/javascript">
$(document).ready(function () {
if (cookie_this) {
$("body").load("onelanguage.html body");
} else {
$("body").load("otherlanguage.html body");
}
});
</script>
</head>
<body>
</body>
</html>
The detection should be on the server (preferably based on the Accept-Language header the client sent), and you should send a static file that has already been localized.
This does not fit the (edited) criteria of the original question, but may be useful regardless.
Use a server-side script. What you're looking for can easily be done in PHP. You'd probably want a hierarchy of documents based on language, and would look up given that. For example, a directory tree:
/en/
/en/page1.html
/en/page2.html
/es/
/es/page1.html
/es/page2.html
In PHP it's as simple as
$language = $_GET['lang'];
$page = $_GET['page'];
include($language . '/' . $page);
// URL is: /whatever.php?lang=LANGUAGE_HERE&page=PAGE_HERE
However, that has many security issues along with it. Sanitize your input and make sure the directory and file exist. Fuller example:
$contentRoot = './'; // CHANGE ME. Do include trailing /.
$defaultLanguage = 'en'; // CHANGE ME.
$defaultPage = 'home'; // CHANGE ME.
if(isset($_GET['lang']))
$language = $_GET['lang'];
else
$language = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 2);
if(isset($_GET['page']))
$page = $_GET['page'];
else
$page = $defaultPage;
$languageDir = basename($_GET['lang']) . '/';
$pageFile = basename($page) . '.html';
if(!file_exists($contentRoot . $languageDir) || !is_dir($contentRoot . $languageDir))
$languageDir = $defaultLanguage;
$fullFileName = $contentRoot . $languageDir . $pageFile;
if(!file_exists($fullFileName) || !is_file($fullFileName) || !is_readable($fullFileName))
$pageFile = $defaultPage;
readfile($fullFileName);
// Or, if you want to parse PHP in the file:
include($fullFileName);
You may also want to use mod_rewrite (Apache) to allow URL's such as http://www.mysite.com/en/page1. (Just be sure to hide the actual page.)
// TODO mode_rewrite rules
Another approach is putting the above hierarchy into the document root and handing out URL's directly. This gives you less power (e.g. templating is more difficult), however, and you have to worry about external media being referenced properly.
If you're looking for a dynamic approach, on the client side use Javascript to fetch the data using Ajax. This is also trivial, and does not require a dynamic server backend. I recommend a Javascript framework such as jQuery to make this as easy as possible.
There is no good way to do this in JS. The best way is to use VERY simple PHP code.
But, if you want, there is a way in JS - prepare pages like these:
some pages with different language versions, like index_en.html, index_ru.html
main index.html page, where you have code like
if(cookie) windows.location.replace('index_en.html') else ...

Categories