How can I write shadow dom that selects particular elements *after* most of the children? - css-selectors

I'm writing a custom <figure>-like element for which I'd like to put a <figcaption> under the main content. The <figcaption> needs to include some content (text with potential formatting) from the child nodes of the custom element, for which I'm using <content select="span.caption"></content>. However, per the Shadow DOM spec, the <span> has already been distributed to the earlier <content></content> element that laid out the body of the custom element.
I've tried using <content select=":not(span.caption)"></content> to avoid distributing the caption, but that appears not to be a compound selector and matches nothing.
What's the recommended way to get the elements to render in the order I want?
Here's the code with the problem, which is also at http://jsbin.com/vizoqusu/3/edit:
<!DOCTYPE html>
<html>
<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/polymer/0.1.4/platform.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/polymer/0.1.4/polymer.js"></script>
<meta charset="utf-8">
</head>
<body>
<polymer-element name="my-figure" noscript>
<template>
<figure>
<content></content>
<figcaption>Figure 7: <content select="span.caption"></content></figcaption>
</figure>
</template>
</polymer-element>
<my-figure>
Hello World!
<span class="caption">The caption</span>
</my-figure>
</body>
</html>

This actually does work today under ShadowDOM polyfill, with two caveats:
:not only takes simple selectors as arguments, so you need to do something like *:not(.caption)
If you use a selector at all, plain text nodes are ignored, so you have to wrap your other content in some kind of element.
Here is an example:
http://jsbin.com/vizoqusu/5/edit
<polymer-element name="my-figure" noscript>
<template>
<figure>
<content select="*:not(.caption)"></content>
<figcaption>Figure 7: <content select="span.caption"></content></figcaption>
</figure>
</template>
</polymer-element>
<my-figure>
<span>Hello World!</span>
<span class="caption">The caption</span>
</my-figure>
As for the native system, I used your use case to argue for inclusion of :not() into the specification, you can see it here:
https://www.w3.org/Bugs/Public/show_bug.cgi?id=24867

I see a few options here:
You could use getDistributedNodes to first get the content of the element and then inject the text (Hello World) and caption at the right points as follows:
<polymer-element name="my-figure">
<template>
<figure id="figureHolder">
<content id="content"></content>
<figcaption>Figure 7: <content id="caption" select="span.caption"></content></figcaption>
</figure>
</template>
<script>
Polymer('my-figure', {
caption: '',
ready: function() {
var nodes = this.$.content.getDistributedNodes();
this.$.caption.innerHTML = nodes[1].innerHTML;
this.$.content.getDistributedNodes()[0].data = nodes[0].data;
this.$.content.getDistributedNodes()[1].innerHTML = '';
}
});
</script>
</polymer-element>
<my-figure>
Hello World!
<span class="caption">The caption</span>
</my-figure>
You could move your caption into an attribute and this allows you to maintain a clean separation between the content of your figure and the caption itself:
<polymer-element name="my-figure" attributes="caption" noscript>
<template>
<figure>
<content></content>
<figcaption>Figure 7: {{caption}} </figcaption>
</figure>
</template>
</polymer-element>
<my-figure caption="The caption">
Hello World!
</my-figure>
You could move your main content (e.g Hello World) into it's own span and add a class to allow targeting it from inside your template directly, so:
<polymer-element name="my-figure" noscript>
<template>
<figure>
<content select="span.hello"></content>
<figcaption>Figure 7:
<content select="span.caption"></content></figcaption>
</figure>
</template>
</polymer-element>
<my-figure>
<span class="hello">Hello World!</span>
<span class="caption">The caption</span>
</my-figure>

Related

Missing DOM when building a static react page with react-snapshot

Newbie on pre-rendering here, so i'm following this tutorial:
https://medium.com/superhighfives/an-almost-static-stack-6df0a2791319
And I get to the point where the static HTML is built by react-snapshot.
On the generated HTML the body looks like this:
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],p[t]&&i.push(p[t][0]),p[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),a()}function a(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==p[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},p={2:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var r=window.webpackJsonp=window.webpackJsonp||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;a()}([])</script><script src="/static/js/1.2664f19e.chunk.js"></script><script src="/static/js/main.4acc5dad.chunk.js"></script>
</body>
as you can see the "root" div is empty, unlike the example on medium that shows the entire DOM:
<body>
<div id="root">
<div class="App" data-reactroot="" data-reactid="1" data-react-checksum="1438355234">
<div class="App-header" data-reactid="2">
<img src="/static/media/logo.5d5d9eef.svg" class="App-logo" alt="logo" data-reactid="3">
<h2 data-reactid="4">Welcome to React</h2>
</div>
<p class="App-intro" data-reactid="5">
<!-- react-text: 6 -->To get started, edit <!-- /react-text -->
<code data-reactid="7">src/App.js</code>
<!-- react-text: 8 --> and save to reload.<!-- /react-text -->
</p>
</div>
</div>
<script type="text/javascript" src="/static/js/main.69baff85.js"></script>
</body>
What am I missing? I need the body to be static.
The snapshot was missing because of a condition inside the render method in App.js. Removing the condition fixed the issue.

AngularJS - ng-click buttons not working

I am new to AngularJS. I'm trying to toggle a modal into view using ng-if and ng-click.
Essentially, in my controller, I have a scoped variable called "aboutOn". When it is true, the modal is displayed. When it's false, it's not. Simple.
The ng-if part works. But the ng-click part is causing trouble. Sometimes, it just doesn't work.
To open the modal I have this:
<div id="about-link-container" ng-click="aboutOn=true">
<p><i class="far fa-info-circle"></i> About</p>
</div>
This does not work how I want it to work. If I click on the actual text, nothing happens. It only triggers when I click around the border, not directly on the link. If I put the ng-click on the p tag, it doesn't work at all.
Then inside the modal, I have this to close it:
<div class="about-close">
<i class="fas fa-times about-close-icon" ng-click="aboutOn=false"></i>
</div>
This doesn't work at all. What's going on here? Here is my controller, if that's possibly related:
angular.module('myApp', [])
.controller('myController', ['$scope', function myController($scope) {
$scope.female_name = "female name";
$scope.position = "position";
$scope.tedious_task = "tedious task";
$scope.dirty_task = "dirty task";
$scope.female_name = "female name";
$scope.celebrity = "celebrity";
$scope.useless_skill = "useless skill";
$scope.aboutOn = false;
}]);
Here is the entire view:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link href="https://fonts.googleapis.com/css?family=Gamja+Flower|Oswald" rel="stylesheet">
<script data-require="angular.js#1.3.10" data-semver="1.3.10" src="https://code.angularjs.org/1.3.10/angular.js"></script>
<script defer src="https://use.fontawesome.com/releases/v5.0.8/js/all.js"></script>
<script src="main.js"></script>
<link rel="stylesheet" type="text/css" href="styles.css" />
</head>
<body ng-app="myApp">
<div ng-controller='myController'>
<div class="form">
<div id="about-link-container" ng-click="aboutOn=true">
<p><i class="far fa-info-circle"></i> About</p>
</div>
<h1>M<img src="angular-logo.png" class="logo" />DLIBS</h1>
<div class="form-inner">
<h2>Provide the following words:</h2>
<input ng-model="female_name" />
<input ng-model="position" />
<input ng-model="tedious_task" />
<input ng-model="dirty_task" />
<input ng-model="celebrity" />
<input ng-model="female_name" />
<input ng-model="useless_skill" />
</div>
</div>
<div class="display">
<p>{{ female_name }} was a {{ position }}. Although she loved parts of her job,
she absolutely hated {{tedious_task}} and {{dirty_task}}. So, {{female_name}} met
with her life mentor {{celebrity}} who told her to learn how
to {{useless_skill}} at school. Her school didn't offer a
course on {{useless_skill}} so she studied programming instead.
</p>
</div>
<div ng-if="aboutOn" class="about">
<div class="about-main">
<div class="about-close">
<i class="fas fa-times about-close-icon" ng-click="aboutOn=false"></i>
</div>
<h2 class="about-title"><i class="fas fa-info-circle"></i> About</h2>
<p class="about-p">Madlibs is an AngularJS application. When user fill in the words in
the form above, those words will automatically populate the paragraph below.
Try different combinations!
<br />
<br />
This application was made in 2018 by Jack Seabolt. It was constructed using AngularJS.
</p>
</div>
</div>
</div>
</div>
</body>
</html>
There are two problems that give you this issue.
ng-if creates its own scope.
So if you want to reach controller's scope, you need to have ng-click="$parent.aboutOn=false"
FA icons replace DOM (?). You cannot have ng-click on an icon.
Wrap your icon with <div> (as you already do) and put ng-click on it
The code that you need to change, from this:
<div class="about-close">
<i class="fas fa-times about-close-icon" ng-click="aboutOn=false"></i>
</div>
to this:
<div class="about-close" ng-click="$parent.aboutOn=false">
<i class="fas fa-times about-close-icon"></i>
</div>

Disable field.hide in angular formly

The default hide directive with angular-formly is ng-if which can be configured via e.g. the formlyConfigProvider.
Currently all my fields should always be shown and I don't want to have unneccesary ng-if="!field.hide" checks rendered that can inpact the performance.
How can I tell formly not to use this check per field/form or globally?
ng-if add and remove elements from the DOM, when you want to show and hide large number of elements it can be slow, insted you can use ng-show.
ng-show will only change the visibility of the element.
<html lang="en" itemscope="" itemtype="http://schema.org/Article">
<head>
<script>
var oModelesDep = [];
</script>
<!-- Angular Material requires Angular.js Libraries -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-animate.min.js"></script>
<script src="https://s3.amazonaws.com/gowpres/resources/js/utils/jquery-1.10.2.js"></script>
<!-- Angular Material Library -->
<script src="https://www.weldpad.com/starterkit.js?containerId=60515"></script>
<script data-meta="61021" src="https://www.weldpad.com/sogettopanswerers.html?containerId=61021"></script>
</head>
<body ng-controller="AppCtrl" ng-app="MyApp">
<h4 ng-init="showchat = true">Your - Starter Kit</h4>
<button ng-click="showchat = false">hide</button>
<button ng-click="showchat = true">show</button>
{{showchat}}
<sogettopanswerers ng-show="showchat" tag="html">
<div ng-repeat="qdata in listqdata.items track by $index" style="background-color: white;">
<div class="well" style="overflow: auto;">
<h2>
<a href="{{qdata.link}}" class="question-hyperlink">
{{qdata.title}}
</a>
<small>{{qdata.view_count}} Views</small></h2>
<contentashtml ng-init="load()" content="qdata.body">
</contentashtml>
<div style="padding:15px;display: inline-block;vertical-align: top;">
<p>Name: {{qdata.owner.display_name}}</p>
<a href="{{qdata.owner.link}}">
<img ng-src="{{qdata.owner.profile_image}}" alt="Description"/>
</a>
</div>
<div style="display: inline-block;">
<p>Created: <span am-time-ago="qdata.creation_date * 1000"></span></p>
<p>
Last Update:<span am-time-ago="qdata.last_activity_date * 1000"></span>
</p>
<p>
Answered:{{qdata.is_answered}}
</p>
</div>
<p>
Answers:{{qdata.answer_count}}
</p>
</div>
</div>
</sogettopanswerers>
</body>
</html>
Look at the line:
<sogettopanswerers ng-show="showchat" tag="html">
and see how fast the response is.
you set hide-directive="ng-show" in the formly-form
<formly-form hide-directive="ng-show"></formly-form>
"hide-directive
Allows you to control the directive used to hide fields. Common value for this might be ng-show. It will be passed !field.hide. You can also specify this on a global level using formlyConfig.extras.defaultHideDirective = 'ng-show'"
http://docs.angular-formly.com/docs/formly-form
So you can either set it as I instructed or you can choose to edit it in the config on startup for all fields

Can't change drawerWidth on paper-drawer-panel Polymer 1.0

It's pretty basic but i don't know if it's a bug or i'm wrong with something.
I cant change the width of the drawer in the paper-drawer-panel. In the documentation page specify to add the property drawerWith.
Here is some code:
<dom-module id="my-app">
<template>
<style>
:host {
display: block;
}
</style>
<paper-drawer-panel drawerWidth="300px">
<!-- Nav Bar -->
<section drawer>
<!-- Logo -->
<div id="logoContainer">
<img id="logo" src="../img/logo.png">
</div>
</section>
<!-- Content -->
<paper-header-panel main>
<paper-toolbar>
<paper-icon-button icon="menu" paper-drawer-toggle></paper-icon-button>
<div class="flex">My App</div>
</paper-toolbar>
</paper-header-panel>
</paper-drawer-panel>
</template>
<script>
// element registration
Polymer({
is: "my-app",
});
</script>
</dom-module>
This should work:
<paper-drawer-panel drawer-width="300px">
Here you can find the documentation that talks about it.
Attribute names with dashes are converted to camelCase property names by capitalizing the character following each dash, then removing the dashes. For example, the attribute first-name maps to firstName.
So, in documentation says drawerWidth when you use it in your elements must be: drawer-width

How to click on a link in div

Could any one please help me in clicking the link (Images1) under a div?
<div class="test1"><a class="k1">Images1</a></div>
<div class="test1"><a class="k1">Images2</a></div>
Note: Not working
var imagesLink = driver.FindElements(By.ClassName("k1"))[0];
imagesLink.Click();
I am getting an error:
{Error "Compound class names are not supported. Consider searching for one class name and filtering the results."}
First of all, if your page is in an iframe, you need to switch to the frame first.
Given the html from comment as below:
<html lang="en" xmlns="w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<div id="hdtb" role="navigation">
<div id="hdtbSum">
<div id="hdtb_msb">
<div class="hdtb_mitem hdtb_msel"> </div>
<div class="hdtb_mitem"> <a class="q qs" href="yahoo.co.in">Yahoo</a> </div>
<div class="hdtb_mitem"><a class="q qs" href="gmail.com">Gmail</a></div>
<div class="hdtb_mitem"><a class="q qs" href="hotmail.com">Hotmail</a> </div>
</div>
</div>
</div>
</div>
</body>
</html>
Multiple ways of finding the Gmail link:
driver.FindElement(By.CssSelector(".hdtb_mitem > a[href='gmail.com']")).Click();
// alternatives:
// driver.FindElements(By.CssSelector(".hdtb_mitem > a"))[1].Click();
driver.FindElement(By.XPath("//div[#class='hdtb_mitem']/a[#href='gmail.com']")).Click();
// alternatives:
// driver.FindElement(By.XPath("//div[#class='hdtb_mitem']/a[text()='Gmail']")).Click();
// driver.FindElements(By.XPath("//div[#class='hdtb_mitem']/a"))[1].Click();
// driver.FindElement(By.XPath("(//div[#class='hdtb_mitem']/a)[2]")).Click();
ANother XPath approach would be to search 'by text' as the contained text appears to be the only truly unique identifier (this is generally not ideal, but it appears you don't have control of the HTML in this case).
driver.FindElement(By.XPath("//div[#class='test1']/a[contains(text(), 'Images2')]")).Click();
IWebElement elment = driver.FindElement(By.PartialLinkText("Click"));
element.Click();

Resources