An external source is providing the content of the script tags written as HTML in a string.
I need to add these script tags into the head.
If I do it like this, all script tags are added to the DOM in head tag, however none of the sources are actually being loaded (devtools network tab).
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<script>
let sLoadThis = '<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/underscore#latest/underscore-umd-min.js">'
sLoadThis = sLoadThis + "<" + "/script>"
let oScript = $(sLoadThis).get(0);
document.head.appendChild(oScript);
</script>
</body>
</html>
If I sort of use the jQuery as an interpreter to then insert the script tag with vanilla JS, it works:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<script>
let sLoadThis = '<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/underscore#latest/underscore-umd-min.js">'
sLoadThis = sLoadThis + "<" + "/script>"
let oScript = $(sLoadThis).get(0);
var vanillaScript = document.createElement('script')
vanillaScript .type = 'text/javascript'
vanillaScript .src = oScript.src
document.head.appendChild(vanillaScript )
</script>
</body>
</html>
I would like to understand why it doesn't work in my first example.
The second example runs because you 're using a dom node. That 's how appendChild() works. The jQuery get() method works with existing dom elements. The only thing you 've got is a string, that does not exist in your dom yet. Try the following.
let script = $.parseHtml(sLoadThis);
console.log(script.get(0)); // should be a script dom node element
document.head.appendChild(script.get(0));
In vanilla JavaScript its even easier and much more faster than jQuery because you don 't have to use a dependency.
const fragment = document.createRange().createContextualFragment(htmlStr);
document.head.appendChild(fragment);
As far as I know there are 4 ways to create a DOM element from a string:
OPTION 1: innerHTML
OPTION 2: insertAdjacentHTML
OPTION 3: DOMParser().parseFromString
OPTION 4: createRange().createContextualFragment
HTML5 specifies that a <script> tag inserted with one of the 3 first options should not execute because it could became a security risk. See this
So, in the example, although all script tags are added to the DOM in the <head> section, only the option number 4 will be executed.
const str1 = "<script>alert('option 1 executed')" + "<" + "/script>"
const str2 = "<script>alert('option 2 executed')" + "<" + "/script>"
const str3 = "<script>alert('option 3 executed')" + "<" + "/script>"
const str4 = "<script>alert('option 4 executed')" + "<" + "/script>"
// OPTION 1
const placeholder1 = document.createElement('div')
placeholder1.innerHTML = str1
const node1 = placeholder1.firstChild
document.head.appendChild(node1)
// OPTION 2
const placeholder2 = document.createElement('div')
placeholder2.insertAdjacentHTML('afterbegin', str2)
const node2 = placeholder2.firstChild
document.head.appendChild(node2)
// OPTION 3
const node3 = new DOMParser().parseFromString(str3, 'text/html').head.firstElementChild
document.head.appendChild(node3)
// OPTION 4
const node4 = document.createRange().createContextualFragment(str4)
document.head.appendChild(node4)
Okay, Stumped. I just use code to show possibilities to customers (am self-employed).
Trying to show get-selected-text-from-IE11-Browser. Don't need cross-Browser and expert stuff just yet (if customer goes ahead).
Have tried to write a javascript function to get the selected text from the browser. This has worked fine when called direct (put function into Console via F12 facility) and this returns the selection. But when calling from Context Menu HTM script it fails with the mentioned error. The code to get the selected text comes from another context menu script that works fine when all the code is in the one HTM script (cmGoogleMapSelection_1.htm). I was just trying to be a little more efficient with reuse and learn a little more myself. I will return to in-line code if I can't resolve the issue (with help from your marvellous selves).
Keep getting the following error reported in the HTM script :
The value of the property 'myGetSelectedText' is null or undefined, not a Function object.
Have read a number of posts and tried to ensure that I have covered their suggestions. Still stumped, any help appreciated.
The code, first the 'function', then the 'script'; both script file and function file are in the same local file folder (please excuse the Debug code - gulp):
fn_myGetSelectedText.js:
function myGetSelectedText(pDefault) {
var zDbug = 1;
var zDbugMsg = "Debug: ";
var zSelection = "";
if (zDbug) {alert(zDbugMsg + "Starting Function 'myGetSelectedText' from fn_myGetSelectedText.js");}
zSelection = "" + window.getSelection().toString();
if (zDbug) {alert(zDbugMsg + " Selection= '" + zSelection + "'");}
if (zSelection == "") {
zSelection = pDefault;
alert(zDbugMsg + "Null selection, using: " + zDefault + " !");
}
return zSelection;
}//EndOf: Function -----
cmGoogleMapSelection_2.htm:
<!-- saved from url=(0016)http://localhost -->
<script type="text/javascript" src="fn_myGetSelectedText.js"></script>
<script type="text/javascript">
//- zDbug: 0 = false = no messages; 1 = true = show messages -----
var zDbug = 1;
var zDbugMsg = "Debug: ";
if (zDbug) {alert(zDbugMsg + "Starting cmGoogleMap_Selection2.htm V14");}
//- Google Maps stem URL & default location -----
var zMaps = "http://maps.google.co.uk/maps?hl=en&q=";
var zDefault = "+London";
var zSelection = myGetSelectedText(zDefault); //- Error occurs here <<<<<<<<<<<
if (zDbug) {alert(zDbugMsg + " Selection= '" + zSelection + "'");}
//- Build Maps URL -----
var zGo = zMaps + zSelection;
//- Open new Maps window -----
if (zDbug) {alert(zDbugMsg + "Issuing Window.Open on URL: " + zGo);}
window.open(zGo, "_blank");
//- Close this window -----
window.close()
</script>
<!-- Just to put something into the main code window so I know which one it is -->
<style>
p {font-family: "Lucida Console"; color: Red; font-size: 16pt;}
</style>
<p> >>-- Map Selected Text Function --<< <br>
>>-- . . 'myGetSelectedText' . . --<< </p>
I am hoping like heck that I haven't missed a bracket somewhere - embarrassing!
Other stuff: Windows 10 Pro (fully updated); 64 bit IE11; just javascript; Compatibility View OFF; Registry Keys/Values pointing where they should (cloned from working version).
While browser downloads fn_myGetSelectedText.js file from the internet, it does not stop parsing other code in your HTML. There's concurrent downloading of assets going on while browser parses the DOM.
when browser reaches this line var zSelection = myGetSelectedText(); it does not see myGetSelectedText defined on the window object at that moment thus throws out error.
What you want to do is wrap your script/code in your HTML into DOMContentLoaded event and call it once page load completes.
<script>
document.addEventListener("DOMContentLoaded", function(event) {
console.log("DOM fully loaded and parsed");
});
</script>
reference: https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded
My guess would be that in your called function, pDefault is undefined, because you aren't passing a variable when you do the call.
So, this line: var zSelection = myGetSelectedText();
Should become: var zSelection = myGetSelectedText(zDefault);
HTH,
Jim
I am currently doing this:
<div id="textChange" style="display:none;">Blah blah</div>
<script type="text/javascript">
var d = new Date();
var funnyDate = (d.getFullYear() + "" + (d.getMonth()+11) + "" + (d.getDate()+10));
if((funnyDate>=20131916) && (funnyDate<=20131923))
{
document.getElementById("textChange").style.display ="block";
}
</script>
and would like to move the script to an external JS file. How do I do that? I doesn't seem to be working for me.
Thanks.
Include this script after your #textChange div and it will work. For example before closing </body> tag:
...
<script src="funny-script.js" type="text/javascript"></script>
</body>
This is the simplest method. You could also run this code on DOMContentLoaded or window.onload events, but looking at what your script doing I don't think it makes sence.
1-open notepad or notepad ++ or whatever you use as a text editor.
2-copy the javascript code to the text editor without and tags
var d = new Date();
var funnyDate = (d.getFullYear() + "" + (d.getMonth()+11) + "" + (d.getDate()+10));
if((funnyDate>=20131916) && (funnyDate<=20131923))
{
document.getElementById("textChange").style.display ="block";
}
3-save the files with any name you want and don't forget to add the .js extension to the file for example save the file as "test.js"
4-copy the "test.js" to the same directory as html page.
5-add this line to the html page
<script type="text/javascript" language="javascript" src="test.js"></script>
One way to do this is to create a function and include this in a js file
function style_changer(){
var d = new Date();
var funnyDate = (d.getFullYear() + "" + (d.getMonth()+11) + "" + (d.getDate()+10));
if((funnyDate>=20131916) && (funnyDate<=20131923))
{
document.getElementById("textChange").style.display ="block";
}
}
Now in your html give reference to the js file containing this function for example
<script type="text/javascript" src="yourscriptfilename.js" />
you can include this in your section and should work
Save the a file called script.js with the contents.
var d = new Date();
var funnyDate = (d.getFullYear() + "" + (d.getMonth()+11) + "" + (d.getDate()+10));
if((funnyDate>=20131916) && (funnyDate<=20131923))
{
document.getElementById("textChange").style.display ="block";
}
And place this tag inside your HTML document. Place it just before the </body> so you'll know that the element textChange will exist in the DOM before your script is loaded and executed.
<script type="text/javascript" src="script.js" />
Make sure that script.js is in the same directory as your HTML document.
put this below code in a function
step1:
function onLoadCall()
var d = new Date();
var funnyDate = (d.getFullYear() + "" + (d.getMonth()+11) + "" + (d.getDate()+10));
if((funnyDate>=20131916) && (funnyDate<=20131923))
{
document.getElementById("textChange").style.display ="block";
}
}
Step2:-
call that function on page load
<body onload='onLoadCall()'>
...
</body>
step3:-
now move the script to another file it will work
Put script in a separate file and name it yourScript.js and finally include it in your file
add the code within the script file
function changeFunnyDate(){
var d = new Date();
var funnyDate = (d.getFullYear() + "" + (d.getMonth()+11) + "" + (d.getDate()+10));
if((funnyDate>=20131916) && (funnyDate<=20131923))
{
document.getElementById("textChange").style.display ="block";
}
}
Finally add the script in your file & call the method
<script src="yourScript.js" type="text/javascript"></script>
Take everything between your script tags and put it in another file. You should save this file with a .js file extension. Let's pretend you save it as textChange.js.
Now the simplest thing to do would be to include the script file just after your <div> tag -- so basically where the <script> tags and code were before, write:
<script type="text/javascript" src="textChange.js"></script>
This assumes that 'textChange.js' is in the same folder as your HTML file.
...
However, that would far too easy! It is generally best practice to place <script> tags in the <head> of your HTML file. You can move the line above up into the head but then the script will load before your <div> does--it will try to do what it does and it will fail because it can't find the div. So you need to put something around the code in your script file so that it only executes when the document is ready.
The simplest way to do this (and there may be better ways) is write the following...
window.onload = function () {
var d = new Date();
var funnyDate = (d.getFullYear() + "" + (d.getMonth()+11) + "" + (d.getDate()+10));
if ((funnyDate>=20131916) && (funnyDate<=20131923))
{
document.getElementById("textChange").style.display ="block";
}
}
This will mean your script is in the head where it should be and that it only performs when your whole page is ready, including the div that you want to act on.
Hope this helps.
I'm trying to create and manipulate the Pin It button after page load. When i change the button properties with js, it should be rerendered to get the functionality of pinning dynamically loaded images. So, does Pinterest have any method like Facebook's B.XFBML.parse() function?
Thanks...
Just add data-pin-build attribute to the SCRIPT tag:
<script defer
src="//assets.pinterest.com/js/pinit.js"
data-pin-build="parsePinBtns"></script>
That causes pinit.js to expose its internal build function to the global window object as parsePinBtns function.
Then, you can use it to parse links in the implicit element or all of the links on the page:
// parse the whole page
window.parsePinBtns();
// parse links in #pin-it-buttons element only
window.parsePinBtns(document.getElementById('pin-it-buttons'));
Hint: to show zero count just add data-pin-zero="1" to SCRIPT tag.
The best way to do this:
Remove the iframe of the Pin It button you want to manipulate
Append the html for the new button manipulating it as you wish
Realod their script - i.e. using jQuery:
$.ajax({ url: 'http://assets.pinterest.com/js/pinit.js', dataType: 'script', cache:true});
To render a pin-it button after a page has loaded you can use:
<a href="..pin it link.." id="mybutton" class="pin-it-button" count-layout="none">
<img border="0" src="//assets.pinterest.com/images/PinExt.png" width="43" height="21" title="Pin It" />
</a>
<script>
var element = document.getElementById('mybutton');
(function(x){ for (var n in x) if (n.indexOf('PIN_')==0) return x[n]; return null; })(window).f.render.buttonPin(element);
</script>
Assuming of course the assets.pinterest.com/js/pinit.js is already loaded on the page. The render object has some other useful methods like buttonBookmark, buttonFollow, ebmedBoard, embedPin, embedUser.
I built on Derrek's solution (and fixed undeclared variable issue) to make it possible to dynamically load the pinterest button, so it can't possibly slow down load times. Only tangentially related to the original question but I thought I'd share anyway.
at end of document:
<script type="text/javascript">
addPinterestButton = function (url, media, description) {
var js, href, html, pinJs;
pinJs = '//assets.pinterest.com/js/pinit.js';
//url = escape(url);
url = encodeURIComponent(url);
media = encodeURIComponent(media);
description = encodeURIComponent(description);
href = 'http://pinterest.com/pin/create/button/?url=' + url + '&media=' + media + '&description=' + description;
html = '<img border="0" src="http://assets.pinterest.com/images/PinExt.png" title="Pin It" />';
$('#pinterestOption').html(html);
//add pinterest js
js = document.createElement('script');
js.src = pinJs;
js.type = 'text/javascript';
document.body.appendChild(js);
}
</script>
in document ready function:
addPinterestButton('pageURL', 'img', 'description');//replace with actual data
in your document where you want the pinterest button to appear, just add an element with the id pinterestOption, i.e.
<div id="pinterestOption"></div>
hope that helps someone!
Here's what I did.
First I looked at pinit.js, and determined that it replaces specially-marked anchor tags with IFRAMEs. I figured that I could write javascript logic to get the hostname used by the src attribute on the generated iframes.
So, I inserted markup according to the normal recommendations by pinterest, but I put the anchor tag into an invisible div.
<div id='dummy' style='display:none;'>
<a href="http://pinterest.com/pin/create/button/?
url=http%3A%2F%2Fpage%2Furl
&media=http%3A%2F%2Fimage%2Furl"
class="pin-it-button" count-layout="horizontal"></a>
</div>
<script type="text/javascript" src="//assets.pinterest.com/js/pinit.js">
</script>
Then, immediately after that, I inserted a script to slurp up the hostname for the pinterest CDN, from the injected iframe.
//
// pint-reverse.js
//
// logic to reverse-engineer pinterest buttons.
//
// The standard javascript module from pinterest replaces links to
// http://pinterest.com/create/button with links to some odd-looking
// url based at cloudfront.net. It also normalizes the URLs.
//
// Not sure why they went through all the trouble. It does not work for
// a dynamic page where new links get inserted. The pint.js code
// assumes a static page, and is designed to run "once" at page creation
// time.
//
// This module spelunks the changes made by that script and
// attempts to replicate it for dynamically-generated buttons.
//
pinterestOptions = {};
(function(obj){
function spelunkPinterestIframe() {
var iframes = document.getElementsByTagName('iframe'),
k = [], iframe, i, L1 = iframes.length, src, split, L2;
for (i=0; i<L1; i++) {
k.push(iframes[i]);
}
do {
iframe = k.pop();
src = iframe.attributes.getNamedItem('src');
if (src !== null) {
split = src.value.split('/');
L2 = split.length;
obj.host = split[L2 - 2];
obj.script = split[L2 - 1].split('?')[0];
//iframe.parentNode.removeChild(iframe);
}
} while (k.length>0);
}
spelunkPinterestIframe();
}(pinterestOptions));
Then,
function getPinMarkup(photoName, description) {
var loc = document.location,
pathParts = loc.pathname.split('/'),
pageUri = loc.protocol + '//' + loc.hostname + loc.pathname,
href = '/' + pathToImages + photoName,
basePath = (pathParts.length == 3)?'/'+pathParts[1]:'',
mediaUri = loc.protocol+'//'+loc.hostname+basePath+href,
pinMarkup;
description = description || null;
pinMarkup = '<iframe class="pin-it-button" ' + 'scrolling="no" ' +
'src="//' + pinterestOptions.host + '/' + pinterestOptions.script +
'?url=' + encodeURIComponent(pageUri) +
'&media=' + encodeURIComponent(mediaUri);
if (description === null) {
description = 'Insert standard description here';
}
else {
description = 'My site - ' + description;
}
pinMarkup += '&description=' + encodeURIComponent(description);
pinMarkup += '&title=' + encodeURIComponent("Pin this " + tagType);
pinMarkup += '&layout=horizontal&count=1">';
pinMarkup += '</iframe>';
return pinMarkup;
}
And then use it from jQuery like this:
var pinMarkup = getPinMarkup("snap1.jpg", "Something clever here");
$('#pagePin').empty(); // a div...
$('#pagePin').append(pinMarkup);
I rewrote the Pinterest button code to support the parsing of Pinterest tags after loading AJAX content, similar to FB.XFBML.parse() or gapi.plusone.go(). As a bonus, an alternate JavaScript file in the project supports an HTML5-valid syntax.
Check out the PinterestPlus project at GitHub.
The official way to do this is by setting the "data-pin-build" attribute when loading the script:
<script defer="defer" src="//assets.pinterest.com/js/pinit.js" data-pin-build="parsePins"></script>
Then you can render your buttons dynamically like so:
// render buttons inside a scoped DOM element
window.parsePins(buttonDomElement);
// render the whole page
window.parsePins();
There is also another method on this site which lets you render them in JavaScript without the script tag.
Here is what i did.. A slight modification on #Derrick Grigg to make it work on multiple pinterest buttons on the page after an AJAX reload.
refreshPinterestButton = function () {
var url, media, description, pinJs, href, html, newJS, js;
var pin_url;
var pin_buttons = $('div.pin-it a');
pin_buttons.each(function( index ) {
pin_url = index.attr('href');
url = escape(getUrlVars(pin_URL)["url"]);
media = escape(getUrlVars(pin_URL)["media"]);
description = escape(getUrlVars(pin_URL)["description"]);
href = 'http://pinterest.com/pin/create/button/?url=' + url + '&media=' + media + '&description=' + description;
html = '<img border="0" src="http://assets.pinterest.com/images/PinExt.png" title="Pin It" />';
index.parent().html(html);
});
//remove and add pinterest js
pinJs = '//assets.pinterest.com/js/pinit.js';
js = $('script[src*="assets.pinterest.com/js/pinit.js"]');
js.remove();
js = document.createElement('script');
js.src = pinJs;
js.type = 'text/javascript';
document.body.appendChild(js);
}
});
function getUrlVars(pin_URL)
{
var vars = [], hash;
var hashes = pin_URL.slice(pin_URL.indexOf('?') + 1).split('&');
for(var i = 0; i < hashes.length; i++)
{
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
}
Try reading this post http://dgrigg.com/blog/2012/04/04/dynamic-pinterest-button/ it uses a little javascript to replace the pinterest iframe with a new button and then reloads the pinit.js file. Below is the javascript to do the trick
refreshPinterestButton = function (url, media, description) {
var js, href, html, pinJs;
url = escape(url);
media = escape(media);
description = escape(description);
href = 'http://pinterest.com/pin/create/button/?url=' + url + '&media=' + media + '&description=' + description;
html = '<img border="0" src="http://assets.pinterest.com/images/PinExt.png" title="Pin It" />';
$('div.pin-it').html(html);
//remove and add pinterest js
pinJs = $('script[src*="assets.pinterest.com/js/pinit.js"]');
pinJs.remove();
js = document.createElement('script');
js.src = pinJs.attr('src');
js.type = 'text/javascript';
document.body.appendChild(js);
}
Their pinit.js file, referenced in their "Pin it" button docs, doesn't expose any globals. It runs once and doesn't leave a trace other than the iframe it creates.
You could inject that file again to "parse" new buttons. Their JS looks at all anchor tags when it is run and replaces ones with class="pin-it-button" with their iframe'd button.
this works fine for me: http://www.mediadevelopment.no/projects/pinit/ It picks up all data on click event
I tried to adapt their code to work the same way (drop in, and forget about it), with the addition that you can make a call to Pinterest.init() to have any "new" buttons on the page (eg. ajax'd in, created dynamically, etc.) turned into the proper button.
Project: https://github.com/onassar/JS-Pinterest
Raw: https://raw.github.com/onassar/JS-Pinterest/master/Pinterest.js
As of June 2020, Pinterest updated the pin js code to v2. That's why data-pin-build might not work on
<script defer="defer" src="//assets.pinterest.com/js/pinit.js" data-pin-build="parsePins"></script>
Now it works on pinit_v2.js
<script async defer src="//assets.pinterest.com/js/pinit_v2.js" data-pin-build="parsePins"></script>
This should be simple - don't get what I'm doing wrong! This is a very basic test (I'm new to PERL and Javascript) - this is the CGI file:
#! /usr/local/bin/perl
print "Content-type: text/html\n\n";
print "<html>\n" ;
print "<head>Hello\n";
print '<script type="text/javascript" language="javascript" src="wibble.js">\n';
print "</script>\n";
print "</head>\n";
print "<body>\n";
$fred = "Fred";
$numb = 7;
print <<TEST;
<p>Starting...</p>
<p><script type="text/javascript" language="javascript">
theText = "$fred";
theNum = "$numb";
document.writeln("Direct write...");
document.writeln("Number is: " + theNum);
document.writeln("Text is: " + theText);
testWrite(theNum, theText);
</script></p>
<p>...ending JS</p>
TEST
and in wibble.js:
function testWrite(num1, txt1)
{
document.writeln("In testWrite...");
document.writeln("Number is: " + num1);
document.writeln("Text is: " + txt1);
}
In my browser, I get the first set of writeln's but my function is never called. The error on the webpage says 'Object expected' at line 15 (the 'print <<TEST' line).
I mostly suspect I haven't got the right path in my src element but I've tried every combination I can think of ('.', './', full path etc) - nothing works. The js file is in the same dir as the CGI file.
(I actually originally had the function call with no parameters, hoping that theNum and theText are global and will still work (that was the original point of this test program)).
Please put me out of my misery...
As requested, here is source code from browser:
<html>
<head><script type="text/javascript" language="javascript" src="wibble.js"></script>
</head>
<body>
<p>Starting...</p>
<p><script type="text/javascript" language="javascript">
theText = "Fred";
theNum = "7";
document.writeln("Direct write...");
document.writeln("Number is: " + theNum);
document.writeln("Text is: " + theText);
testWrite(theNum, theText);
</script></p>
<p>...ending JS</p>
</body>
</html>
and this is the actual output on the web page:
Starting...
Direct write... Number is: 7 Text is: Fred
...ending JS
Did you check your server's log to see if wibble.js is ever requested? If it's not, then there's your problem. As well, while not really the problem, this line:
print "<head>Hello\n";
is generating bad html. You can't have "bare" text in the <head> block.
For global JS variables, you use the var keyword.
x = 7; // local
var y = 7; // global