I am trying to add SyntaxHighlighter to my angular blog project. My articles have <pre> formatted code snippets and is loaded from database. This article HTML is then displayed in ArticleComponent template. This ArticleComponent itself is loaded when a particular route is triggered. But when I browse to this route and an article is loaded, the <pre> formatted codes are not highlighted at all. However, if I refresh the page, highlighting works well. Not sure where I am going wrong.
index.html
<head>
<meta charset="utf-8">
<title>My Blog</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="./assets/css/foundation.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.1/css/all.css" integrity="sha384-5sAR7xN1Nv6T6+dT2mhtzEpVJvfS3NScPQTrOxhwjIuvcA67KV2R5Jz6kr4abQsz" crossorigin="anonymous">
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shCore.css" rel="stylesheet" type="text/css" />
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css" rel="stylesheet" type="text/css" />
</head>
<body>
<app-root></app-root>
<script type="text/javascript" src="./assets/js/jquery.js"></script>
<script type="text/javascript" src="./assets/js/what-input.js"></script>
<script type="text/javascript" src="./assets/js/foundation.js"></script>
<script>
$(document).foundation();
</script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushBash.js" type="text/javascript" ></script>
<script type="text/javascript">
SyntaxHighlighter.defaults['toolbar'] = false;
SyntaxHighlighter.all()
</script>
</body>
article.component.html
<div class="cell">
<span [innerHTML]="article"></span>
</div>
article.component.ts
export class ArticleComponent implements OnInit {
public article = '';
public title = '';
constructor(private _route: ActivatedRoute, private _articleService: ArticleService) {
}
ngOnInit() {
const articleId: string = this._route.snapshot.paramMap.get('id');
const allArticles = this._articleService.getArticles();
const selectedArticleMetaData = allArticles.find(x => x.id === parseInt(articleId, 10));
this.title = selectedArticleMetaData.title;
this._articleService
.getArticleHtml(selectedArticleMetaData.id)
.subscribe((article: string) => {
this.article = article;
});
}
}
I checked the HTML DOM elements and I am able to verify that the article that is loaded without refresh has all its <pre> blocks as raw code. But when SyntaxHighlighter is actually working in case of refresh, these <pre> tags are all converted to div elements with proper style classes.
Looks like when when articles are loaded through binding, SyntaxHighlighter is unable to process the <pre> tags. Any idea how to fix this ? Also any idea why it works when I refresh this page ? In either case, the article is loaded through binding.
The SyntaxHighlighter.all() method which highlights all html with <pre> tag, is called only once i.e. when the page is loaded because you included it in the script tag of index.html. When this method is called, it will highlight all available <pre> on the page.
When you go to a route, the SyntaxHighlighter.all() function needs to be called which will highlight the new generated tags or text inside that tag.
When you refresh the page, <pre> tags are available on the page html when SyntaxHighlighter.all() is called which is the reason, it is highlighting correctly.
So for articles that are loaded through binding, call SyntaxHighlighter.all() in logic once to highlight your <pre> tags.
You need to choose the right event, where it should be called in your component and also, you need the define a variable at the top of your component below your import statements so that tslint should let you use that variable in your component i.e. declare var SyntaxHighlighter
Solved it. Answering it here as it might be helpful to some one else.
SyntaxHighlighter.all() method registers a Highlight method to windows.load event. Hence this method is only triggered on page load and never later. This solves the mystery of why highlighting worked during page refresh and not when angular routing is triggered.
The fix is to trigger a window.load event after article is loaded in the DOM. There are better ways to do this, but here is the most straight forward way to do this. Update article.component.ts to include the code to trigger window.load event.
this._articleService
.getArticleHtml(selectedArticleMetaData.id)
.subscribe((article: string) => {
this.article = article;
this.loading = false;
// trigger window.load event after 500 ms of binding the article property
setTimeout(() => {
window.dispatchEvent(new Event('load'));
}, 500);
});
Related
My goal is to load javascript in the <head> only if a certain element exists in the <body>.
However, I have a problem: I am loading a Web Component, which is just a <script> hosted elsewhere and is pulled in as:
<script src="https://someurl.com/some-web-component.min.js"></script>
This web component file is huge, so I don't want to pull it in unless it is inserted into body by our Content Management System (CMS).
The constraints I am working under are:
• The <head> is shared between pages, so I need conditional logic
• I have no control over the <body> inserted by the CMS, which will potentially contain the <my-web-component> tag
• I NEED the script to load the web component, so I can't use jQuery's $(document).ready, at least I think I can't - an error will be thrown because the browser won't know the element exists
This plunker partially shows my problem, minus all the web component complexity:
https://plnkr.co/edit/GGif2RNHX1iLAvSk1nUw?utm_source=next&utm_medium=banner&utm_campaign=next&p=preview
Any way around this?
You can use DOMContentLoaded event.
The DOMContentLoaded event is fired when the initial HTML document has
been completely loaded and parsed, without waiting for stylesheets,
images, and subframes to finish loading.
In this case you can look for the Component and add the script with something like the following
document.addEventListener("DOMContentLoaded", function(event) {
if(document.querySelector('Component')){
var script = document.createElement('script');
script.src = 'https://someurl.com/some-web-component.min.js';
document.head.appendChild(script)
}
});
Probably a better approach though would be to add the script in the head with async attribute and later remove it if the component is not found.
Something like this
<script async src = "https://someurl.com/some-web-component.min.js"> </script>
<script >
document.addEventListener("DOMContentLoaded", function(event) {
if (document.querySelector('Component') == null) {
var script = document.querySelector('script[src="https://someurl.com/some-web-component.min.js"]')
document.head.removeChild(script)
}
});
</script>
More about DOM lifecycle events
More about async script loading
I am using $(document).ready and inside this method checking if element exists or not. It is working completely fine for me. Below is code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jQuery Test Element Exists or Not</title>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var elem = document.querySelector('h1');
var isElemPresent = !!elem;
console.log('Is the element present: ', isElemPresent);
});
</script>
</head>
<body>
<h1>Hello Plunker!</h1>
<script>
var elem = document.querySelector('h1');
var isElemPresent = !!elem;
console.log('Oh NOW it works...: ', isElemPresent);
</script>
</body>
</html>
I am not sure where you are facing issue while using jQuery. There might be some other issue. Above approach is good enough to load script after checking if element is present.
Plunker link:
https://run.plnkr.co/preview/cjgczwlzt000knneldv5d52ea/
im trying to load HTML page it has it is own data and ajax calls to render a view using html-import, but the issue is when the import happens the view yet not finish rendering, is there is a way to know when the view is finished calling and rendering all its element
(function(){
function createLauncherPanel() {
var div = document.createElement("div");
div.setAttribute("class", "launcher-panel");
div.classList.add("hidden");
div.appendChild(createLauncherLink(div));
document.getElementsByTagName("body")[0].appendChild(div);
return div;
}
function createLauncherLink(container) {
var link = document.createElement("link");
link.setAttribute("rel", "import");
link.setAttribute("href", "path-to-htmlpage-with-data-load -asynchronously-with-json-feed-and-view-to-render");
console.log('container',container);
link.addEventListener('load', function(e) {
// all imports loaded
console.log(e);
console.log('link',link);
console.log(link.import);
// #ele is the element i need to get from the imported page - but sence this element is not rendered yet because of the lateinse of rendering and network calls , this will return null
container.appendChild(link.import.querySelector('#ele'));
});
return link;
}
createLauncherPanel();
})();
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>title</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/1.0.13/webcomponents-lite.js" type="text/javascript"></script>
</head>
<body>
<h1>Hello, worssld</h1>
<div id="ff"></div>
<script src="sc.js" type="text/javascript"></script>
</body>
</html>
I would suggest you to use the onload event as it will fire when everything has finished loading.
The onload event occurs when an object has been loaded.
onload is most often used within the element to execute a
script once a web page has completely loaded all content (including
images, script files, CSS files, etc.).
This question already has answers here:
Include another HTML file in a HTML file
(41 answers)
Closed 2 years ago.
I have a html "head" template and a navigation template that I want to include in all my other html files for my site.
I found this post:
Include another HTML file in a HTML file
And my question is... what if it's the header that I want to include?
So for example, I have the following file structure:
/var/www/includes/templates/header.html
navigation.html
header.html might look like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Test Portal</title>
</head>
In a case like this, can I still follow the example in the other post where they create a div and populate the div via jquery?
Method 1:
I think it would be best way to include an html content/file into another html file using jQuery.
You can simply include the jQuery.js and load the HTML file using $("#DivContent").load("yourFile.html");
For example
<html>
<head>
<script src="jquery.js"></script>
<script>
$(function(){
$("#DivContent").load("another_file.html");
});
</script>
</head>
<body>
<div id="DivContent"></div>
</body>
</html>
Method 2:
There are no such tags available to include the file but there are some third party methods available like this:
<!DOCTYPE html>
<html>
<script src="http://www.w3schools.com/lib/w3data.js"></script>
<body>
<div w3-include-html="content.html"></div>
<script>
w3IncludeHTML();
</script>
</body>
</html>
Method 3:
Some people also used server-side-includes (SSI):
<!--#include virtual="a.html" -->
Use <object> tag:
<object data="filename.html"></object>
I needed to include many files. So I created the following script:
<script>
$(function(){
$('[id$="-include"]').each(function (e){
$(this).load("includes\\" + $(this).attr("id").replace("-include", "") + ".html");
});
});
</script>
Use div, for example, to put a placeholder for the insertion.
<div id="example-include"></div>
Created folder "includes" for all files I needed to include. Created file "example.html".
It works with any number of includes. You just have to use the name convention and put all included files in the right folder.
Using HTML <iframe> tag.
I have faced similar problem , then I used
<*iframe* src = "b.html" height="*80px*" width="*500px*" > </*iframe*>
For anyone interested in a Web Component approach:
<html-include src="my-html.html"></html-include>
And the corresponding JS:
class HTMLInclude extends HTMLElement {
constructor() {
super();
this.innerHTML = "Loading...";
this.loadContent();
}
async loadContent() {
const source = this.getAttribute("src");
if (!source) {
throw new Error("No src attribute given.");
}
const response = await fetch(source);
if (response.status !== 200) {
throw new Error(`Could not load resource: ${source}`);
}
const content = await response.text();
this.innerHTML = content;
}
}
window.customElements.define("html-include", HTMLInclude);
Note that it is possible to do some nice things with a shadow DOM to make sure styling of loaded content does not influence the outer page.
The above code is pretty "modern" JS and you might not want to use the above code directly without some polyfills/babel transpilation.
This is similar to another custom tag solution, but this one uses the text between the opening and closing tags as the include path/url. The other solution uses the src attribute instead.
<html-include> ./partials/toolbar.html </html-include>
The element implementation's a little trickier:
# -- ./js/html-include.js --
class HTMLInclude extends HTMLElement {
constructor(src) {
super();
this.attachShadow({mode: "open"});
if (src) {
this.textContent = src;
}
setTimeout(() => this._load());
}
async _load() {
let src = this.textContent.trim();
if (!src) {
throw new Error("URL missing between <html-import> tags.");
}
let rsp = await fetch(src);
if (rsp.status != 200) {
throw new Error(`Failed to load file (${src}) for <html-import>.`);
}
this.shadowRoot.innerHTML = await rsp.text();
}
}
customElements.define("html-include", HTMLInclude);
The setTimeout() was necessary because this.textContent, if accessed too early, can include all the preceding text of the page (seen on Chrome/Chromium). Deferring the access fixes that.
This is what it looks like incorporated in a page:
<html>
<head>
<link rel="stylesheet" href="./css/index.css">
<script type="text/javascript" src="./js/html-include.js"></script>
</head>
<body>
<html-include> ./partials/toolbar.html </html-include>
<html-include> ./partials/side-menu.html </html-include>
<html-include> ./partials/statusbar.html </html-include>
</body>
</html>
CSS styles should be properly applied to the newly imported HTML.
This element can also be created from JS by passing it the URL to the import file.
let wgthost = document.getElementById("tgt");
wgthost.appendChild(new HTMLInclude("./partials/mywidget.html"));
I am trying to create a webpage including an iframe where the iframe has customizable content. Its an API project.
My main issue is not how to manage appending content to an iframe using jQuery, but in this case I have an empty iframe where I want to insert a full webpage into it. The code for the webpage going into the iframe is received using a .post function where I in return get the full webpage in the data variable.
I can't manage to insert the full code, which include local javascript with important javascript libraries. It is as if it doesn't execute the code inside the iframe. The code is inserted but all the main html/body etc tags are deleted from the code passed in data variable, which prevents various javascript code from working.
I have of course checked that the page received in data variable is ALL of the code. It is correct. The missing tags are missing when displayed in the iframe.
(parent page, domain unknown to me as a API provider)
<body>
<iframe id="EMSNLframe" srcdoc="" seamless></iframe>
<script>
$("#EMSNLframe").load(function(){
$.post("https://api.educationalmedia.se/API_displayNTNewsLetterMenu",
{
ResUID:"[secret code]",
MenuLanguage:"English",
PDFLanguage:"" // Blank for all languages, or state a language
},
function(data,status){
$("#EMSNLframe").contents().find("html").html(data); //<---- this is the line I need a solution for! If there is any.
});
$("#EMSNLframe").contents().find("head").append($("<style> #EMSNLmenu{font-family:'Arial',sans-serif;font-size:12px} </style>"));
});
</script>
</body>
Webpage received in data variable:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" >
<meta charset="utf-8">
<link href="https://data.educationalmedia.se/styles/kendo.common.min.css" rel="stylesheet" type="text/css" >
<link href="https://data.educationalmedia.se/styles/kendo.default.min.css" rel="stylesheet" type="text/css" >
<script src="https://data.educationalmedia.se/js/jquery.min.js"></script>
<script src="https://data.educationalmedia.se/js/kendo.core.min.js"></script>
<script src="https://data.educationalmedia.se/js/kendo.popup.min.js"></script>
<script src="https://data.educationalmedia.se/js/kendo.menu.min.js"></script>
</head>
<body>
<div class="EMSNLmenu">
<!--#4DHTML HTMLmenu_t--> Don't mind this line of code. It is server side tags for data
</div>
<script>
$("#EMSNLmenu").kendoMenu({
animation: { open: { effects: "fadeIn" } },
orientation: "vertical",
closeOnClick: true,
openOnClick: true,
direction: "bottom right"
});
</script>
</body>
</html>
I have a html + css + javascript application.
I want to be able to enable theming.
All my css are replicated in two folders: /theme1/... and /theme2/...
So my html looks like:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="theme1/file1.css"/>
<link rel="stylesheet" href="theme1/file2.css"/>
....
....
</head>
<body>
.....
</body>
</html>
I want to be able to change using javascript the home folder of the css (theme1 to theme2).
Any ideas?
Here is what you will need to solve the problem:
Get the relevant tags. In this case, any link tag with rel="stylesheet" will probably do, but you can even go so far as to specify "starting with theme1" if you want. This can all be done with document.querySelectorAll("link[rel=stylesheet][href^=theme1]")
Loop through them. A simple for loop will do nicely.
getAttribute("href") gets the string you need.
replace() will allow you to replace the part of the string you want.
setAttribute("href",newattr) will put the attribute back into the tag.
<link id="foo" rel="stylesheet" href="theme1/file1.css"/>
When you want to change the theme:
document.getElementById('foo').href = 'theme1/file2.css';