Determine the /category/ in the URL with Javascript - javascript

I have a need to include (through Javascript) different content depending on the major category captured from the url.
The website is laid out like so:
http://example.com/Category/Arts/Other/Sub/Categories/
http://example.com/Category/News/Other/Sub/Categories/
http://example.com/Category/Sports/Other/Sub/Categories/
http://example.com/Category/Business_And_Finance/Other/Sub/Categories/
The different major categories above are:
Arts, News, Sports, and Business_And_Finance
What is the best way to accomplish this in javascript.
What I need may look something like the following,
if (category = Arts) {
alert("Arts");
}else if (category = News) {
alert("News");
}...
Thank you in advance.

Split the location.href then do a switch on the appropriate variable. So, for example:
var url = document.location.href,
split = url.split("/");
/*
Split will resemble something like this:
["http:", "", "example.com", "Category", "Arts", "Other", "Sub", "Categories", ""]
So, you'll find the bit you're interested in at the 4th element in the array
*/
switch(split[4]){
case "Arts":
alert("I do say old chap");
break;
case "News":
alert("Anything interesting on?");
break;
default:
alert("I have no idea what page you're on :O!");
}

you can access the current url like this
document.location.href
you could do a
if ( document.location.href.indexOf("categoryYouWant")>-1){
//whatever you want
}
but you should do a regular expression
category=document.location.href.match(/example\.com\/(\w+)\//i)[1];

var url = window.location.href;
var category = url.split('Category/')[1].split('/')[0];
if (category === 'Arts') {
alert("Arts");
}else if (category === 'News') {
alert("News");
}

I made this example:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<script type="text/javascript">
function determine(url)
{
var myArray = url.split('/');
if(myArray[4] == "News")
alert(myArray[4]);
}
</script>
</head>
<body>
<div>
http://example.com/Category/News/Other/Sub/Categories/
</div>
</body>
</html>
Saludos ;)

Related

How to change an Id on another page as it loads through JavaScript

I want for the user to click a button which leads to another page. Depending on what button the user clicks, the page content should look different despite being on the same page. A simplified example is below:
Starting page html code:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
Click Here
Click Here
<script src="script.js"></script>
</body>
</html>
second-page.html code:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p id="content-id">*CONTENT SHOULD BE LOADED HERE BASED OFF BUTTON CLICKED*</p>
<script src="script.js"></script>
</body>
</html>
script.js code:
function changeContent(n) {
document.getElementById("content-id").innerHTML = n;
}
The above code does not work. I'm guessing the browser doesn't see the content-id on the first page and fails to change anything before loading the second page. Any way to reference the right id on the right page using JavaScript (no jQuery) when the new page is loaded?
Short answer: there are several approaches, the easier that comes to mind is to use localStorage if you're dealing with same origin pages
What you need is to have user information available across multiple pages. So, unlike sessionStorage, localStorage allows to store data and save it across browser sessions:
localStorage is similar to sessionStorage, except that while localStorage data has no expiration time, sessionStorage data gets cleared when the page session ends β€” that is, when the page is closed.
To use it, consider adapting your javascript of first page:
function changeContent(n) {
localStorage.setItem('optionChosen', n);
}
Then retrieve it in the second page's javascript.
var opt = localStorage.getItem('optionChosen')
var content = document.querySelector('#content-id')
if (opt == null) console.log("Option null")
if (opt === 'Option One') content.innerText = "Foo"
if (opt === 'Option Two') content.innerText = "Bar"
Edited -
Added 3 working examples that can be copy and pasted.
Problem -
Display content on a new view based on the button clicked to get to that view.
Approach -
You can store the value of ID in the browser to help identify the content that should be displayed in many ways. I will show you three working examples.
Notes -
I am over complicating this a little to show you how you might make this work since I do not know the exact circumstances you are working with. You should be able to use this logic to refactor for your requirements. You will find the following 3 solutions below.
1. Using GET Params
Uses the GET params in the URL to help you track necessary changes in your view.
2. Using Session Storage
A page session lasts as long as the browser is open, and survives over page reloads and restores.
Opening a page in a new tab or window creates a new session with the value of the top-level browsing context, which differs from how session cookies work.
Opening multiple tabs/windows with the same URL creates sessionStorage for each tab/window.
Closing a tab/window ends the session and clears objects in sessionStorage.
3. Using Local Storage
The difference between localStorage and sessionStorage is the time the data persists. LocalStorage spans multiple windows and lasts beyond the current session.
The memory capacity may change by browser.
Similar to cookies, localStorage is not permanent. The data stored within it is specific to the user and their browser.
Solutions -
Working Examples - (Copy and paste any of the below solutions into an HTML file and they will work in your browser.)
Using GET Params
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<script type="text/javascript">
let currentURL = window.location.href.split("?")[0];
function appendParams(val) {
if (val === "a") {
window.location.assign(currentURL + "?id=a");
}
if (val === "b") {
window.location.assign(currentURL + "?id=b");
}
}
</script>
<title>Working Example</title>
</head>
<body>
<button onclick="appendParams('a')">Click Here</button>
<button onclick="appendParams('b')">Click Here</button>
<p id="replace-id"></p>
</body>
</html>
<script type="text/javascript">
let url_str = window.location.href;
let url = new URL(url_str);
let search_params = url.searchParams;
let id = search_params.get("id");
document.getElementById("replace-id").id = id;
let ContentOne = "Some text if id is A";
let ContentTwo = "Some text if id is B";
if (id === "a") {
document.getElementById("a").innerHTML = ContentOne;
}
if (id === "b") {
document.getElementById("b").innerHTML = ContentTwo;
}
</script>
Using Session Storage
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<script type="text/javascript">
sessionStorage.setItem("id", "default");
function addSessionStorage(val) {
sessionStorage.setItem("id", val);
updateContent();
}
function updateContent() {
let id = sessionStorage.getItem("id");
let ContentOne = "Some text if id is A";
let ContentTwo = "Some text if id is B";
if (id === "a") {
document.getElementById("replace-content").innerHTML =
ContentOne;
}
if (id === "b") {
document.getElementById("replace-content").innerHTML =
ContentTwo;
}
}
</script>
<title>Working Example</title>
</head>
<body>
<button onclick="addSessionStorage('a')">Click Here</button>
<button onclick="addSessionStorage('b')">Click Here</button>
<p id="replace-content">Default Content</p>
</body>
</html>
Using Local Storage
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<script type="text/javascript">
localStorage.setItem("id", "default");
function addLocalStorage(val) {
localStorage.setItem("id", val);
updateContent();
}
function updateContent() {
let id = localStorage.getItem("id");
let ContentOne = "Some text if id is A";
let ContentTwo = "Some text if id is B";
if (id === "a") {
document.getElementById("replace-content").innerHTML =
ContentOne;
}
if (id === "b") {
document.getElementById("replace-content").innerHTML =
ContentTwo;
}
}
</script>
<title>Working Example</title>
</head>
<body>
<button onclick="addLocalStorage('a')">Click Here</button>
<button onclick="addLocalStorage('b')">Click Here</button>
<p id="replace-content">Default Content</p>
</body>
</html>

Internationalization of HTML pages for my Google Chrome Extension

I found a very easy way to implement translation (or localization) of my Google Chrome Extension, but that seems to apply only to .json, css and js files.
But how to localize my html content, say in the popup or an options window?
What you would do is this.
First, in your HTML use the same syntax as Chrome requires anywhere else. So your basic popup.html will be:
<!DOCTYPE html>
<html>
<head>
<title>__MSG_app_title__</title>
</head>
<body>
__MSG_link001__
<!-- Need to call our JS to do the localization -->
<script src="popup.js"></script>
</body>
</html>
Then provide the usual translation in _locales\en\messages.json:
{
"app_title": {
"message": "MyApp",
"description": "Name of the extension"
},
"link001": {
"message": "My link",
"description": "Link name for the page"
},
"prompt001": {
"message": "Click this link",
"description": "User prompt for the link"
}
}
And finally your popup.js will perform the actual localization:
function localizeHtmlPage()
{
//Localize by replacing __MSG_***__ meta tags
var objects = document.getElementsByTagName('html');
for (var j = 0; j < objects.length; j++)
{
var obj = objects[j];
var valStrH = obj.innerHTML.toString();
var valNewH = valStrH.replace(/__MSG_(\w+)__/g, function(match, v1)
{
return v1 ? chrome.i18n.getMessage(v1) : "";
});
if(valNewH != valStrH)
{
obj.innerHTML = valNewH;
}
}
}
localizeHtmlPage();
Plain an simple:
{
"exmaple_key": {
"message": "example_translation"
}
}
<sometag data-locale="example_key">fallback text</sometag>
document.querySelectorAll('[data-locale]').forEach(elem => {
elem.innerText = chrome.i18n.getMessage(elem.dataset.locale)
})
Building from ahmd0's answer. Use a data attribute to allow a hard-coded fallback.
<!DOCTYPE html>
<html>
<head>
<title data-localize="__MSG_app_title__">My Default Title</title>
</head>
<body>
Default link text
<script src="localize.js"></script>
</body>
</html>
Then provide the usual translation in _locales\en\messages.json:
{
"app_title": {
"message": "MyApp",
"description": "Name of the extension"
},
"link001": {
"message": "My link",
"description": "Link name for the page"
},
"prompt001": {
"message": "Click this link",
"description": "User prompt for the link"
}
}
And finally your localize.js will perform the actual localization:
function replace_i18n(obj, tag) {
var msg = tag.replace(/__MSG_(\w+)__/g, function(match, v1) {
return v1 ? chrome.i18n.getMessage(v1) : '';
});
if(msg != tag) obj.innerHTML = msg;
}
function localizeHtmlPage() {
// Localize using __MSG_***__ data tags
var data = document.querySelectorAll('[data-localize]');
for (var i in data) if (data.hasOwnProperty(i)) {
var obj = data[i];
var tag = obj.getAttribute('data-localize').toString();
replace_i18n(obj, tag);
}
// Localize everything else by replacing all __MSG_***__ tags
var page = document.getElementsByTagName('html');
for (var j = 0; j < page.length; j++) {
var obj = page[j];
var tag = obj.innerHTML.toString();
replace_i18n(obj, tag);
}
}
localizeHtmlPage();
The hard-coded fallback avoids the i18n tags being visible while the JavaScript does the replacements. Hard-coding seems to negate the idea of internationalisation, but until Chrome supports i18n use directly in HTML we need to use JavaScript.
As RobW noted in a comment, a feature request for adding i18n support in HTML using the same mechanism was created, but it has since then been rejected due to performance and security concerns. Therefore you can't use the same approach.
The issue mentions one possible workaround: to have separate HTML pages per language and switch between them in the manifest:
"browser_action": {
"default_popup": "__MSG_browser_action_page__"
}
But if that's not a suitable approach, the only way is to translate the page dynamically via JavaScript. You mention a solution the simplest approach, by just tagging elements to translate with ids and replacing them on page load.
You can also employ more sophisticated tools like webL10n in parallel with Chrome's approach. Note that you should probably still minimally implement Chrome's approach, so that Web Store knows that the item is supporting several languages.
Rather than parsing the full DOM, just add a class "localize" to the elements that have to be translated and add a data attribute data-localize="open_dashboard"
<div class="localize" data-localize="open_dashboard" >
Open Dashboard
</div>
JavaScript :
$('.localize').each(function(index,item){
var localizeKey = $(item).data( 'localize' );
$(item).html(chrome.i18n.getMessage(localizeKey));
});
'_locales/en/messages.json' file
{
"open_dashboard": {
"message": "Open Dashboard",
"description": "Opens the app dashboard"
}
}
A workaround to avoid replacements:
Use a simple "redirect"
It works for popups and options
In your manifest, declare the default popup
"default_popup": "popup/redirect.html"
The popup/redirect.html is almost empty. It just includes the script link to the redirect script
<!DOCTYPE html>
<html>
<head></head>
<body>
<script src="redirect.js"></script>
</body>
The popup/redirect.js file is very simple too:
var currentlang = chrome.i18n.getMessage("lang");
var popupUrl = chrome.runtime.getURL("popup/popup-"+currentlang+".html");
window.location.href = popupUrl;
Create multiple popups, already localized:
popup-fr.html
popup-en.html
Go into each of your messages.json files (in _locales) and add a "lang" message with the current language abbreviation as value: en for the english json, fr in the french json...
example for _locales/en/message.json:
"lang": {
"message": "en",
"description": "Locale language of the extension."
},
A simple workaround for very small project... definitely not a good choice for large ones. And it also works for Option pages.
One of the ways to localize your content in popup html is to fetch it from javascript onLoad. Store the strings in the _locales folder under various languages supported by you as mentioned here and do chrome.i18n.getMessage("messagename") to fetch and load the variable strings and set them using javascript/jquery onLoad function for each html element from your background.js or whatever js you load before your html pages loads.
I faced the same problem, but I solved it with a simple approach using custom data attributes.
Implement a localizing class that uses chrome.i18n and call it in the DOMContentLoaded event. In HTML, mark up the element you want to localize with the data-chrome-i18n attribute. (This attribute name is tentatively named.) Specifying the message name as the value of this attribute localizes the text content of the element. If you want to localize an attribute, specify it in the format attribute_name=message_name. Multiple specifications can be specified by separating them with ;.
const i18n = (window.browser || window.chrome || {}).i18n || { getMessage: () => undefined };
class Localizer {
constructor(options = {}) {
const { translate = Localizer.defaultTranslate, attributeName = Localizer.defaultAttributeName, parse = Localizer.defaultParse } = options;
this.translate = translate;
this.attributeName = attributeName;
this.parse = parse;
}
localizeElement(element) {
for (const [destination, name] of this.parse(element.getAttribute(this.attributeName))) {
if (!name)
continue;
const message = this.translate(name) || '';
if (!destination) {
element.textContent = message;
}
else {
element.setAttribute(destination, message);
}
}
}
localize(target = window.document) {
const nodes = target instanceof NodeList ? target : target.querySelectorAll(`[${CSS.escape(this.attributeName)}]`);
for (const node of nodes)
this.localizeElement(node);
}
}
Localizer.defaultTranslate = i18n.getMessage;
Localizer.defaultAttributeName = 'data-chrome-i18n';
Localizer.defaultParse = (value) => {
return (value || '').split(';').map(text => (text.includes('=') ? text.split('=') : ['', text]));
};
const localizer = new Localizer();
window.addEventListener('DOMContentLoaded', () => {
localizer.localize();
});
<!DOCTYPE html>
<html data-chrome-i18n="lang=##ui_locale">
<head>
<meta charset="UTF-8" />
<title data-chrome-i18n="extensionName"></title>
</head>
<body>
<p data-chrome-i18n="foo;title=bar;lang=##ui_locale"></p>
</body>
</html>
There are several things to consider to solve this problem.
Use chrome.i18n (Many people will want to aggregate in messages.json.)
Supports attributes as well as element content
Supports not only popup but also options page
Rendering performance
Security
First, the approach of switching HTML for each language in manifest.json does not work. Even if you give __MSG_*__ to the default_popup field, popup will still show the error "ERR_FILE_NOT_FOUND". I don't know why. There is no detailed reference to default_popup in the Chrome extensions Developer Guide, but MDN mentions that it is a localizable property. Similarly, if you give __MSG _*__ to the page field in options_ui, the extension itself will fail to load.
I intuitively felt that the approach of replacing __MSG_*__ in HTML and rewriting the result usinginnerHTML had performance and security problems.
This answer is cool!
And I want to make some modifications.
For chrome 93.0.4577.63 chrome.i18n.getMessage permalink, link-by-version
chrome.i18n.getMessage(messageName, substitutions, {escapeLt})
So I want to make it support
substitutions
escapeLt
Test Data
// _locales/en/messages.json
{
"hello": {
"message": "<b>Hello</b> $USER$ Welcoming $OUR_SITE$. $EMOJI$",
"description": "Greet the user",
"placeholders": {
"user": {
"content": "$1", // chrome.i18n.getMessage("hello", "variable 1")
"example": "Carson"
},
"our_site": {
"content": "Example.com"
},
"emoji": {
"content": "$2",
"example": "\uD83D\uDE42" // πŸ™‚, 😎
}
}
},
"app": {
"message": "My cool APP.",
"description": "description"
}
}
<!-- test.html-->
<script src="my-i18n.js"></script>
<p data-i18n="__MSG_hello__"></p>
<p data-i18n="__MSG_hello__<b>Carson</b>"></p>
<p data-i18n="__MSG_hello__<b>Carson</b>|0"></p>
<p data-i18n="__MSG_hello__<i>Carson</i>|1"></p>
<button title="__MSG_hello__<b>Carson</b>" data-i18n></button>
<button title="__MSG_hello__<b>Carson</b>|0" data-i18n></button>
<button title="__MSG_hello__<b>Carson</b>|1" data-i18n></button>
<p title="__MSG_app__" data-i18n="__MSG_hello__Carson,πŸ™‚"></p>
output
Script
// my-i18n.js
/**
* #param {string} msg "__MSG_Hello__para1,para2|1" or "__MSG_Hello__para1,para2|0"
* */
function convertMsgAsFuncPara(msg) {
const match = /__MSG_(?<id>\w+)__(?<para>[^|]*)?(\|(?<escapeLt>[01]{1}))?/g.exec(msg) // https://regex101.com/r/OeXezc/1/
if (match) {
let {groups: {id, para, escapeLt}} = match
para = para ?? ""
escapeLt = escapeLt ?? false
return [id, para.split(","), Boolean(Number(escapeLt))]
}
return [undefined]
}
function InitI18nNode() {
const msgNodeArray = document.querySelectorAll(`[data-i18n]`)
msgNodeArray.forEach(msgNode => {
const [id, paraArray, escapeLt] = convertMsgAsFuncPara(msgNode.getAttribute("data-i18n"))
if (id) {
msgNode.innerHTML = chrome.i18n.getMessage(id, paraArray, {escapeLt})
}
// ↓ handle attr
for (const attr of msgNode.attributes) {
const [attrName, attrValue] = [attr.nodeName, attr.nodeValue]
const [id, paraArray, escapeLt] = convertMsgAsFuncPara(attrValue)
if (!id) {
continue
}
msgNode.setAttribute(attrName, chrome.i18n.getMessage(id, paraArray, {escapeLt}))
}
})
}
(() => {
window.addEventListener("load", InitI18nNode, {once: true})
})()
Modify pseudo-category content in batches.
<div data-content="font"></div>
div::before {
content: attr(data-content);
}
document.querySelectorAll('[data-content]').forEach(el => {
el.dataset.content = chrome.i18n.getMessage(el.dataset.content);
});
Use CSS Internationalization.
<p></p>
p::before {
content: "__MSG_font__";
}
Another workaround - you can use content property in css with __MSG_myText inside.
Use Vue.js:
<html>
<head>
</head>
<body>
<div id="app">{{msgTranslated}}</div>
</body>
</html>
javascript file injected:
new Vue({
el: '#app',
data: {
msgTranslated: chrome.i18n.getMessage("message")
}
})

ajax data doesn't return correct jQuery html object

When I convert html data from my partial view to $(data) it doesn't return jQuery object I was hoping for like:
console.log($(data)) -> [#document]
Instead it returns this:
console.log($(data)) -> [#text, <meta charset=​"utf-8">​, #text, <meta name=​"viewport" content=​"width=device-width">​, #text, <title>​</title>​, #text, <link href=​"/​css?v=EY1fW53lN3Ow8WxEYembf-Ji7ogVpt21gpjVWDy8x6c1" rel=​"stylesheet">, #text, <div>​default​</div>​, #text, <div id=​"test-div">​default​</div>​, #text, <script src=​"/​jsv=k_VAvcysGBhwGVVEK9GAtPakb2vjMfaxz2PhpxPuN0I1">​</script>​, #text
]
When I do raw console log of my data it returns this:
console.log(data) ->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title></title>
<link href="/css?v=EY1fW53lN3Ow8WxEYembf-Ji7ogVpt21gpjVWDy8x6c1" rel="stylesheet"/>
</head>
<body>
<div>default</div>
<div id="test-div">default</div>
<script src="/js?v=k_VAvcysGBhwGVVEK9GAtPakb2vjMfaxz2PhpxPuN0I1"></script>
</body>
</html>
Question 1: How can I extract #test-div from this data?
Update 1
Here is my controler:
public ActionResult Index(string pageName, string categorySlug, string itemSlug)
{
if (categorySlug != null)
{
var category = _db.Categories.SingleOrDefault(x => x.Slug == categorySlug);
if (category == null) return Redirect("/");
ViewBag.Category = category;
if (itemSlug != null && category.Items.SingleOrDefault(x => x.Slug == itemSlug) != null)
{
ViewBag.ItemSlug = itemSlug;
}
else if (itemSlug != null)
{
return Redirect("/i/" + categorySlug);
}
}
ViewBag.PageName = pageName;
return View();
}
Here is my routing:
routes.MapRoute(
name: "Default",
url: "{pageName}/{categorySlug}/{itemSlug}",
defaults: new { controller = "Home", action = "Index", pageName = UrlParameter.Optional, categorySlug = UrlParameter.Optional, itemSlug = UrlParameter.Optional },
namespaces: new string[] { "ClothRent.Controllers" }
);
Index view:
#{
ViewBag.Title = "";
Layout = "~/Views/Shared/_Layout.cshtml";
string pageName = ViewBag.PageName;
ClothRent.Models.Category c = ViewBag.Category;
}
#switch (pageName)
{
case "info":
Html.RenderPartial("_Info");
break;
case "i":
Html.RenderPartial("_Category", c);
break;
default:
<div>default</div>
<div id="test-div">default</div>
break;
}
_Category partial view:
#model ClothRent.Models.Category
#{
string zveri = "zveri";
string teli = "teli";
}
#switch (Model.Slug)
{
case "zveri":
#zveri
break;
case "pasaku-teli":
#Html.Partial("_Teli", Model);
break;
}
How does it work:
I access trough url /i/category/ and it returns /home/index with appropriate partial view data, see how I acquire partial view above.
Then if user wants he will proceed browsing website, but instead of reloading page I want to do htmlPushState in witch case I will make ajax call to specific controlers and will extract data I need for the next content I will show.
Update 2
Question 2: Is there a way I render returned data from ajax call only with JavaScript, so that I wouldn't need to write an API for ajax calls exclusively?
Update 3
Thanks all for the answers I will surely in future make api for exclusive ajax calls, but for now as I don't master back-end of my website I found a solution using JS.
Posted Answer.
I solved problem thanks to Dave A by using this syntax.
$("#container").load("/i/category/ #test-div")
I think I understand. Would a load filtered by a selector for div test-div do what you want?
$("#container").load('/i/category/').('#test-div');

JavaScript to read content between <a> tags

Below is my html page:
<html>
<head>
<title>My Cat website</title>
</head>
<script type="text/javascript" src="script12.js"></script>
<body>
<h1>
My_first_cat_website
</h1>
</body>
</html>
Below is my JavaScript:
window.onload=initall;
function initall()
{
var ans=document.getElementsByTagName('a')[0].firstChild.data;
alert(ans);
if(ans<10)
{
alert(ans);
}
var newans=ans.subString(0,9)+"...";
}
Here my code is not going into if block. My requirement is if var "ans" length is above 10 then append it with ... else throw an alert directly. Can anyone help me?
Here is Solution using data property
window.onload=initall;
function initall()
{
var ans=document.getElementsByTagName('a')[0].firstChild.data;
if(ans.length<10)
{
alert("hmmm.. its less then 10!");
}
var newans= ans.substring(0,9)+"...";
document.getElementsByTagName('a')[0].firstChild.data = newans;
}
Here is it live view you wise to check example: http://jsbin.com/obeleh
I have never heard of the data property on a DOM element. Thanks to you, I learned it's a property on textNode elements (the same as nodeValue).
Also, using getElementsByTagName when the ID is available is unperformant.
subString doesn't work, it is substring. The case is important for methods as javascript is case sensitive (like most programming languages).
The other thing you're missing is an else. In your code, the var newans... will always be ran.
Here is something working:
window.onload = function() {
var ans = document.getElementById( 'message' ).textContent;
if ( ans.length < 10 ) {
alert( ans );
}
else {
var newans = ans.substring( 0, 9 ) + '...';
}
}

getElementById in 3 ways

I have an application that needs to retrieve a value out of a hidden input form field. However, this application has a base page which calls another page that is in an iFrame and then it also can call itself inside another iFrame:
default.asp -> screen.asp (in iFrame)
screen.asp -> a new instance of screen.asp (in iFrame)
document.getElementById('focusValue').value
window.frames[0].document.getElementById('focusValue').value
parent.frames[arrVal].document.getElementById('focusValue').value
When I reference the hidden input form field from default -> screen I can use the standard document.getElementById('focusValue').value;. Then when I'm in the 1st level iFrame I have to use window.frames[0].document.getElementById('focusValue').value;. Then when I'm in the 2+ levels in an iFrame I have to use the parent.frames[arrVal].document.getElementById('focusValue').value;.
A common structure that I'm starting to see is this:
if(document.getElementById('focusValue') == undefined){
window.frames[0].document.getElementById('focusValue').value = focusValue;
console.log('1');
}else if((parent.frames.length -1) == arrVal){
console.log('2');
if (arrVal > 0) {
parent.frames[arrVal].document.getElementById('focusValue').value = focusValue;
}
}else{
document.getElementById('focusValue').value = focusValue;
console.log('3');
}
Now I can certainly do this but outside of writing a novel worth of comments I'm concerned with other programmers(or me 1 month from now) looking at this code and wondering what I was doing.
My question is there a way to achieve what I'm looking to do in a standard form? I'm really hoping that there is a better way to achieve this.
I would suggest you have each page do the work of finding the value you want by calling a method. Basically exposing a lookup interface. Then you only need to call a method on the target page from the parent page. Proper naming will help developers understand what is going on and using methods will simplify the logic.
Or if you only need to get the value from the parent page, then you could register a hook with each page in an iframe using a common interface. Each page can just call that hook to get the value. This prevents your complex logic of determining what level the page is. Something like
iframe1.GetValueHook = this.GetValue;
iframe2.GetValueHook = this.GetValue;
Then each page can just call
var x = this.GetValueHook();
If you have nested pages, you could make this recursive. If you need communication between all pages then use the same approach but with a registration process. Each page registers itself (and it's children) with it's parent. But if you need to do this then you should reevaluate your architecture.
Example:
register.js
var __FRAMENAME = "Frame1";
var __FIELDID = "fieldId";
var __frames = [];
function RegisterFrame(frame) {
__frames.push(frame);
for (var i = 0; i < frame.children.length; i++) {
__frames.push(frame.children[i]);
}
RegisterWithParent();
}
function RegisterWithParent() {
var reg = {
name: __FRAMENAME,
getvalue: GetFieldValue,
children: __frames
};
if(parent != undefined && parent != this) {
parent.RegisterFrame(reg);
}
}
function SetupFrame(name, fieldId) {
__FRAMENAME = name;
__FIELDID = fieldId;
RegisterWithParent();
}
function GetFieldValue() {
return document.getElementById(__FIELDID).value;
}
function GetValueFrom(name) {
for (var i = 0; i < __frames.length; i++) {
if (__frames[i].name == name) {
return __frames[i].getvalue();
}
}
}
index.html
<html>
<head>
<script language="javascript" type="text/javascript" src="register.js"></script>
</head>
<body>
PAGE
<input type="hidden" id="hid123" value="123" />
<iframe id="frame1" src="frame1.html"></iframe>
<iframe id="frame2" src="frame2.html"></iframe>
<script type="text/javascript">
SetupFrame("Index", "hid123");
setTimeout(function () { //Only here for demonstration. Make sure the pages are registred
alert(GetValueFrom("frame3"));
}, 2000);
</script>
</body></html>
frame1.html
<html>
<head>
<script language="javascript" type="text/javascript" src="register.js"></script>
</head>
<body>
<input type="hidden" id="hid" value="eterert" />
<script type="text/javascript">
SetupFrame("frame1", "hid");
</script>
</body></html>
frame2.html
<html>
<head>
<script language="javascript" type="text/javascript" src="register.js"></script>
</head>
<body>
<input type="hidden" id="hid456" value="sdfsdf" />
<iframe id="frame2" src="frame3.html"></iframe>
<script type="text/javascript">
SetupFrame("frame2", "hid456");
</script>
</body></html>
frame3.html
<html>
<head>
<script language="javascript" type="text/javascript" src="register.js"></script>
</head>
<body>
<input type="hidden" id="hid999" value="bnmbnmbnm" />
<script type="text/javascript">
SetupFrame("frame3", "hid999");
</script>
</body></html>
This would be better if you can change it up to use a dictionary/hash tbale instead of loops.
Your best bet will be to set varables named correctly so it's self documenting. Something like this...
var screenFrame = window.frames[0];
var screenFrame2 = parent.frames[arrVal];
var value = screenFrame2.document.getElementById('focusValue').value
This will make it easier to read.
If you really must search frames for a given element, then you should just make your own function to do that and use that function everywhere. Put a lot of comments in the function explaining why/what you're doing and give the function a meaningful name so it will be more obvious to future programmers looking at your code what you are doing or where they can look to find what you are doing.
function setValueByIdFrames(name) {
if(document.getElementById(name) == undefined){
window.frames[0].document.getElementById(name).value = name;
console.log('1');
} else if((parent.frames.length -1) == arrVal){
console.log('2');
if (arrVal > 0) {
parent.frames[arrVal].document.getElementById(name).value = name;
}
} else {
document.getElementById(name).value = name;
console.log('3');
}
}

Categories