Bootstrap 4 toolkit not working properly inside angular ngFor - css

I am having a strange issue with the bootstrap toolkit inside ngFor. On hover, it is taking some time to display the toolkit title and CSS is not getting applied. Please find the screenshot for the same. And if the toolkit is used outside ngFor, it works fine. It works normally.
Here is my code
.ts
ngOnInit(): void {
jQuery(function() {
(jQuery('[data-toggle="tooltip"]') as any).tooltip();
});
this.getGroupsForBox();
}
async getGroupsForBox() {
this.groups = await this.boxManagementService.findGroupsOfBox(this.hub.id);
}
.html
<div *ngFor="let group of groups">
<span
class="badge badge-info"
data-toggle="tooltip"
data-placement="bottom"
title="{{ group.GroupDescription }}"
>{{ group.GroupName }}</span
>
<span> </span>
</div>

*ngFor is structural directive, it creates html DOM on the fly when get the data. That's how it work, ideally you should get ride of jQuery and use the angular bootstrap library.
How ever you can achieve that, you just need to make sure to execute jQuery method after that *ngFor completed the rendering off all the items in the list. Than only you should do that.
code.ts
ngOnInit(): void {
// right now in ngOnInit, nothing is there in the DOM it doesn't applied to that
// Remove this from here.
// jQuery(function() {
// (jQuery('[data-toggle="tooltip"]') as any).tooltip();
// });
this.getGroupsForBox();
}
async getGroupsForBox() {
this.groups = await this.boxManagementService.findGroupsOfBox(this.hub.id);
// once you know list are populated in html then do that part
// getting the data in TS and rendering in html there will be time difference in ms, use small hack of setTimeout not ideal not recommended but it will do the job
setTimeout(()=>{
jQuery(function() {
(jQuery('[data-toggle="tooltip"]') as any).tooltip();
});
},500);
}

Related

JavaScript can't select DOM element by ID in WordPress

Is there a way for JavaScript to interact with the DOM on a WordPress page. Or is interaction only possible through jQuery?
Button element in header.php:
<div id="settings" class="special-menu__item btn-settings">
<button class="special-menu__title">
<i style="margin-right: 10px" class="fa fa-cog" aria-hidden="true"></i>
Настройки
</button>
</div>
JavaScript Code:
const settingButton = document.getElementById("settings");
let toggle = false;
settingButton.addEventListener("click", function _f() {
if (toggle === false) {
settingButton.classList.add("active");
settingOpen.classList.add("open");
} else {
settingButton.classList.remove("active");
settingOpen.classList.remove("open");
}
toggle = !toggle;
removeEventListener("click", _f);
});
I was having this issue too.
In the console, I could grab any element I wanted with getElementById or querySelector. But when I would select it in my custom JS code, it would be null.
To get around this, you can just add your code in a load event listener. This way, after the page loads, you can run your code and change the DOM element how you want.
It might not be the most functional thing to watch, but it works.
example:
window.addEventListener('load', () => {
const element = document.querySelector('#elementId');
console.log('element I selected:', element)
})

How to render Svelte Server-side?

Can I somehow produce "initial" HTML files using Svelte?
I am using Django, Webpack and Tailwindcss. I want to use Svelte with my frontend, but I don't want to give up the speed that comes with simply using server-side rendering (Django templates). If what I present initially is a bootstrap HTML page that pulls in the bundle.js and Svelte builds the DOM on the client side, then the browsers only starts downloading images only after the JS file is loaded.
Compare this to having the initially rendered HTML that already contains links to images, the browser starts downloading them alongside the JS making for faster perceived page loading.
I do not want to use Sapper as my application server, I want to continue using Django.
The challenge is sharing state (props) between your Django app and the Svelte components.
To get the HTML code from a component:
require('svelte/register')
const MyComponent = require('./MyComponent.svelte').default
const { html } = MyComponent.render({ ...props... })
If the components has no props, you can compile and cache the HTML templates (maybe even before runtime).
If you want to send props dynamically, for example based on data in your database, then you need to do so at runtime. That means executing JS server-side. Performance won't be bad if you cache the result.
If you can't cache, then using Django for performance would be negated, because you'd be executing Svelte anyways, so might as well use Svelte to do the whole server-side job, and then use Django as a backend server.
According to this blog post you do the following instructions:
In the following post I will show how to make use of server-side
rendering in Svelte.
Most of the time you will probably run your Svelte code client-side,
but there are scenarios where you can benefit from server-side Svelte
rendering.
SEO In my opinion the primary use case for server-side rendering is
SEO. Doing an initial rendering on the server will make your website
much more crawler accessible. Crawlers typically support some
JavaScript execution, but a complex JavaScript application is unlikely
to be indexed correctly.
My experience is that applications that make ajax calls to fetch data
are particularly challenging to index. The crawler might successfully
render the initial static part of the application, but the data will
be missing since the crawler won't make the necessary ajax calls.
Doing the initial rendering on the server allows crawlers to download
the application as fully constructed html. There is no need to execute
JavaScript on the client to compose the initial view since the view
was already built on the server.
Server-side vs Client-side Technically you could build a Svelte
application without any client side components, but it would be
completely static. Basically the application would behave much like an
old server-side PHP application. Great for crawlers, but real users
typically expect a richer user experience.
This is where server-side meets client-side.
Once the server generated html is fully rendered in the browser, we
can start a client-side counterpart of the application. The
client-side version picks up where the server side application left
off.
Both versions of the application may use the same Svelte components,
but it's important to understand that they execute independently of
each other. The server-side version does not know about the
client-side version and vice versa.
It's also important to understand that there is no default state
sharing between client and server (e.g. data property).
Article Carousel I decided to use server side Svelte to build an
article carousel for the landing page of my blog. The idea is to use a
Svelte component to cycle through articles in four of my article
categories.
The carousel should load instantly on page load, so I decided to
render the initial view on the server. Once the page has loaded I
start the client-side counterpart of the Svelte component to
dynamically cycle through the articles.
I am not known for css or design skills, so don't expect a pretty UI,
but here's how I wired everything up.
I start by creating an Article component that will be used to render
the articles in the carousel. The component should probably be split
into two components, but keeping it as one for the purposes of this
blog post.
<div class="row">
<span class="slide-show-card col-sm-3">
<h4>Angular</h4>
<h5><a class="slide-show-link" href="{{angularUrl}}">{{angularTitle}}</a></h5>
<div class="label label-success slide-show-count">Viewed {{angular.viewCount}} times</div>
<div>{{angularIntro}}</div>
</span>
<span class="slide-show-card col-sm-3">
<h4>JavaScript</h4>
<h5><a class="slide-show-link" href="{{javascriptUrl}}">{{javascriptTitle}}</a></h5>
<div class="label label-success slide-show-count">Viewed {{javascript.viewCount}} times</div>
<div>{{javascriptIntro}}</div>
</span>
<span class="slide-show-card col-sm-3">
<h4>Svelte</h4>
<h5><a class="slide-show-link" href="{{svelteUrl}}">{{svelteTitle}}</a></h5>
<div class="label label-success slide-show-count">Viewed {{svelte.viewCount}} times</div>
<div>{{svelteIntro}}</div>
</span>
<span class="slide-show-card col-sm-3">
<h4>React</h4>
<h5><a class="slide-show-link" href="{{reactUrl}}">{{reactTitle}}</a></h5>
<div class="label label-success slide-show-count">Viewed {{react.viewCount}} times</div>
<div>{{reactIntro}}</div>
</span>
</div>
<script>
class ArticleService {
constructor() {
this.articles = [];
this.index = {
'angular': -1,
'javascript': -1,
'svelte': -1,
'react': -1
};
}
getArticles() {
return fetch('/full-article-list-json')
.then((data) => {
return data.json();
})
.then((articles) => {
this.articles = articles;
return articles;
});
}
getNextArticle(key) {
this.index[key] = (this.index[key] + 1) % this.articles[key].length;
let a = this.articles[key][this.index[key]];
return a;
}
}
let articleService = new ArticleService();
export default {
onrender() {
let update = () => {
this.set({angular: articleService.getNextArticle('angular')});
this.set({javascript: articleService.getNextArticle('javascript')});
this.set({svelte: articleService.getNextArticle('svelte')});
this.set({react: articleService.getNextArticle('react')});
};
articleService.getArticles()
.then((articles) => {
update();
if(typeof global === 'undefined') {
document.querySelector('#articles').innerHTML = '';
document.querySelector('#articles-client-side').style.display =
"block";
}
})
.then(() => {
setInterval(() => {
articleService.getArticles()
.then((articles) => {
update();
});
}, 5000);
});
},
data () {
return {}
},
computed: {
angularTitle: angular => angular.title || '',
angularIntro: angular => angular.intro || '',
angularUrl: angular => `viewarticle/${angular.friendlyUrl}`,
javascriptTitle: javascript => javascript.title || '',
javascriptIntro: javascript => javascript.intro || '',
javascriptUrl: javascript => `viewarticle/${javascript.friendlyUrl}`,
svelteTitle: svelte => svelte.title || '',
svelteIntro: svelte => svelte.intro || '',
svelteUrl: svelte => `viewarticle/${svelte.friendlyUrl}`,
reactTitle: react => react.title || '',
reactIntro: react => react.intro || '',
reactUrl: react => `viewarticle/${react.friendlyUrl}`,
}
}
</script>
Server Let's take a look at the server code.
The first thing we have to do is activate the compiler by requiring
svelte/ssr/register
require( 'svelte/ssr/register' );
Next we have to require the component html file to get a handle to the
component.
We then call the render method and pass it an initial data object. The
data object is a standard Svelte data object.
app.get('/', function(req, res) {
var component = require('./app/article-show/Articles.html');
var articles = component.render({
angular: articles.angular,
svelte: articles.svelte,
react: articles.react,
javascript: articles.javascript
});
res.render('index.html', {articles: articles});
});
After calling render we get back a fully rendered component. We now
have to pass this to the node view engine.
In my case I am using Express with Mustache, so I can just pass the
component as an object to the render method. Then in my index.html
page I use Mustache view syntax with triple curly braces to render the
component on the page like so.
{{{articles}}} Client What we have so far is enough to render the
initial view of my component, but it won't support cycling through new
articles every few seconds.
To achieve this we have to start up a client-side version of the
Article component.
The client side version is loaded as a standard Svelte client-side
component.
import articles from './articles';
var articlesSection = new articles({
target: document.querySelector( 'main' ),
data: {angular: {}, javascript: {}, svelte: {}, react: {}}
});
Once the client-side version is activated we will have two components
in the DOM. As soon as the client-side version is ready to take over I
wipe out the server-side version.
There might be a more elegant way to do this, but I simply clear out
the server generated DOM element and flip a style on the client-side
version.
Navigation In addition to the article carousel I also built my main
navigation as a Svelte server side component. The nav is a pure server
side component with no client side counterpart.
The navigation is largely static, but it supports dynamic styling of
the active nav item. Here is the nav component code:
<div class="nav col-md-2 hidden-sm hidden-xs">
<a class="navLink" href="/"><div class="navBox {{home}}">Home</div></a>
<a class="navLink" href="/most-popular"><div class="navBox {{mostpopular}}">Most Popular</div></a>
<a class="navLink" href="/most-recent"><div class="navBox {{mostrecent}}">Most Recent</div></a>
<a class="navLink" href="/articleList/angular"><div class="navBox {{angular}}">Angular</div></a>
<a class="navLink" href="/articleList/react"><div class="navBox {{react}}">React</div></a>
<a class="navLink" href="/articleList/aurelia"><div class="navBox {{aurelia}}">Aurelia</div></a>
<a class="navLink" href="/articleList/javascript"><div class="navBox {{javascript}}">JavaScript</div></a>
<a class="navLink" href="/articleList/nodejs"><div class="navBox {{nodejs}}">NodeJS</div></a>
<a class="navLink" href="/articleList/vue"><div class="navBox {{vue}}">Vue</div></a>
<a class="navLink" href="/articleList/svelte"><div class="navBox {{svelte}}">Svelte</div></a>
<a class="navLink" href="/articleList/mongodb"><div class="navBox {{mongodb}}">MongoDB</div></a>
<a class="navLink" href="/articleList/unittesting"><div class="navBox {{unittesting}}">UnitTesting</div></a>
<a class="navLink" href="/articleList/dotnet"><div class="navBox {{dotnet}}">.Net</div></a>
<a class="navLink" href="/questions"><div class="navBox {{questions}}">Q&A</div></a>
<a class="navLink" href="/full-article-list"><div class="navBox {{all}}">All</div></a>
</div>
<script>
export default {
data () {
return {}
},
}
</script>
Because the nav is so static, there is no reason to regenerate the
server side html for every request. As an optimization I have decided
to cache the different variations of the nav. The only difference
between the different versions of the nav is that the "active" nav
items style is applied to different elements.
Here is some basic caching logic that ensures that we only generate
each nav version once.
function getNavComponent(key) {
key = key || 'home';
key = key.replace('-', '');
if(!navCache[key]) {
navCache[key] = createNavComponent(key);
}
return navCache[key];
}
function createNavComponent(key) {
var nav = require('./app/article-show/Navigation.html');
var data = {};
data[key] = 'activeNav';
return nav.render(data);
}
Demo If you want to test out my server-side vs client-side view you
can find it here.
I also loaded my site in Google web master tools to compare a
crawler's view of the component to a user's view
As you can tell from the screenshot my component looks pretty good to
crawlers after adding server side rendering.
Left side is the crawler view and the right side is the user view.

Disqus Comments affecting my pagespeed. How can I either lazy load or click to show comments?

Hi I've static website(made with Hugo) with Disqus installed. My website load time is really affected by ~30% in negative. So, I wish I could prevent it from loading it until a person reaches to that section or a better approach is that there'll be 'show comments' button that totally leave on visitor to decide.
I'm already using lazyload(lozad js library) but it's not working with disqus even I tried following code too
var iframes = doc.querySelectorAll('iframe');
iframes.forEach(function (e){
e.classList.add('lozad'); // adds required 'lozad' class
var iframeAttr = e.getAttribute('src');
e.setAttribute('data-src', iframeAttr); // lozad needs source data in data-src attribute
e.setAttribute('src', ''); // empty src attribute so that library use it at right time
});
Zachary wrote a tutorial for Hugo sites on how to display a Show comments button and have the Disqus comments only displayed when a reader click on the button.
As a bonus, the Disqus Javascript file embed.js will only load when the button is clicked.
To add this to your site, first, create a partial and name it disqus.html. Inside this file, put the following code:
<div id="disqus-container">
{{ with .Site.DisqusShortname }}
<button id="disqus-button" onclick="showComments()">Show comments</button>
<div id="disqus-comments">
{{ $isDummyName := eq . "yourdiscussshortname" }}
{{ $isServer := $.Site.IsServer }}
{{ if or $isDummyName $isServer }}
<p>Disqus comments are disabled.</p>
<script type="application/javascript">
function showComments() {
{{ partial "disqus-js-common.js" . | safeJS }}
}
</script>
{{ else }}
<div id="disqus_thread">
</div>
<script type="application/javascript">
function showComments() {
{{ partial "disqus-js-main.js" . | safeJS }}
{{ partial "disqus-js-common.js" . | safeJS }}
}
</script>
{{ end }}
<noscript>Enable JavaScript to view Disqus comments.</noscript>
</div>
{{ end }}
</div>
Then, create two Javascript files. First one call it disqus-js-common.js. Inside this file, add the following code:
// Remove button
var disqusButton = document.getElementById('disqus-button');
disqusButton.parentNode.removeChild(disqusButton);
// Un-hide comments
var disqusComments = document.getElementById('disqus-comments');
disqusComments.style.display = 'block';
The second Javascript file call it disqus-js-main.js. And inside this one, add the following code:
// Config
var disqus_config = function () {
};
// Build and append comments
var d = document;
var s = d.createElement('script');
s.async = true;
s.src = '//' + "{{ . }}" + '.disqus.com/embed.js';
s.setAttribute('data-timestamp', + new Date());
(d.head || d.body).appendChild(s);
Finally, add a little bit of CSS to make everything looks better:
#disqus-container {
font-size: 0.85rem;
border: 1px solid;
padding: 1.5rem;
}
#disqus-button {
width: 100%;
}
#disqus-comments {
display: none;
}
#disqus-comments,
#disqus-comments iframe {
max-height: 65vh !important;
overflow-y: auto;
}
Source
You can use the IntersectionObserver API to lazily load & initialize Disqus only once a user scrolls to a particular part of the page. The IO API is well-supported in modern browsers, so you wouldn't need to load any other lazy loading package (or custom JS) that'd further bog down your site.
Here's an example I got working. It uses the div that Disqus uses to mount its comments to as the point on the page that triggers everything to load & initialize. Note that a good chunk of this (it's commented) is copied straight from their documentation.
<script>
let mountNode = document.querySelector("#disqus_thread");
let options = {
rootMargin: "0px",
threshold: 0,
};
let observer = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
console.log("Initialize Disqus!");
//-- Copied from their documentation: START
var disqus_config = function () {
this.page.url = "https://whatever.com";
this.page.identifier = "whatever!";
};
var d = document,
s = d.createElement("script");
s.src = "https://your-shortname.disqus.com/embed.js";
s.setAttribute("data-timestamp", +new Date());
(d.head || d.body).appendChild(s);
//-- Copied from their documentation: END
// After initializing, we don't need this observer anymore.
observer.unobserve(mountNode);
return;
}
});
}, options);
observer.observe(mountNode);
</script>
All that said, even this sort of approach is gonna get you so far. Building comments and a form client-side is an inherently expensive set of tasks, and so you're still gonna see some detrimental effects to page performance. If not load time, then the blocking of the main thread by all that Disqus is executing on the page when it starts up. You're also gonna miss out on the SEO benefit of having status HTML comments on your page.
All of this represents the bulk of the reasons I ditched disqus on my own site and built my own service for it, which I eventually opened up to the public. It integrates with different platforms (not yet Hugo, but it's down the road hopefully), and spits out statically-rendered comments and avoides the-side bloat, etc. Keep it in mind for any future rebuild you go through:
https://jamcomments.com/
It can be achieved with pure HTML and JavaScript
But since I really like Alpine.js this is how it can be done
<button
x-data="disqusComponent()"
x-show="!loaded"
#click="toggle()"
>Show/Post Comments
</button>
<div id="disqus_thread"></div>
See Load Disqus Comments on demand

how to attach events to generated html of a template in Meteor 0.8 with Blaze

I'm using Meteor 0.8 with Blaze and I want to attach events dynamically to HTML contents generated using UI.toHTML of a template. The functionality I am looking for is the alternative to Spark.attachEvents in Blaze.
What I have done so far is that I have created the following template to be used like a widget/component.
<template name="postLinks">
<div id="link-popover-wrapper" >
<ul class="link-popover">
{{#each linkOptions}}
<li><a tabindex="-1" class="link-action" id="link-{{value}}" href="#">{{label}}</a>
</li>
{{/each}}
</ul>
</div>
</template>
And the template is used in Helper of the myPostItem template.
Template.myPostItem.events({
'click .post-item-link-picker': function (evt, tmpl) {
var tempData = {linkOptions:[{label:'Favorite', value : 'favorite'}, ...]};
// Get the HTML content of the template passing data
var linkContent = UI.toHTML(Template['postLinks'].extend({data: function () { return tempData; }}));
// Attach events to the linkContent like in Spark
/*Spark.attachEvents({
'click link-action': function (e, tmpl) {
alert("Component item click");
}
}, linkContent);*/
// Popover the content using Bootstrap popover function
}
});
So my requirement is to attach events to a dynamically generated HTML contents.in the linkContent like Spark.attachEvents after the following line as mentioned in above code.
var linkContent = UI.toHTML(Template['postLinks'].extend({data: function () { return tempData; }}));
Hope somebody can help to find a way to do this in Meteor 0.8 with Blaze.
The reason that Spark-generated HTML could be directly inserted into the DOM is because it had "landmarks" - annotations that could be processed into events and handlers when the DOM nodes were materialized.
Blaze works differently - it inserts a UI component into the DOM directly and attaches events, using the UI.render function. It cannot directly attach template events to the DOM if you use UI.toHTML because there are none of the annotations that Spark had for doing this.
I'm also using Bootstrap popovers in my app, and as far as I know there's no clean way to insert reactive content into a popover. However, you can approximate it with a hack in the following way:
In the content callback of the popover, render the template with UI.toHTML - a nonreactive version of the content. This is necessary because otherwise the popover won't be sized and positioned properly.
Using a Meteor.defer call, replace the popover contents with reactive content, so they'll continue updating while the popover is open.
Because Bootstrap uses jQuery, you should be fine with removing reactive logic properly, for now. Future versions of Meteor will probably have easier ways to do this.

Angular JS: Detect when page content has rendered fully and is visible

My situation
I have a BIG list of people which all have their own profile pic and information. All items have some CSS3 styling/animations.
Im using:
<li ng-repeat="person in people">
Where people is an array with over 150 objects from an external JSON file that all have to show on the same page.
The page loads/renders slowly, and its not because of the $http GET function that gets the JSON data with people (which only takes about 100ms), but the rendering of the page and seemingly the rendering of all the css3 styling applied to each item (if i turn my CSS off it gets much faster).
My main problem
I can live with the page rendering slowly but what i really want is to detect whenever my heavy page has loaded its content in order to start and stop a loading indicator. Ive tried making a directive like this:
directive("peopleList", function () {
return function (scope, element, attrs) {
scope.$watch("people", function (value) {
var val = value || null;
console.log("Loading started");
if (val){
console.log("Loading finished");
}
});
};
});
And put this directive on the wrapper div that is loading my list of people. But the loading appears to stop whenever my object with JSON data has been filled, which only takes around 100ms, but the actual visual rendering of the page takes more like 4-5s.
Ive also tried solutions like http://jsfiddle.net/zdam/dBR2r/ but i get the same result there.
What i need
When i navigate through my pages (which all have the same kind of lists) i need something that tells me whenever the page has fully loaded and is visible for the user so that i know when to hide my loading indicator.
Try adding this directive -- the magic is in the link function:
directives
.directive('onFinishRender', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
scope.$evalAsync(attr.onFinishRender);
}
}
}
});
Add a loader as the first item:
<li ng-show="!isDoneLoading">
<div class="loading">Loading...</div>
</li>
Then in your li, add this:
<li ng-repeat="..." on-finish-render="isDoneLoading = true;"> ... </li>

Resources