document.body.innerHTML issue - javascript

I have an email converter on a website that was written by another developer that takes a person’s user name and formats it to an email address.
If you type on a page [[username]] it will generate a clickable email address on the webpage: username#gmail.com
Now I am trying to incorporate PayPal’s skip to accessibility navigation to the website.
After I add the code for Pay Pal, when I press tab the "Skip to" link appears at the top left, but the link doesn’t work. Meaning the dropdown menu will not appear.
https://github.com/paypal/skipto
In my javascript email converter there is a code line
document.body.innerHTML = emailConvert(document.body.innerHTML);
This seems to be the the issue, because the Paypal code also is using innerHTML in their code as well.
I’m looking for a solution for this specific line of code or all of the code
document.body.innerHTML = emailConvert(document.body.innerHTML);
I have tried various solutions with no results. Any help would be great.
Here is the code:
$(document).ready(function () {
(function () {
var domains = { 'n': 'gmail.com', 'p': 'yahoo.com', 's': 'comcast.net', 'k': 'att.net', 'b': 'aol.com', 'i': 'sbcglobal.net' };
function renderEmail(str, user, domain, text) {
var email = (user.match(/#/)) ? user : user + "#" + (domain || domains["n"]);
if (domains[user.toLowerCase()]) { email = domain + "#" + domains[user.toLowerCase()]; }
return " <a href='mailto:" + email + "?Subject=" + document.title + "\"'>" + (text || email) + "</a>";
}
function emailConvert(source) { return source.replace(/\[\[\s*([^\ [\,\|]*)\|?\s*([a-zA-Z0-9_\.-]+)?\s*()\]\]/g, renderEmail).replace(/\[\[\s*([^\[\,\|]*)\|?\s*([a-zA-Z0-9_\.-]+)?\s*\,{1}\s*([^\]]*)\s*\]\]/g, renderEmail); }
document.body.innerHTML = emailConvert(document.body.innerHTML);
$.fn.emailConverter = function () { this.innerHTML = emailConvert($(this).html(emailConvert($(this).html()))); }
})();
});

innerHTML gives you a string representation of the HTML within an element. It doesn't capture event handlers that were attached to those elements programmatically, since those aren't represented in the HTML.
Don't overwrite the innerHTML of document.body. That's like telling your secretary to write something down, and s/he instead grabs everything on your desk, hurls it out the window, then scrawls the desired note directly on your desktop. Even if the secretary draws a very accurate picture of your phone on the desk, you shouldn't be surprised when it doesn't work.
Okay, not a perfect metaphor, but hopefully you get my meaning.
Instead, you should find the specific HTML element(s) you want to modify, and/or insert new HTML elements at the desired locations.
There are lots of different methods by which you can grab an existing element on the page, such as document.getElementById, document.querySelector, document.querySelectorAll, and document.getElementsByTagName. You can also drill down into the child nodes of document.body if you want to obsessively check all the elements on the page.
Similarly, there are also multiple way to insert HTML into an HTML element once you've selected it, including both appendChild and insertAdjacentHTML. You can still manipulate the innerHTML of a specific element, but you should be careful about overwriting any child nodes it might contain, especially if those child nodes might have event listeners.

Related

Google Tag Manager: return position in the DOM of the tracked link/element, {{Click Element}} variable

Background description:
I am tracking clicks on links on a web page using Google Tag Manager (GTM) and have set this up successfully, together with python code to extract the data from the API. However, GTM currently doesn't return any indication of the position on the page of the link clicked. I would like to put in the Event Label the position in the DOM of the clicked element.
The purpose of doing this is twofold: Firstly, so I can distinguish identical links (i.e. links which have the same link text and which go to same destination). Secondly, so that I can identify which of the many sections of the page the link is in: is it in the sidebar, on the top bar, in the main content etc.
Problem
I have tried using the {{Click Element}} variable, but this only returns the href of the link, not the position on the DOM.
This is puzzling because when I go into Preview mode in GTM and have a look at the data layer, I see very clearly that there was a push API call to send the DOM location:
dataLayer.push({
event: 'gtm.click',
gtm.element: 'https:// [rest of URL] : html.js.yes-js.js_active >
[lots more DOM elements separated with >s of which I am only including a few]
> div#page-container > div#et-main-area > div#main-content > h3.rpwe-title > a',
gtm.elementClasses: '',
gtm.elementId: '',
gtm.elementTarget: '',
gtm.elementUrl: __*~~URL~~*__,
gtm.uniqueEventId: 425
})
And in the "Variables" tab of the GTM preview page, Click Element shows up just as above: it is a Data Layer Variable, it is a string, and its value is given as first the URL, then a colon, followed by the long description of where the link is on the DOM, with all the parents starting from the root of the document, all the way down until the clicked link.
So why, when I add the {{Click Element}} variable to the GTM Event Label, does this only return the href of the link, not the position on the DOM? I submit the changes and publish a new version in GTM, then go to Google Analytics, and only the link href shows there.
What I have tried
Going into the DevTools in chrome (right click, Inspect) and entering the console and typing dataLayer shows that some events, e.g. gtm.elementVisibility, do have a gtm.element object which seems pretty vast. In this gtm.element object, you can click on parentElement which then has its own parentElement and so on until the top level of the DOM. So this data is clearly all there.
To access it I tried a Custom JS variable in GTM which used JSON.stringify to convert Click Element into a string. This didn't work, I suspect because the gtm.element object is too complex, with nested objects and circular references to many locations within the object.
I don't know Javascript, I am groping in the dark with it, I just want to get a nice string through to Google Analytics and from there to my python code where I can manipulate it as necessary.
Possible solution which isn't practical
In GTM I could make multiple triggers for the same event, and specify for each trigger that Click Element matches the CSS Selector for each of the sections of the page. However that would require a separate trigger for each section, which would get messy if I ever need to update the trigger settings. It also wouldn't help me distinguish between identical links.
Question
Why isn't the above working nicely, and how can I accomplish this (preferably with minimal javascript coding...)? Thanks!
Simo Ahava has a great blog post about exactly this question, at https://www.simoahava.com/analytics/create-css-path-variable-for-click-element/ (no idea why multiple google searches didn't find this...)
As suspected in the question, he explains that Click Element is not actually a string with the location of the link in the DOM, as the GTM Preview mode indicated. It is a complex HTML element.
He goes on to provide javascript code which takes this complex Click Element and extracts the location in the DOM of the link:
function() {
// copied from https://www.simoahava.com/analytics/create-css-path-variable-for-click-element/
// Build a CSS path for the clicked element
var originalEl = {{Click Element}};
var el = originalEl;
if (el instanceof Node) {
// Build the list of elements along the path
var elList = [];
do {
if (el instanceof Element) {
var classString = el.classList ? [].slice.call(el.classList).join('.') : '';
var elementName = (el.tagName ? el.tagName.toLowerCase() : '') +
(classString ? '.' + classString : '') +
(el.id ? '#' + el.id : '');
if (elementName) elList.unshift(elementName);
}
el = el.parentNode
} while (el != null);
// Get the stringified element object name
var objString = originalEl.toString().match(/\[object (\w+)\]/);
var elementType = objString ? objString[1] : originalEl.toString();
var cssString = elList.join(' > ');
// Return the CSS path as a string, prefixed with the element object name
return cssString ? elementType + ': ' + cssString : elementType;
}
}
This returns something looking like 'HTMLDivElement: html > body > div#blog > div.hasCoverMetaIn#main' where div is the type of element, element class(es) follows the period (.), and element id follows the hash (#).
So thank you to Simo Ahava, hope it is OK to share his code here. See his article for fuller explanation

Is there a to change the value of an element using JavaScript

I'm trying to change the value of an element on a third-party web page using a JavaScript Add-on to display a hyperlink
I already have the link on the page i would like to be able to click it
I think I'm on the right track using document.getElementById although I'm not sure how to then change the id into a "a href" and then how to pass it back into the value.
Sorry, this is a bit of a tricky situation so I'll try my best to explain it. On a third-party web-page which we use for our HR related tasks, there is a section titled "File Link" although this isn't a link. When you copy and paste the address into a browser it displays the file. What i am trying to do is create a hyperlink on the "File Link" section to remove the need to copy and paste the link. Because this is a third party website. We have access to the JavaScript on the website and need to change the address into a hyperlink. I'm not entirely sure this is possible.The element id is "__C_cb_file_link" and i would like to insert the link address into the element using a variable then add the link parameters into the variable then reinsert it into the element/value.
function linkIt() {
var intoLink = document.getElementById("__C_cb_file_link");
var hLink = "<a href="+intoLink+"</a>;
intoLink.value = hLink;
}
window.onload = linkIt();
<td><div class="sui-disabled" title="">m-files://view/37FF751C-A23F-4233-BD8B-243834E67731/0-46524?object=C46A7624-D24B-45F3-A301-5117EFC1F674</div>
<input type="hidden" name="__C_cb_file_link" id="__C_cb_file_link" value="m-files://view/37FF751C-A23F-4233-BD8B-243834E67731/0-46524?object=C46A7624-D24B-45F3-A301-5117EFC1F674"/></td></tr>
In below code first we read input value with new link (however we can read this value from other html tags), then we remove this element (and button) and add to parent element (of removed input) the new link
function linkIt() {
let intoLink = __C_cb_file_link.value;
let parent = __C_cb_file_link.parentNode;
__C_cb_file_link.remove();
btn.remove();
parent.innerHTML += `${intoLink}`;
}
<input id="__C_cb_file_link" value="https://example.com">
<button id="btn" onclick="linkIt()">Link It</button>
There are a number of issues with your code:
1) The code snippet in your question doesn't run because of a missing " at the end of the second line of the linkIt() function.
2) intoLink is a hidden field so anything you add to it will not be visible in the page
3) Even if point 2 were not true, setting the value of a form field will not cause HTML to appear on the page (at best you might get some plain text in a textbox).
4) "<a href="+intoLink+"</a>" doesn't work because intoLink is a complex object which represents the entire hidden field element (not just its value property). You can't convert a whole object into a string directly. You need to extract the value of the field.
A better way to do this is by creating a new element for the hyperlink and appending it to the page in a suitable place. Also I recommend not adding your event via onload - when written using this syntax only one onload event can exist in a page at once. Since you're amending another page which isn't under your control you don't want to disable any other load events which might be defined. Use addEventListener instead, which allows multiple handlers to be specified for the same event.
Demo:
function linkIt() {
var intoLink = document.getElementById("__C_cb_file_link");
var hLink = document.createElement("a");
hLink.setAttribute("href", intoLink.value);
hLink.innerHTML = "Click here";
intoLink.insertAdjacentElement('beforebegin', hLink);
}
window.addEventListener('load', linkIt);
<td>
<div class="sui-disabled" title="">m-files://view/37FF751C-A23F-4233-BD8B-243834E67731/0-46524?object=C46A7624-D24B-45F3-A301-5117EFC1F674</div>
<input type="hidden" name="__C_cb_file_link" id="__C_cb_file_link" value="m-files://view/37FF751C-A23F-4233-BD8B-243834E67731/0-46524?object=C46A7624-D24B-45F3-A301-5117EFC1F674" /></td>
</tr>
P.S. m-files:// is not a standard protocol in most browsers, unless some kind of extension has been installed, so even when you turn it into a hyperlink it may not work for everyone.
[UPDATE] I supose that your "__C_cb_file_link" was a paragraph so I get the previous text http://mylink.com and create a link with, is it what you want, right?
function linkIt() {
let fileLink = document.getElementById("__C_cb_file_link");
let hLink = fileLink.textContent;
fileLink.innerHTML = ""+hLink+"";
}
linkIt();
<div>
<p id="__C_cb_file_link">http://myLink.com</p>
</div>

Control + F similar text search box to embed in website for iframe

So I have a long iframe in my website full of text. Iframe is from an other domain that I don't own so I have no control over this embedded iframe. What I do when I want to search a part of text in this iframe is to click CTRL+F and it will find the needed text and jump on that part of the iframe.
That was ok for as long as I used this page only for myself. Now that many people are accessing this iframe i can not explain the functionality of CTRL+F to everybody. So I was thinking to embed a Text search in this page where the iframe is so it will be easier for people to find what they want.
I tried many suggestions by this site but they don't really work with iframe since they search words in the page where the iframe is embedded. How CTRL+F can detect text from iframe? Is it possible to have same functionality embedded in my website?
Thank you
Did this other day using only Javascript maybe not the most efficient as it reads and re-writes the inner html onkeyup (so i had to make some regex to ignore tags) but it works and is quick let me know if you have any improvements.
I tried to put it in codepen but as it f**ks with innerhtml so it doesnt work but this might be up for a while:http://longstone.rocks/Cinturato/fitment-guide/
Note---id:matches is a <\p> under my search telling the user how many matches it found
<script>
function Search(){
//clear everyting
document.getElementById("matches").innerHTML = "Type to find your car!";
document.getElementById("main-content").innerHTML = document.getElementById("main-content").innerHTML.replace(/<span.+?>(.+?)<\/span>/g,'$1');
if (document.getElementById("textbox").value !== ""){
alert();
//this regegex basically matches all pattern exept from when its inbetween < and >
var searchterm = ("(?<!</?[^>]*|&[^;]*)(" + document.getElementById("textbox").value + ")");
var regex = new RegExp(searchterm,"gi");
w3.addClass('textarea', 'w3-border-red');
//if there is no match then it breaks in this following line when i want it to return 0 rather than break
var numberOfMatches = document.getElementById("main-content").innerHTML.match(regex).length;
alert(numberOfMatches);
alert("fdfd");
document.getElementById("main-content").innerHTML = document.getElementById("main-content").innerHTML.replace(regex,'<span class="w3-aqua">$1</span>');
document.getElementById("matches").innerHTML = numberOfMatches + " Matches highlighted scroll to see matches";
if(numberOfMatches !== 0)
w3.removeClass('textarea', 'w3-border-red');
}
else
w3.addClass('textarea', 'w3-border-red');
}
</script>

What's the best way to append html tags to existing text?

I want to use the currently selected text in the office document to be replaced by the same selected text but surrounded with html. Effectively adding a hyperlink to the current selection.
I first read the text property of the selection
var objRange = objContext.document.getSelection();
objRange.load('text');
followed by
return objContext.sync().then(function(){
var strSelection = objRange.text;
objRange.insertHtml(
"<a href='" + decodeURIComponent(strHyperlink) + "'>" + strSelection + "</a>",
Word.InsertLocation.replace
);
return objContext.sync().then(function(){
objDialog.close();
});
});
I need a sync to read the text and then another one to write the updated text back into the document after that I close a dialog. But this sometimes causes the html to get written into the document twice. Is there a better way of doing this instead of with double context syncs?
To answer your question, if you need to read the text and then write into a different context, you'll need two syncs.
But you might take a look at the Range.hyperlink property, which is writeable. I don't know if it'll give you a way to avoid two syncs, but is intended for what you seem to be using insertHtml to do.

Programmatically turning on <strong></strong> for editable section in html5

I am working on an HTML5/javascript/php project where I have a which is set to be editable.
I know the user can be press ctrl + b to make the text bold, but I also want to allow the user to be able to click on the appropriate button on the web page to perform the same action.
I.e. when the user types it will be in normal text, they click on the bold button on the web page and it appends <strong> to the html of the section, and whatever they type now will be hold until they press the button again and it then appends </strong>.
However, at the moment when I append <strong> it seems to automatically add </strong> and I want to stop that from happening. Am I going about this the right way or is there a better way that this can be achieved.
Below is the javascript that I am trying to use
function enableDisableBold(section)
{
var boldEnabled = $("#boldEnabled").val();
var content = $("#" + section).html();
var newContent;
if (boldEnabled == "true")
{
$("#btnBold").removeClass("formatButtonsActivated");
$("#boldEnabled").val("false");
//newContent = content + "</strong>";
//$("#" + section).html(newContent);
}
else
{
$("#btnBold").addClass("formatButtonsActivated");
$("#boldEnabled").val("true");
newContent = content + "<strong>";
$("#" + section).html(newContent.replace("</strong>", ""));
}
alert($("#" + section).html());
}
Thanks for any help you can provide.
UPDATE
I've just come across another solution which does what I want to achieve, but there is a slight issue. At the end of the function call, I perform the following bit of code.
var article = document.getElementById(section);
article.focus();
document.execCommand("Bold", false, null);
The problem is this is working fine in Internet Explorer, but in Chrome is where I am having the problem. When I set the focus back to the <section> tag, it puts the cursor back to the beginning of the text. I read somewhere that putting onfocus="this.value = this.value;" on the control but this doesn't help.
I was trying to find a way of putting the cursor back to the end of the character, but I would actually need to put the cursor back to where it originally was. Is this something that can be done.
create a strong element and append your content to it
$('<strong />').append(content);

Categories