How to decrease the size of the mouse pointer in testcafe.. Below is code which I wrote but not working...
import { ClientFunction } from 'testcafe';
import { Selector } from 'testcafe';
fixture test
.page http://example.com
.beforeEach(async t => {
await disableCursor();
})
const disableCursor = ClientFunction(() => {
var styleElement = document.createElement('style');
styleElement.innerHTML = '.cursor-hammerhead-shadow-ui {width:10px; height:40px }';
document.head.appendChild(styleElement);
});
test('test', async t => {
await t.click(Selector('body > div > p:nth-child(3) > a'))
await t.click(Selector('#header > div.navigation > ul > li:nth-child(1) > a'))
});
To decrease the mouse pointer size, do the following:
Specify cursor selector: #root-hammerhead-shadow-ui.root-hammerhead-shadow-ui .cursor-hammerhead-shadow-ui.
Use the !important flag in CSS properties.
As a result, the code below replaces the default cursor with a red square:
const resizeCursor = ClientFunction(() => {
var styleElement = document.createElement('style');
styleElement.innerHTML = '#root-hammerhead-shadow-ui.root-hammerhead-shadow-ui .cursor-hammerhead-shadow-ui { background: red !important; width:40px !important; height:40px !important }';
document.head.appendChild(styleElement);
});
If you want to change the cursor to your own image, try this CSS property: background-image.
Note, since in a general case, it is hard to predict what unexpected results adding this CSS may produce, I suggest that you carefully check this prior to integrating this solution into your project.
See also: ClientFunction
Related
This question already has answers here:
Why do pseudoclasses on the host element have to be inside of the host function?
(2 answers)
Closed 3 years ago.
Is it not possible, or not allowed, to combine :host and :defined in CSS, while combining the latter with the :host() pseudoclass works?
As you can see in below example, the following
:host:defined { display: block; }
does not work, while
:host(:defined) { display: block; }
works.
class CustomElement extends HTMLElement {
constructor() {
super();
this.shadow = this.attachShadow({ mode: 'closed' });
const css = `
:host { display: none; }
:host:defined { display: block; }
`;
this.styles = document.createElement('style');
this.styles.innerHTML = css;
}
connectedCallback() {
const div = document.createElement('div');
div.innerHTML = `<code><${this.tagName.toLowerCase()}></code> connected!`;
this.shadow.appendChild(this.styles);
this.shadow.appendChild(div);
}
}
class OtherCustomElement extends HTMLElement {
constructor() {
super();
this.shadow = this.attachShadow({ mode: 'closed' });
const css = `
:host { display: none; }
:host(:defined) { display: block; }
`;
this.styles = document.createElement('style');
this.styles.innerHTML = css;
}
connectedCallback() {
const div = document.createElement('div');
div.innerHTML = `<code><${this.tagName.toLowerCase()}></code> connected!`;
this.shadow.appendChild(this.styles);
this.shadow.appendChild(div);
}
}
customElements.define('custom-element', CustomElement);
customElements.define('other-custom-element', OtherCustomElement);
<custom-element></custom-element>
<other-custom-element></other-custom-element>
The above code example on codepen: https://codepen.io/connexo/pen/GRKEGax
From the specification we can read:
The :host pseudo-class, when evaluated in the context of a shadow tree, matches the shadow tree’s shadow host. In any other context, it matches nothing
The :host() function pseudo-class has the syntax: :host( <compound-selector-list> )
When evaluated in the context of a shadow tree, it matches the shadow tree’s shadow host if the shadow host, in its normal context, matches the selector argument. In any other context, it matches nothing.
Basically, :host will match the shadow host and nothing more. You cannot combine it with any other selector While the second syntax allow you to add a selector inside ().
If you refer to the example shown in the specification:
say you had a component with a shadow tree like the following:
<x-foo class="foo">
<"shadow tree">
<div class="foo">...</div>
</>
</x-foo>
For a stylesheet within the shadow tree:
:host matches the <x-foo> element.
x-foo matches nothing.
.foo matches only the element.
.foo:host matches nothing
:host(.foo) matches the element.
Note the (2) and the (4). (2) is selecting nothing because no common selector can select outside the shadow tree. Only :host and :host() can do. The (4) is selecting nothing because :host is designed to be used alone to select the shadow host but if you want to add another selector you have to use :host() like in the (5).
Here is a basic example to illustrate:
class CustomElement extends HTMLElement {
constructor() {
super();
this.shadow = this.attachShadow({ mode: 'closed' });
const css = `
:host.box { color:red; }
`;
this.styles = document.createElement('style');
this.styles.innerHTML = css;
}
connectedCallback() {
const div = document.createElement('div');
div.innerHTML = `<code><${this.tagName.toLowerCase()}></code> connected!`;
this.shadow.appendChild(this.styles);
this.shadow.appendChild(div);
}
}
class OtherCustomElement extends HTMLElement {
constructor() {
super();
this.shadow = this.attachShadow({ mode: 'closed' });
const css = `
:host(.box) { color:red }
`;
this.styles = document.createElement('style');
this.styles.innerHTML = css;
}
connectedCallback() {
const div = document.createElement('div');
div.innerHTML = `<code><${this.tagName.toLowerCase()}></code> connected!`;
this.shadow.appendChild(this.styles);
this.shadow.appendChild(div);
}
}
customElements.define('custom-element', CustomElement);
customElements.define('other-custom-element', OtherCustomElement);
<custom-element class="box"></custom-element>
<other-custom-element class="box"></other-custom-element>
Now the question is: Why we have two kind of selectors when we can simply have :host combined with any other selector.
It's to avoid confusion and ambiguity when parsing the selector since the shadow host can only be selected by a special selector. If we write :host.foo the browser will try to match the element with .foo and :host but it will be tricky because .foo can only match an element inside the shadow tree while :host can go outside so parsing the selector to find if yes or no we have :host inside in order to consider the remaining part of the selector to match the shadow host will be tedious.
Using :host() make it easy for the browser to parse the selector and :host is a particular case of :host() with no selector.
Note: This is different from the specificity of similar pseudo-classes, like :matches() or :not(), which only take the specificity of their argument. This is because :host is affirmatively selecting an element all by itself, like a "normal" pseudo-class; it takes a selector argument for syntactic reasons (we can’t say that :host.foo matches but .foo doesn’t), but is otherwise identical to just using :host followed by a selector.
Note the we can’t say that :host.foo matches but .foo doesn’t
I have a component that has a hover animation where 4 images are rotated in a loop:
animation: changeImg-1 2.5s linear infinite;
#keyframes changeImg-1 {
0%, 100% { background-image: url('images/wel1.png'); }
25% { background-image: url('images/wel2.png'); }
50% { background-image: url('images/wel3.png'); }
75% { background-image: url('images/wel4.png'); }
}
Now I want to make this component reusable by being able to pass image strings in as props, those get assigned as css variables which then get picked up by the animation.
I got as far as defining the css variable with a path in a computed property which is then used in the css:
computed: {
userStyle () {
return {
'--myBackground': `url(${require('#/components/ImagesInText/images/wel1.png')})`,
}
}
},
CSS:
.image {
background:var(--myBackground);
}
What I can't get to work is to pick up an image path from props and use it in the computed property...
props: {
image: { type: String, default: '#/components/ImagesInText/images/wel1.png' },
},
If I do this below I get en error: Cannot find module '#/components/ImagesInText/images/wel1.png'"
computed: {
userStyle () {
return {
'--myBackground': `url(${require( this.image )})`,
}
}
},
When using a variable path/filename, require needs some assistance. You must hard code at least the first portion of the path as a string.
For example, this works:
const filename = 'wel1.png';
require('#/components/ImagesInText/images/' + filename); // <-- The string is needed
This works too:
const path = 'components/ImagesInText/images/wel1.png';
require('#/' + path); // <-- This is good enough too
But this would not work:
const fullpath = '#/components/ImagesInText/images/wel1.png';
require(fullpath); // <-- No. Can't infer the path from a variable only
As the way mentioned In this thread. it's working perfectly for me.
<div class="register-img" :style="{'background-image': 'url(' + require('#/assets/images/register-image.png') + ')'}"></div>
I'm trying to change the opacity of my ion-backdrop from 0.08 to 0.33.
I've tried:
ion-backdrop {
opacity: 0.33 !important;
}
and setting $popover-ios-background: rgba(0, 0, 0, 0.33);.
Setting the value on ion-backdrop does work but since it's important, it doesn't animate the fade out.
How can I change the opacity of the backdrop?
I know I am a bit late to this party, but now with Ionic 5, you have a CSS selector that will do the job for you. That is mentioned in their documentation as well.
So basically all you could do is, initialize the modal and style it in your SCSS file.
This is my component.ts file:
import { Component } from '#angular/core';
import { ModalController } from '#ionic/angular';
// ModalComponent is just a normal angular component, your path may vary
import { ModalComponent } from '../../modals/modal.component';
#Component({
selector: 'some-component',
templateUrl: './some-component.component.html',
styleUrls: ['./some-component.component.scss']
})
export class SomeComponentComponent {
constructor(
private modalController: ModalController,
) { }
async presentModal() {
const modal = await this.modalController.create({
component: ModalComponent,
cssClass: 'modal-class'
});
return await modal.present();
}
}
and my component.scss file:
.modal-class {
ion-backdrop {
--backdrop-opacity: 0.33;
}
}
I’ve do it using the cssClass property in alertController (Ionic 4)
async alertError(message: string) {
const alert = await this.alertController.create({
cssClass: 'alertClass',
animated: true,
header: 'Error',
message,
buttons: ['OK']
});
await alert.present();
}
ion-alert {
&.alertClass{
background: rgb(0,0,0,.8);
}
}
I am guessing that this ion-backdrop question it's related with the Ionic Alert Controller. If that is the case than you need to apply CSS inside the global.scss (Ionic 3) file or theme\variable.scss (Ionic 4/5). This is required because ion-backdrop lives in the app as an Ionic Global Component.
Therefore find the mentioned file inside your Ionic project. It's usually in this directory app > src > global.scss.
Now let's suppose that we have this Alert Controller instanciated in some page class.
...
async serviceErrorAlert() {
const alert = await this.alertController.create({
cssClass: 'try-again-alert',
...
});
await alert.present();
}
...
As you can see this Alert Controller haves a CSS class of try-again-alert.
So to add all custom CSS that you want just go the style file and add your own style.
global.scss (Ionic 3):
.try-again-alert {
--background: rgba(55, 67, 77, 0.9);
}
theme\variable.scss (Ionic 4/5):
I strongly recommend you to use CSS background attribute and rgba() property. With this approach you can now choose the color that you want (first three numbers) and the opacity of the color (fourth number).
There is currently an open issue about this in Ionic's GitHub. The only workaround listed there that doesn't break the animation is long and complex - too much to list here. A direct link to the solution: https://github.com/ionic-team/ionic/issues/9105#issuecomment-375010398
I only managed to do it in Ionic 5 by using background: rgba() property with a desired alpha value.
Page where modal is called
openModal(): Promise<void> {
return this.modalCtrl.create({
component: ModalPage,
backdropDismiss: true,
cssClass: 'custom-class'
}).then(modal => {
modal.present();
});
}
app/theme/variable.css
.custom-class {
background: rgba(0,0,0,0.8); /*black with 0.8 opacity*/
}
Is there a TamperMonkey equivalent to GreaseMonkey's GM_addStyle method for adding CSS?
In GreaseMonkey, you can add a bunch of CSS properties to multiple elements like so:
GM_addStyle("body { color: white; background-color: black; } img { border: 0; }");
To do the equivalent in TamperMonkey, I'm currently having to do the following:
function addGlobalStyle(css) {
var head, style;
head = document.getElementsByTagName('head')[0];
if (!head) { return; }
style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = css;
head.appendChild(style);
}
addGlobalStyle('body { color: white; background-color: black; }');
This works, but is there a built-in GM_addStyle equivalent for TamperMonkey that saves me from having to repeat this on every script?
Version 4.0 or +, update of 2018
ReferenceError: GM_addStyle is not defined
You need to create your own GM_addStyle function, like this :
// ==UserScript==
// #name Example
// #description Usercript with GM_addStyle method.
// ==/UserScript==
function GM_addStyle(css) {
const style = document.getElementById("GM_addStyleBy8626") || (function() {
const style = document.createElement('style');
style.type = 'text/css';
style.id = "GM_addStyleBy8626";
document.head.appendChild(style);
return style;
})();
const sheet = style.sheet;
sheet.insertRule(css, (sheet.rules || sheet.cssRules || []).length);
}
//demo :
GM_addStyle("p { color:red; }");
GM_addStyle("p { text-decoration:underline; }");
document.body.innerHTML = "<p>I used GM_addStyle.</p><pre></pre>";
const sheet = document.getElementById("GM_addStyleBy8626").sheet,
rules = (sheet.rules || sheet.cssRules);
for (let i=0; i<rules.length; i++)
document.querySelector("pre").innerHTML += rules[i].cssText + "\n";
DEPRECATED
If GM_addStyle(...) doesn't work, check if you have #grant GM_addStyle header.
Like this :
// ==UserScript==
// #name Example
// #description See usercript with grant header.
// #grant GM_addStyle
// ==/UserScript==
GM_addStyle("body { color: white; background-color: black; } img { border: 0; }");
According to the TamperMonkey documentation, it supports GM_addStyle directly, like GreaseMonkey does. Check your include/match rules are correct, then add this demo code to the top of your userscript:
GM_addStyle('* { font-size: 99px !important; }');
console.log('ran');
I just tested it on a fresh userscript in Chrome 35 and it worked as expected. If you have any other #grant rule, you will need to add one for this function, otherwise it should be detected and granted automatically.
If somebody is interessted, I changed the code so you don't have to write "!important" after every css rule. Of course this only works, if you use the function instead of GM_addStyle.
function addGlobalStyle(css) {
var head, style;
head = document.getElementsByTagName('head')[0];
if (!head) { return; }
style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = css.replace(/;/g, ' !important;');
head.appendChild(style);
}
The output of this "addGlobalStyle('body { color: white; background-color: black; }');",
will be "body { color: white !important; background-color: black !important; }');"
I was having this same issue. I tried all the fixes, making sure to have // #grant GM_addStyle in the header. My issue was, I also had the default code's // #grant none at the bottom of the header. Removed that piece and now all my css works. Hope this helps someone else if they are stuck on this too.
I have GreaseMonkey scripts that run in various engines, this covers all varieties:
--snip--
// #include *.someplace.com/*
// #grant GM_addStyle
// ==/UserScript==
(function() {
'use strict';
let myCSS=(<><![CDATA[
body { background: #121212 url(https://somewhere.github.io/boss/imgs/bg.jpg) top left repeat !important; }
]]></>).toString();
// workaround for various GreaseMonkey engines
if (typeof GM_addStyle != "undefined") {
GM_addStyle(myCSS);
} else if (typeof PRO_addStyle != "undefined") {
PRO_addStyle(myCSS);
} else if (typeof addStyle != "undefined") {
addStyle(myCSS);
} else {
var node = document.createElement("style");
node.type = "text/css";
node.appendChild(document.createTextNode(myCSS));
var heads = document.getElementsByTagName("head");
if (heads.length > 0) {
heads[0].appendChild(node);
} else {
// no head yet, stick it whereever
document.documentElement.appendChild(node);
}
}
This excerpt from a script that was written in 2018 that is known to work in GreasMonkey, TamperMonkey, and ViolentMonkey (probably others too). Adapt the above mentioned addGlobalStyle(css) functions and you should be good to go anywhere .
My 2 cents on the topic, thought it might be interesting to someone, I modified PaarCrafter's answer, to allow multiple lines without brackets:
usage:
addGlobalStyle`
button.special {
position: relative;
top: -3em;
}
`
// string templating ('Template literals') works anyways
addGlobalStyle(`p {
color: red;
}`)
// Still works
addGlobalStyle('p {color: red;}')
Modified version:
function addGlobalStyle(css = '') {
let target = documnet.head || document.body;
let style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = (css || arguments.length ? arguments[0][0] : '').replaceAll(';', ' !important;');
target.append(style);
}
Here is the solution used by https://userstyles.org, for example when you click on the link "Install style as userscript" on a style page like https://userstyles.org/styles/23516/midnight-surfing-global-dark-style:
if (typeof GM_addStyle != "undefined") {
GM_addStyle(css);
} else if (typeof PRO_addStyle != "undefined") {
PRO_addStyle(css);
} else if (typeof addStyle != "undefined") {
addStyle(css);
} else {
var node = document.createElement("style");
node.type = "text/css";
node.appendChild(document.createTextNode(css));
var heads = document.getElementsByTagName("head");
if (heads.length > 0) {
heads[0].appendChild(node);
} else {
// no head yet, stick it whereever
document.documentElement.appendChild(node);
}
}
Note: the code will work on Greasemonkey 4 as well as similar addons. I don't use Tampermonkey which is not open source but this answer may help other users finding this question. It will try to use several built-in functions of different addons before using a pure JavaScript solution. You may only need the code from the else block.
The "if" condition checking the head tag length may not be needed if you are sure the page has a head tag and you can add the node to the head like this instead : document.head.appendChild(node);. However I noticed sometimes there is a JavaScript error saying the head is undefined or null depending on the method used, for example on facebook.com while logged out (at least when using // #run-at document-start which is required for a dark theme to avoid flickering). So checking the length can be useful in this case.
If you want to use multiple lines of CSS code, you can create the css variable with backticks like this:
var css = `
CODE HERE
`;
Update: I just saw this solution is also used in another answer but there was no source mentioned. However you may see the console error document.documentElement is null but it can be solved with a MutationObserver workaround: https://github.com/greasemonkey/greasemonkey/issues/2996#issuecomment-906608348.
How to detect via js if any sort of transition is being applied to the element right now?
Short story of my problem:
I have a situation where I'm firing a function on the `transitionend` event, but sometimes the element doesn't have any *transition* being applied (because in Firefox, for example, the user might click some element rapidly which makes the transition goes crazy and stop working) so I want to know when it doesn't work and just fire the function myself, skipping the `transitionend`. I am trying to avoid ugly solutions..
You can use the Web Animation API for that, notably the Element#getAnimations method, which will return a list of Animation objects applied on the Element. These will include Web Animations (from .animate()), CSS #keyframes animations, and CSS transitions.
document.querySelectorAll("a").forEach((el) => {
el.onmouseenter = (evt) => {
const animations = el.getAnimations(); // we could also ask to look in the subtree
// we're only interested in CSS transitions
const transitions = animations.filter((anim) => anim instanceof CSSTransition);
console.log(transitions.length
? transitions.map((anim) => anim.transitionProperty )
: "no transition"
);
};
});
a:hover {
color: red;
opacity: 0.5;
}
.color-transition {
transition: color 1s;
}
.color-and-opacity-transition {
transition: color 1s, opacity 5s;
}
/*SO-only*/.as-console-wrapper { max-height: 110px !important }
<b>Hover these anchors to log their applied transitions.</b><br>
<a class="color-transition">color transition</a><br>
<a class="color-and-opacity-transition">color & opacity transition</a><br>
<a>no transition</a>
You can listen to transitionstart, transitionend, and, transitioncancel events. To tell if some element is under transition. However, you cannot know if some element will start a transition (even if it has transition-delay: 0s) using this code:
/** #type {Map<HTMLElement, number>} */
const transitionCounter = new Map();
/** #type {(() => void)[]} */
const waitingTransition = [];
const incReference = (counter, target) => {
if (counter.has(target)) {
counter.set(target, counter.get(target) + 1);
} else {
counter.set(target, 1);
}
};
const desReference = (counter, target) => {
if (!counter.has(target)) {
return;
} else if (counter.get(target) === 1) {
counter.delete(target);
} else {
counter.set(target, counter.get(target) - 1);
}
};
document.addEventListener('transitionstart', event => {
const { target } = event;
incReference(transitionCounter, target);
const onFinish = event => {
if (event.target !== target) return;
desReference(transitionCounter, target);
target.removeEventListener('transitioncancel', onFinish);
target.removeEventListener('transitionend', onFinish);
[...waitingTransition].forEach(listener => { listener(); });
};
target.addEventListener('transitioncancel', onFinish);
target.addEventListener('transitionend', onFinish);
});
/**
* #param {HTMLElement} element
* #returns {boolean}
*/
const isUnderTransition = function (element) {
const parents = [];
for (let i = element; i; i = i.offsetParent) parents.push(i);
return Array.from(transitionCounter.keys()).some(running => parents.includes(running));
};
/**
* #param {HTMLElement} element
* #returns {Promise<void>}
*/
const waitTransitionEnd = async function (element) {
if (!isUnderTransition(element)) return Promise.resolve();
return new Promise(resolve => {
waitingTransition.push(function listener() {
if (isUnderTransition(element)) return;
waitingTransition.splice(waitingTransition.indexOf(listener), 1);
resolve();
});
});
};
As specified by W3C Editor's Draft - CSS Transition
The ‘transitionend’ event occurs at the completion of the transition. In the case where a transition is removed before completion, such as if the transition-property is removed, then the event will not fire.
So, I think there's not a valid simple way to solve this problem. The solution is left to the implementation (the browser) which decide if it does or doesn't render the transition at all.
Maybe, a solution could be to attach a listener to the element that fires the transition and after a specific elapsed time it checks if the transitioned element has the required CSS attributes set, and if those attributes aren't set as expected you can run your function by yourself.