Build hyperlink using value in array in javascript - javascript

I'd like to display hyperlinks in my page template based on values in an array.
If the page contains a certain value, I'd like to insert the corresponding hyperlink.
From another question, I've managed to cobble together a script that displays a link when a value is found in a string:
<style>
#viewSchoolLink
{display: none;}
</style>
<div id="schoolName">University of Houston</div>
<div id="viewSchoolLink">View on Some Link</div>
<script type="text/javascript">
var div = document.getElementById("schoolName");
var text = div.innerHTML;
if (text.indexOf("University of Houston") >= 0) {
// if so set display to block.
document.getElementById("viewSchoolLink").style.display = "block";
}
</script>
The thing is, I have like 80-90 schools in my list. I suppose I could create 80-90 separate divs with their own unique links, but I imagine there is a much more efficient way to do it. I have an array I've set up as follows:
const schoolLink = {
"University of Houston": "https://www.somelink.com/widget=4379",
"Vanderbilt University" : "https://www.somelink.com/widget=2537",
"University of Dundee": "https://www.somelink.com/widget=2845",
}
Is there a way I can loop through the array to find a match in the key and then insert the value from the array as a hyperlink?
<script type="text/javascript">
var div = document.getElementById("schoolName");
var text = div.innerHTML;
if (text.indexOf("key in array") >= 0) {
// if so display div and use value from the array as hyperlink
}
</script>
Or, since the only part of the link URL that is unique is the widget number, could I set up my array like this:
const schoolLink = {
"University of Houston": 4379,
"Vanderbilt University" : 2537,
"University of Dundee": 2845,
}
and then just build the URL based on the value in the array:
var part = value in array
var link = ''View on Some Link ';

to insert any of the HTML using Javascript we assign in the following manner:
var div = document.getElementById("schoolName")
div.innerHTML = "<a href='https://www.somelink.com/widget=value in array'/>"
if you want to enter the dynamic data in the place of "value in array", then:
div.innerHTML = `<a href="https://www.somelink.com/widget=${variable}"/>`

Related

Dynamic JS/HTML elements appear out of order only in GAS? [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 1 year ago.
Improve this question
I have a dynamic HTML form that I generate from Google Apps Script. I expect the elements to appear in the same order that they appear in the input object, which is the same order by which I append the elements. But the elements actually appear in an unexpected order.
I followed this other post to try to sort this out but the elements still appear out of order when I run my code in GAS. The thing is, when I run my code in jsfiddle, it works as expected, i.e. the elements appear in the same order as they do in the input object. The elements just don't order as expected in GAS.
Why do the elements appear out of order in GAS but appear in order in jsfiddle? How do I resolve this in GAS using vanilla JS?
Copy of jsfiddle code:
HTML
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<div id='input_parent'></div>
</body>
<br><input type="button" value="Submit" onClick="test()">
</form>
JS
inputObj = {"first field":{"required":true,"dataType":"select","options":["first opt","second opt"]},"second field":{"required":true,"dataType":"text","options":"none"}}
// Section
section = document.getElementById("input_parent");
div = document.createElement("div");
div.setAttribute("id", "input_child");
section.appendChild(div);
var fields = Object.keys(inputObj);
Array.from(fields).forEach((arg) => {
// Label
section = document.getElementById("input_parent");
label = document.createElement("label");
label.setAttribute("id", "label_"+arg);
label.setAttribute("for", arg);
label_txt = document.createTextNode(arg+":");
label.appendChild(label_txt);
section.appendChild(label);
if (inputObj[arg].dataType == "select") {
// Create select element
section = document.getElementById("input_parent");
const select_element = document.createElement("select");
select_element.setAttribute("id", "select_"+arg);
section.appendChild(select_element);
var options = inputObj[arg].options
for(let o = 0; o < options.length; o++)
{
var element = document.getElementById("select_"+arg);
const option = document.createElement("option");
var text = document.createTextNode(arg+":");
option.textContent = options[o];
option.setAttribute("value", options[o]);
element.appendChild(option);
};
} else {
section = document.getElementById("input_parent");
input_field = document.createElement("input");
input_field.setAttribute("id", "input_"+arg);
input_field.setAttribute("type", inputObj[arg].dataType);
section.appendChild(input_field);
}
});
Additional info in response to #Nikko J. The following code should reproduce the results in the images below.
dynamHtmlTbrlsht.html to render form.
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<div id='input_parent'></div>
</body>
<br><input type="button" value="Submit" onClick="test()">
</html>
<script type='text/javascript'>
window.onload = function(){
google.script.run.withSuccessHandler(addElements).getElementInfo();
}
</script>
<script type='text/javascript'>
function addElements(inputObj) {
// Section
section = document.getElementById("input_parent");
div = document.createElement("div");
div.setAttribute("id", "input_child");
section.appendChild(div);
var fields = Object.keys(inputObj);
Array.from(fields).forEach((arg) => {
// Label
section = document.getElementById("input_parent");
label = document.createElement("label");
label.setAttribute("id", "label_"+arg);
label.setAttribute("for", arg);
label_txt = document.createTextNode(arg+":");
label.appendChild(label_txt);
section.appendChild(label);
if (inputObj[arg].dataType == "select") {
// Create select element
section = document.getElementById("input_parent");
const select_element = document.createElement("select");
select_element.setAttribute("id", "select_"+arg);
section.appendChild(select_element);
var options = inputObj[arg].options
for(let o = 0; o < options.length; o++)
{
var element = document.getElementById("select_"+arg);
const option = document.createElement("option");
var text = document.createTextNode(arg+":");
option.textContent = options[o];
option.setAttribute("value", options[o]);
element.appendChild(option);
};
} else {
section = document.getElementById("input_parent");
input_field = document.createElement("input");
input_field.setAttribute("id", "input_"+arg);
input_field.setAttribute("type", inputObj[arg].dataType);
section.appendChild(input_field);
}
});
}
</script>
trblsht.gs to create input object. (Note that this is simplified for resolving the problem at hand. In reality, inputObj is generated by running a few functions that dynamically create the object and fetch options from an external source.)
function getElementInfo() {
var inputObj = {"first_fild":{"required":true,"dataType":"select","options":["option 1","option 2","option 3","option 4"]},"second_field":{"required":true,"dataType":"text","options":"none"},"third_field":{"required":true,"dataType":"text","options":"none"},"fourth_field":{"required":true,"dataType":"text","options":"none"},"fifth_field":{"required":false,"dataType":"select","options":["option 1","option 2","option 3","option 4","option 5","option 6","option 7","option 8","option 9","option10"]},"sixth_field":{"required":false,"options":"none"},"seventh_field":{"required":false,"dataType":"select","options":["option 1","option 2","option 3","option 4","option 5","option 6"]}}
Logger.log("inputObj: "+JSON.stringify(inputObj))
return inputObj;
}
For completeness, the following lines are in an onEdit function that generates the form when the active cell == "troubleshoot".
function myOnEditTriggerFunc() {
// do other stuff
var currentRange = SpreadsheetApp.getActiveRange();
var currentVal =
currentRange.getValue().toString().replace(/(^\s+|\s+$)/g,"");
if (currentVal == "troubleshoot") {
openHTML("dynamHtmlTbrlsht","Troubleshoot",400)
return;
}
}
openHTML() referenced in above function.
function openHTML(htmlFile,htmlTitle,height) {
var html = HtmlService.createHtmlOutputFromFile(htmlFile)
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setHeight(height);
SpreadsheetApp.getUi()
.showModalDialog(html, htmlTitle);
return;
};
Output form showing unexpected order of elements:
Output log showing expected order of elements:
The jsfiddle shows the normal js.
I started to wonder if the issue is with Array.from(fields).forEach((arg) in dynamHtmlTbrlsht.html. I intentionally used Array instead of Object since Array is ordered whereas Object is not always ordered (depends on ES). Maybe there's something related to V8 runtime that's affecting this and I'm not picking up on?
When I saw your script, I thought that the reason for your issue is due to that the data is a JSON object. When the document about JSON is seen, it says as follows. Ref
In JSON, they take on these forms:
An object is an unordered set of name/value pairs. An object begins with {left brace and ends with }right brace. Each name is followed by :colon and the name/value pairs are separated by ,comma.
And, in your script, the values are retrieved from the JSON object using var fields = Object.keys(inputObj). In order to confirm this using your script, when console.log(Object.keys(inputObj)) is put after the line of function addElements(inputObj) { at Javascript side and before the line of return inputObj; at Google Apps Script side, each log shows as follows.
For Javascript side, it's ["fourth_field", "seventh_field", "second_field", "sixth_field", "third_field", "fifth_field", "first_fild"].
For Google Apps Script side, it's ["first_fild","second_field","third_field","fourth_field","fifth_field","sixth_field","seventh_field"].
It is found that the order of keys is different between the Javascript side and Google Apps script side. I thought that this might be the reason for your issue.
If you want to use the order of ["first_fild","second_field","third_field","fourth_field","fifth_field","sixth_field","seventh_field"], how about the following modification?
Modified script 1:
In this pattern, the keys are set as an array in order.
Google Apps Script side:
From:
return inputObj;
To:
return [inputObj, ["first_fild","second_field","third_field","fourth_field","fifth_field","sixth_field","seventh_field"]];
Javascript side:
From:
function addElements(inputObj) {
// Section
section = document.getElementById("input_parent");
div = document.createElement("div");
div.setAttribute("id", "input_child");
section.appendChild(div);
var fields = Object.keys(inputObj);
To:
function addElements([inputObj, fields]) {
// Section
section = document.getElementById("input_parent");
div = document.createElement("div");
div.setAttribute("id", "input_child");
section.appendChild(div);
// var fields = Object.keys(inputObj);
Modified script 2:
In this pattern, the keys are set as Object.keys(inputObj) in order. By this, the same order can be used between the Google Apps Script side and the Javascript side.
Google Apps Script side:
From:
return inputObj;
To:
return [inputObj, Object.keys(inputObj)];
In this pattern, the modification of the Javascript side is the same as pattern 1.

Passing a 2D array from one JavaScript function to another

I trying to collect information from the user based on a 2D array. I have problems passing on the 2D array from one function in Javascript to another function, as an array and not as text. What could I be doing wrong?
The array is this
Title Category1 Category2 Category3
Category1 Apples Blue Eyes
Category2 Oranges Green Legs
Category3 Pairs Yellow Arms
Mango Gray
The idea is to display Column1 first by having the "Title" as text and the Category list by buttons. On click of a button it should display the contents of the respected column. If, therefore, the user clicks on Category3, he should see Category3 as text and the options "Eyes", "Legs", "Arms" as buttons.
My problem is passing on the onclick the correct parameters as nextStep(this,database) is not working. When I pass on nextStep(this,\''+database+'\') of course it passes on the "database" as text and not as an array.
Any suggestions?
My HTML is a simple div.
<div id="mainPage"></div>
My Javascript starts on load...
window.addEventListener("load", starting, false);
function starting(){
var database = [[Title, Category1, Category2, Category3], [Category1,Apples,Blue,Eyes],[Category2,Oranges,Green,Legs],[Category3,Pairs,Yellow,Arms],[,Mango,Gray,]];
renderCategories(database,1);
}
function renderCategories(database, pos){
mainPage = document.getElementById("mainPage");
var catTitles = database[0];
var catTitle = catTitles[pos];
var allmainPage = database[2];
mainPage.innerHTML = '<div class="input-field col s12 m12"><h3>'+catTitle+'</h3></div>';
for(var i=0; i<allmainPage.length; i++){
var categoryListValue = allmainPage[i][pos];
if (categoryListValue !=""){
mainPage.innerHTML += '<div class="row"><button onclick="nextStep(this,database)" value="'+categoryListValue+'">'+categoryListValue+'</button></div>';
}
}
}
function nextStep(selection, database){
var selectedCat = selection.value;
alert(selectedCat);
var catTitles = database[0];
alert(catTitles);
}
window.addEventListener("load", starting, false);
I suggest you pass the database using the JSON.stringify() function, and then you can convert it back using the JSON.parse(), like this:
for(var i=0; i<allmainPage.length; i++){
let categoryListValue = allmainPage[i][pos],
databaseString = JSON.stringify(database);
if (categoryListValue !="")
mainPage.innerHTML += '<div class="row"><button onclick="nextStep(this,' + databaseString + ')" value="'+categoryListValue+'">'+categoryListValue+'</button></div>';
}
And then:
function nextStep(databaseString) {
let database = JSON.parse(databaseString)
}
Although I don't think this is the best way of doing it, it should fix your problem.
If you want my opinion, I think you should store the database in your localstorage and acess it using 'keys' as parameters (like database01) and such.

How do I copy specific text from one area to another?

I am trying to copy a specific part of a sentence from a div to another and input it to another area using JavaScript.
<div id="copyfromhere">This is +how+ it works.</div>
<div id="pastehere"></div>
I'd like to copy the part in between the + symbols. The + symbols are included in the original sentence.
you can get the text from div and split it based on '+', which will return the array, then you can access that arrays 2nd value indexed at 1.
var pastehereDiv = document.querySelector("#pastehere");
var copyfromDiv = document.querySelector("#copyfromhere")
pastehereDiv.textContent = copyfromDiv.textContent.split('+')[1];
var copy = function() {
// grab the origin and destination divs
var origin = document.getElementById("copyfromhere");
var destination = document.getElementById("pastehere");
// get the text from the origin, split on "+" and then get the second element in the array
var text = from.innerText.split("+")[1];
// apply that text to the destination div
destination.innerText = text;
}
<div id="copyfromhere">This is +how+ it works.</div>
<div id="pastehere"></div>
<button id="copy" onClick="copy()">Copy</button>
Obviously this will only work if there aren't any other +s in your origin div.

Why does appending my dynamic textarea to a div result in an empty textarea?

I'm simply trying to edit a div named meta description inside of a div named post by taking the html and creating a textarea with it then plugging it back in. I have it working for the Title, content, and even meta keywords, but for some reason the description's textarea just keeps coming up empty in the end.
//---------------edit meta Description------------
var Description = metaDescription;
Description.show();
//save the html within the div
var ogDescription = "None";
if (metaDescription.html()) {
ogDescription = $(Description).html().trim();
}
console.log('ogDescription = ' + ogDescription);
// create a dynamic textarea
var editDescription = $("<textarea />");
editDescription.val(ogDescription);
console.log('editDescript val after adding === ' + editDescription.val());
console.log('editDescript html after adding === ' + editDescription.html());
editDescription.attr('class', 'editDescription')
.css('height', metaHeight)
.css('width', post.css('width'));
// add the textarea
Description.html("<p>meta Description:</p>");
$(Description).append(editDescription);
//--end edit meta Description
Output
Title = Newly Added Post
Posts.js:79 Content = Testing 1, 2, 3
Posts.js:105 ogDescription = Testing 1,2 and u know 3
Posts.js:110 editDescript val after adding === Testing 1,2 and u know 3
Posts.js:111 editDescript html after adding ===
Posts.js:129 Keywords = none, for, now
Description html after === <p>meta Description:</p><textarea class="editDescription" style="height: 80px; width: 262px;"></textarea>
Textarea doesn't have a value attribute. Unlike input, the "value" of textarea is the content between the beginning and end tags. You will need to set the innerHtml instead using .html("blah") or .text("blah")
See Set value of textarea in jQuery
Try to create the textarea as follows
var editDescription = $("<textarea>" + ogDescription + "</textarea>");

How to wrap a class around the first half of a headline?

I'm trying to wrap a class around the first or the second half of a headline so that I could create more dynamic and cool headlines with jQuery.
In theory I want to find all of the spaces in the sentence and divide it into two. If the headline contains an uneven number of words the script should detect that and also add the class to the nearest word.
This is an interesting problem. I would approach with the handy javascript splice method. Splice can be used to insert and delete items of an array. I'd recommend opening up an inspector and trying out some of the examples I've written below.
First we'll use jQuery to select the header then manipulate the html content string. I'm assuming that the specific header you want to manipulate will have a class and I've substituted 'dynamic':
var header = $("h1.dynamic").text();
=> "Header with some other stuff"
var header_as_array = header.split(" ")
=> ["Header", "with", "some", "other", "stuff"]
var first_half = header_as_array.splice(0, header_as_array.length/2)
Keep in mind that splice changes the original array, so at this point:
first_half = ["Header", "with"]
header_as_array = ["some", "other", "stuff"]
Now, you can join them back together and wrap them with spans like so:
var first = '<span class="first_half">'+first_half.join(" ")+'</span>';
var second = '<span class="second_half">'+header_as_array.join(" ")+'</span>';
var finished = first+" "+second;
Finally, we'll put our finished string back into the header with jQuery:
$("h1.dynamic").html(finished);
The way I've written it a header with an odd number of words will always have the second half as the longer half. If you would prefer it the other way around you could do this:
var splice_location = Math.ceil(test_as_array.length/2);
var first_half = header_as_array.splice(0, splice_location);
By default a non-integer value will be truncated, but here we are using the ceiling function to round things up instead of down.
Nice one #lashleigh. You can have a look at a working example here:
http://jsfiddle.net/johnhunter/KRJdm/
#Tony, I've implemented what you are after as a jquery plugin. You call it on the header you want formatted:
$(function() {
$('h1').splitWords();
});
...and it will produce html output like this:
Before:
<h1>This is a long headline</h1>
After:
<h1>
<span class="wrap-1">This is </span>
<span class="wrap-2">a long headline </span>
</h1>
UPDATED:
Not part of the original question but I have updated the example to allow you to specify at which word the wrapping occurs. If you provide an index argument it will use that offset on the list of words (minus values count back from the end). e.g:
$('h1.a').splitWords(); // Split these words equally
$('h1.b').splitWords(1); // Split these after the first word
$('h1.c').splitWords(-2); // Split these after the second last word
http://jsfiddle.net/johnhunter/KRJdm/
Try the following
<html>
<head>
<title>Test</title>
<script type="text/javascript" src="jquery-1.5.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
var headlineText = $('h1.mainHeader').text();
var headlineWords = headlineText.split(' ');
var headlineLength = headlineWords.length;
var headlineMidPoint = (headlineLength / 2) + 1;
var headlineFirstHalfText = headlineWords.slice(0, headlineMidPoint).join(' ') + ' ';
var headlineSecondHalfText = headlineWords.slice(headlineMidPoint).join(' ');
var d = document;
var headlineFirstHalf = $(d.createElement('span')).addClass('headlineHead').text(headlineFirstHalfText);
var headlineSecondHalf = $(d.createElement('span')).addClass('headlineTail').text(headlineSecondHalfText);
var headline = $(d.createElement('h1')).addClass('mainHeader').append(headlineFirstHalf).append(headlineSecondHalf);
$('h1.mainheader').replaceWith(headline);
});
</script>
<style type="text/css">
h1 { font-size:18px;}
span.headlineTail {font-size:1.2em;}
</style>
</head>
<body>
<h1 class="mainHeader">This is a dynamic headline</h1>
<p>Lorem ipsum dolor sit amet, consectetur...</p>
</body>
</html>
lashleigh's answer is great, however, I would challenge the premise that jQuery is the best choice of technology for achieving this effect. I would be inclined to suggest doing the same server-side. Phrasing the markup using PHP, Python or whatever language you are using and then caching the output with the inserted class. Saves on page weight and means the user's browser doesn't have to calculate everything when each a page is loaded. A significant benefit on light clients such as mobile devices.
Here is an example in PHP.
<?php
$headline = "This is a headline of epic proportions";
$split = explode(' ', $headline);
$a = array_slice($split, 0, (count($split)/2));
$b = array_slice($split, (count($split)/2));
$headline = '<span class="whatever">'. join(' ', $a) . '</span>' . join(' ', $b);
print $headline;
?>

Categories

Resources