Chrome 67 broke my WebComponent 😯 - google-chrome

For some strange reason, the update from Chrome 66 to Chrome 67 broke my web application's WebComponents.
The map at the top is defined via a WebComponent custom element. It should be noted that it isn't rendered through a shadow DOM. It is rendered directly by appending a child <img> to my map component.
Chrome 66's DOM:
<bj-map ...>
<img src="...">
</bj-map>
Chrome 67's DOM:
<bj-map ...></bj-map>
The component's class is Map, extending from HTMLElement. Non-relevant methods have been removed.
class Map extends HTMLElement {
connectedCallback () {
this.render()
}
render () {
const src = this.getMapSource()
this.innerHTML = `
<img src="${src}"></img>
`
}
}
customElements.define('bj-map', Map, { extends: 'div' })
export default Map
Are there any ways that I can try to debug this magical issue?

Remove { extends: 'div' } from customElements.define('bj-map', Map, { extends: 'div' }).
class Map extends HTMLElement is not extending HTMLDivElement so you don't need to specify an extend in define.
Extending native HTML elements has additional details.

Related

Prime NG Menu - Focus on first element when activated

I am attempting to move the focus from the prime ng menu to the first of the list elements that appear when you toggle the menu. Here is what I have so far.
In the template I have:
<p-menu appendTo="body" #menu [popup]="true" [model]="menuItems" (onShow)="openMenu()"></p-menu>
And in my typescript I have:
#ViewChild('menu', { read: ElementRef }) menu: ElementRef;
...
openMenu() {
const menuItem = (
this.menu.nativeElement.getElementsByClassName(
'p-menuitem'
) as HTMLCollectionOf<HTMLElement>
)[0];
menuItem.focus();
}
When testing this, the element is definitely being discovered, and I can set attributes on the element. However, setting the focus does not appear to be working. Are there any decent ways of accessing DOM elements and setting focus?
Thanks!
Here is what worked for me in the end:
openMenu() {
const menuItem = (
this.menu.nativeElement.getElementsByClassName(
'p-menuitem-link'
) as HTMLCollectionOf<HTMLElement>
)[0];
setTimeout(() => {
menuItem.focus();
}, 1);
}

Web Components - extend native element's style

I would like to extend the native button element but I am not sure how to add styling. In Google's example here they don't use a template so the fancy-button custom element itself is the button, rather than adding a template and shadow DOM containing a button element. It seems to defeat the object of extending a native element if I just add a button directly to the shadow DOM, but I don't know how to style and extend native element. How can I create a custom element which is simply the native button element extended to have a red background?
var style = `button { background-color: red; };
class FancyButton extends HTMLButtonElement {
constructor() {
super();
}
customElements.define('fancy-button', FancyButton, {extends: 'button'});
since you don't have shadowDOM involved you can use global CSS
you can set styles in the connectedCallback: this.style.background='red'
you can dynamically create a STYLE tag with unique identifier scoping your element
See JSFiddle for all 3 examples: https://jsfiddle.net/WebComponents/gohzwvL4/
Important is the notation for your Customized Built-In Element
Correct : <button is="fancy-button></button>
InCorrect: <fancy-button></fancy-button> (this is Autonomous Element notation)
.
Firefox pitfall:
The INcorrect notation works in Firefox , but not in Chrome & Opera
Firefox processes Extended Built-In Elements with Autonomous Element notation
but only for elements created in the DOM prior to definition:
This
<fancy-button>Hello Fancy Red Button #1</fancy-button>
<script>
class FancyButton extends HTMLButtonElement {
constructor() {
super();
}
connectedCallback() {
this.style.background = 'red';
}
}
customElements.define('fancy-button', FancyButton, { extends: 'button' });
</script>
<fancy-button>Hello Fancy Red Button #2</fancy-button>
is displayed in Firefox as:
any number of Custom Elements before the SCRIPT tag are colored!
When the <SCRIPT> is moved into the <HEAD> Firefox won't color any background
When the script is executed after the onload event all buttons are colored
This is non-standard behaviour!

Vue.js anchor to div within the same component

I'm developing a Vue.js application and I'm having trouble to link an anchor to a certain div within a component.
I have the following anchor:
Porto, Portugal
and the following div:
<div id="porto" class="fl-porto">
I'm using vue-router in hash mode.
The problem is, whenever I click the "porto-button" it will redirect me to the "home" page ( ' / ' )
I'm using Vue.js 1.X and I tried using history mode (URL without the hashbang) but it gives me a cannot GET '/page' error upon refreshing a page.
Am I doing something wrong? What can I do about this?
Because you are using router in hash mode, you will not be able to scroll that easily because scrolling to /#something will actually redirect you to 'something' page.
You will have to emulate scrolling behaviour on your own, try doing something like that:
//P.S. the code is written for Vue 2.
//You will have to adjust it to Vue 1.
//Your view:
<a class="porto-button" #click="scrollMeTo('porto')">Porto, Portugal</a>
...
<div ref="porto" class="fl-porto">
//Your code:
methods: {
scrollMeTo(refName) {
var element = this.$refs[refName];
var top = element.offsetTop;
window.scrollTo(0, top);
}
}
How it works:
Set the references through ref attribute to the element you would like to scroll to;
Write a function that will programmatically set window.scrollY to the top of the referenced element.
Job is done :)
Update 1:
jsfiddle https://jsfiddle.net/5k4ptmqg/4/
Update 2:
Seems that in Vue 1 ref="name" looked like el:name (docs), here is an updated example:
https://jsfiddle.net/5y3pkoyz/2/
Another method is to use "scrollIntoView()"
So, euvl's code still stands, except you would change the method slightly:
new Vue({
el: '#app',
methods: {
goto(refName) {
var element = this.$els[refName];
element.scrollIntoView();
}
}
})
If you wanted to get fancy and make the scroll smooth, you can even add the following:
element.scrollIntoView({ behavior: 'smooth' });
Note that this will need a polyfill for older browsers.
What worked for me
<router-link to="#leaders">Leaders</router-link>
or dynamic
<router-link :to="`#${subMenuItem.linkTarget}`" class="page-submenu-list__link">
{{subMenuItem.linkTitle}}
</router-link>
in router
routes:[],
scrollBehavior (to, from, savedPosition) {
//https://router.vuejs.org/guide/advanced/scroll-behavior.html
if (to.hash) {
return { selector: to.hash }
} else if (savedPosition) {
return savedPosition;
} else {
return { x: 0, y: 0 }
}
}
An alternative solution is to use the v-scroll-to directive (webpage, github).
I find this solution to be clean, simple, flexible and effective. To use:
Install it:
npm install --save vue-scrollto
Have Vue 'use' it:
var VueScrollTo = require('vue-scrollto');
Vue.use(VueScrollTo)
Apply it as a directive in your Vue component's template:
Scroll to #element
<div id="element">
Hi. I'm #element.
</div>
Or apply it programmatically in your Vue component's methods:
this.$scrollTo('#element', 500, { easing: 'ease-in-out' })
Or apply it programmatically in your Vuex actions:
import { scrollTo } from 'vue-scrollto'
scrollTo('#element', 500, { easing: 'ease-in-out' })
Another solution, if you're already using Vuetify, you may prefer to use Vuetify's built-in programmatic scrolling method, $vuetify.goTo():
<v-btn #click="$vuetify.goTo('#element', {duration: 500, easing: 'easeInOutCubic'})">
Scroll to #element
</v-btn>
<div id="element">
Hi. I'm #element.
</div>
If you set a ref="something" on an element, you could also use this oneliner with #click:
<a #click="$refs.something.$el.scrollIntoView()">
Go to something
</a>

AngularJS ng-href and svg xlink

I'd like some input on using xml namespaced attributes with angular.
The problem is angular comes with a couple of directives to handle writing attributes such as href and src when angular has parsed the expresssions (otherwise the browser will try to load {{mymodel.myimage}} as a url)
https://github.com/angular/angular.js/blob/master/src/ng/directive/booleanAttrs.js#L329
The problem I'm facing is that I'm using angular to output svg together with D3 and since angular doesn't have a way to output xlink:href I was stuck.
I created a custom directive that outputs xlink:href
app.directive('ngXlinkHref', function () {
return {
priority: 99,
restrict: 'A',
link: function (scope, element, attr) {
var attrName = 'xlink:href';
attr.$observe('ngXlinkHref', function (value) {
if (!value)
return;
attr.$set(attrName, value);
});
}
};
});
Full demo: http://plnkr.co/edit/cMhGRh
But it seems that if I don't manually add xlink:href to the element, the svg image will not render.
Any suggestions on how to best handle xml namespaces / svg together with angular would be greatly appreciated.
You can use ng-attr-<some attribute>
ng-attr-xlink:href="{{xxx}}" works for me.
Note that you also need an empty xlink:href="" as initial value. – Derek Hsu
If, like me, you're looking for a way to add images to svg, you can do so adding:
xlink:href="" ng-href="{{ foo }}"
Example:
http://jsbin.com/sigoleya/1/edit?html,js,output
Where I found the solution:
https://github.com/angular/angular.js/issues/7697
I ran into a similar problem when trying to output a value for xlink:href that's tied to the model. Based on the user's chosen <option> in a <select> control, I was trying to show a dynamic SVG icon via the xlink:href attribute of the <use> element.
I found a thread about this in the GitHub Issues for AngularJS. Based on the discussion there, it appears that because a viable workaround exists, they've effectively tabled a fix by moving it to the Backlog milestone.
What ultimately worked for me was inspired by this JSBin:
http://jsbin.com/sigoleya/1/edit?html,js,output
Here's the code I used in my template:
<svg class="icon" data-ng-class="category.iconName">
<use xlink:href="" data-ng-href="{{'#' + category.iconName}}">
</svg>
Given a category.iconName of icon-music, for example, Angular sets the xlink:href dynamically to #icon-music, which references the <svg id="icon-music"> element further up on the same page.
As others have noted, what's key is setting a blank xlink:href="" attribute on the element where you call the ngHref directive. Attribute order does not seem to matter. Using ng-attr-xlink:href="{{xxx}}" (as mentioned in Derek Hsu's answer) did not work for me.
All of this assumes Angular 1.3.36.
I solved the same problem with the following modules:
Module for SVGs:
var app = angular.module('Svgs', []);
angular.forEach([
{ ngAttrName: 'ngXlinkHref', attrName: 'xlink:href' },
{ ngAttrName: 'ngWidth', attrName: 'width' },
{ ngAttrName: 'ngHeight', attrName: 'height' }
], function (pair) {
var ngAttrName = pair.ngAttrName;
var attrName = pair.attrName;
app.directive(ngAttrName, function (IeHelperSrv) {
return {
priority: 99,
link: function (scope, element, attrs) {
attrs.$observe(ngAttrName, function (value) {
if (!value) return;
attrs.$set(attrName, value);
if (IeHelperSrv.isIE) element.prop(attrName, value);
});
}
};
});
});
Module for IE detection:
angular.module('IeHelper', []).factory('IeHelperSrv', function () {
return {
isIE: checkForIE.isIE,
}
});
var checkForIE = {
init: function () {
this.isIE = (navigator.userAgent.indexOf('MSIE') != -1);
}
};
checkForIE.init();
HTML:
<!-- image has initial fake source, width and height to force it to render -->
<image xlink:href="~/Content/Empty.png" width="1" height="1"
ng-xlink-href="{{item.imageSrc}}"
ng-width="{{item.width}}" ng-height="{{item.height}}"
ng-cloak
/>
For anyone else having this problem due to Angular/Angular UI Router in HTML5 mode, I came up with a straightforward fix to enable svg sprite icons to work with their xlink:href attribute and the tag.
Gist is here: https://gist.github.com/planetflash/4d9d66e924aae95f7618c03f2aabd4a3
app.run(['$rootScope', '$window', function($rootScope, $window){
$rootScope.$on('$locationChangeSuccess', function(event){
$rootScope.absurl = $window.location.href;
});
<svg><use xlink:href="{{absurl+'#svgvID'}}"></use></svg>
I ran into this problem where I was using Ajax to load the svg spritesheet onto the page. If I had a on the page before the spritesheet was loaded, it would fail and would not resolve once the spritesheet was avaialble. Any added to the dom after the spritesheet was loaded were fine. I had to delay putting the items in the dom until after the spritesheet finished loading.
This only affected IOS. All other browsers didn't care about the order.
This took me more time than I would've wanted. Around 20-30 minutes.
If I understand correctly, any failed loading on image element will render that element useless in the future. I believe it's something similiar #GeekyMonkey is saying. If angular binding system has set xlink:href initially to null, Image element wont work anymore, even if we have valid value in the future.
Here is solution, notice how I have wrapped image element inside g element, using ng-if directive. That makes sure we will bind against image only when a correct value is available.
<g ng-if="vm.svgMap.background != null">
<image
ng-attr-xlink:href="{{vm.svgMap.background.image | trusted}}"
ng-attr-width="{{vm.svgMap.background.width}}"
ng-attr-height="{{vm.svgMap.background.width}}"
xlink:href=""
width="1"
height="1"
x="0"
y="0"></image>
</g>
As others said, the order of attributes are important as well. To ensure that angularJS allows us to bind image element, we'll also have to trust that resource, I've done it through filter (it's the one in xlink:href attribute):
(function() {
'use strict';
angular.module('myTool').filter('trusted', TrustedFilter);
function TrustedFilter($sce) {
return function(url) {
return $sce.trustAsResourceUrl(url);
};
};
}());

With JQueryUI Draggable, how to select object being dragged

I am using JQueryUI draggable and I would like to be able to add styling to the draggable element while it is being dragged. I have tried variations on this code:
$(".ui-widget-content").draggable({
drag: function(event, ui) {
$(this).css("width", "50px");
});
However, my attempts have failed and I believe it it because I don't know how to get the draggable element from the ui object. What am I missing?
No need for extra JavaScript. Just use this CSS selector:
.ui-draggable-dragging {
/*
This class is applied to the element while it is being dragged.
This is done automatically by jQueryUI.
*/
width: 50px;
}
Read the docs here: http://jqueryui.com/demos/draggable/#overview-main