Dart how to determine the source of an on click event - event-handling

I have some radio buttons; some action takes place when a button is clicked. How can I implement code that simulates a click event and detect if the 'click' was caused by the simulation or the user?
chkBxB12 = querySelector('#B12');
chkBxB12.onClick.listen(chkBxB12Clicked);
void chkBxB12Clicked(Event e) {
// do something
// if mouse click or tap then
// do some-additional-thing
}
foo() {
chkBxB12.click()
{
[edit]
I modified JAre's example to better suit my needs (and reduce 8 methods to handle my four radio buttons to two - Robert's example reminded me of that need). Here's the modified code that allows determination if a target button is clicked (or tapped) by hardware of software and the identity of the button.
import 'dart:html';
void main() {
querySelectorAll('input[type="radio"]').onClick.listen(foo);
foo(new MouseEvent("synthetic")); // parameter is
}
void foo(MouseEvent event) {
if (event.target is Element){
Element target = event.target;
if(target.id == 'radio1')
print('${event.type} target ${target.id}');
else if(target.id == 'radio2')
print('${event.type} target ${target.id}');
}
else {
if(event.type == "synthetic") print(event.type);
else print("It didn't work!");
}
}
Output is as expected:
synthetic
click target radio1
click target radio2

You can get the EventTarget with e.target (link) and check it's type
import 'dart:html';
void main() {
querySelector("#sample_text_id")
..text = "Click me!"
..onClick.listen(foo);
foo(new MouseEvent("synthetic"));
}
void foo(MouseEvent event) {
if (event.target is Element){
Element target = event.target;
target.text = "Hello dart";
}
else print(event.type);
}

this is my little example:
.dart
import 'dart:html';
void main() {
querySelectorAll('input[type="radio"]').onClick.listen((MouseEvent e) {
print('detail=${e.detail}');
print('offset=${e.offset}');
print('client=${e.client}');
print('layer=${e.layer}');
print('screen=${e.screen}');
});
RadioButtonInputElement radio1 = querySelector('#radio1');
radio1.click();
}
.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Test click</title>
<script async type="application/dart" src="test_click.dart"></script>
<script async src="packages/browser/dart.js"></script>
</head>
<body>
<input type="radio" name="radio" id="radio1">
<input type="radio" name="radio" id="radio2">
</body>
</html>
You asked for how to detect if the user has clicked or it was "simulated":
The output of this test is:
detail=0
offset=Point(-13, -11)
client=Point(0, 0)
layer=Point(0, 0)
screen=Point(0, 0)
detail=1
offset=Point(3, 1)
client=Point(16, 12)
layer=Point(16, 12)
screen=Point(1936, 73)
The first output is from a "simulated" click. The second one from a real click. I'd check for detail (I wasn't able to tell what this value means) or offset property on the event.
This should answer your question.
EDIT
After doing some research you should use the detail property:
https://developer.mozilla.org/en-US/docs/Web/API/event.detail#Notes
For mouse events [...] the detail property indicates how many times the mouse has been clicked [...]
So this should be the best solution.
Regards
Robert

Related

How can I safely manipulate DOM in a StencilJS component?

I'm trying to safely remove a DOM node from a component made whit StencilJS.
I've put the removing code in a public method - It's what I need.
But, depending on which moment this method is called, I have a problem. If it is called too early, it don't have the DOM node reference yet - it's undefined.
The code below shows the component code (using StencilJS) and the HTML page.
Calling alert.dismiss() in page script is problematic. Calling the same method clicking the button works fine.
There is a safe way to do this remove()? Do StencilJS provide some resource, something I should test or I should wait?
import {
Component,
Element,
h,
Method
} from '#stencil/core';
#Component({
tag: 'my-alert',
scoped: true
})
export class Alert {
// Reference to dismiss button
dismissButton: HTMLButtonElement;
/**
* StencilJS lifecycle methods
*/
componentDidLoad() {
// Dismiss button click handler
this.dismissButton.addEventListener('click', () => this.dismiss());
}
// If this method is called from "click" event (handler above), everything is ok.
// If it is called from a script executed on the page, this.dismissButton may be undefined.
#Method()
async dismiss() {
// Remove button from DOM
// ** But this.dismissButton is undefined before `render` **
this.dismissButton.remove();
}
render() {
return (
<div>
<slot/>
<button ref={el => this.dismissButton = el as HTMLButtonElement} >
Dismiss
</button>
</div>
);
}
}
<!DOCTYPE html>
<html lang="pt-br">
<head>
<title>App</title>
</head>
<body>
<my-alert>Can be dismissed.</my-alert>
<script type="module">
import { defineCustomElements } from './node_modules/my-alert/alert.js';
defineCustomElements();
(async () => {
await customElements.whenDefined('my-alert');
let alert = document.querySelector('my-alert');
// ** Throw an error, because `this.dismissButton`
// is undefined at this moment.
await alert.dismiss();
})();
</script>
</body>
</html>
There are multiple ways to delete DOM nodes in Stencil.
The simplest is to just call remove() on the element, like any other element:
document.querySelector('my-alert').remove();
Another would be to have a parent container that manages the my-alert message(s). This is especially useful for things like notifications.
#Component({...})
class MyAlertManager {
#Prop({ mutable: true }) alerts = ['alert 1'];
removeAlert(alert: string) {
const index = this.alerts.indexOf(alert);
this.alerts = [
...this.alerts.slice(0, index),
...this.alerts.slice(index + 1, 0),
];
}
render() {
return (
<Host>
{this.alerts.map(alert => <my-alert text={alert} />)}
</Host>
);
}
}
There are other options and which one to choose will depend on the exact use case.
Update
In your specific case I would just render the dismiss button conditionally:
export class Alert {
#State() shouldRenderDismissButton = true;
#Method()
async dismiss() {
this.shouldRenderDismissButton = false;
}
render() {
return (
<div>
<slot/>
{this.shouldRenderDismissButton && <button onClick={() => this.dismiss()}>
Dismiss
</button>
</div>
);
}
}
Generally I would not recommend manually manipulating the DOM in Stencil components directly since that could lead to problems with the next renders since the virtual DOM is out of sync with the real DOM.
And if you really need to wait for the component to render you can use a Promise:
class Alert {
loadPromiseResolve;
loadPromise = new Promise(resolve => this.loadPromiseResolve = resolve);
#Method()
async dismiss() {
// Wait for load
await this.loadPromise;
// Remove button from DOM
this.dismissButton.remove();
}
componentDidLoad() {
this.loadPromiseResolve();
}
}
I previously asked a question about waiting for the next render which would make this a bit cleaner but I don't think it's easily possible at the moment. I might create a feature request for this in the future.

Changing the document title in React?

I'm trying to update the title of the document in a React app. I have very simple needs for this. The title is essentially used to put the Total component on display even when you're on a different tab.
This was my first instinct:
const React = require('react');
export default class Total extends React.Component {
shouldComponentUpdate(nextProps) {
//otherstuff
document.title = this.props.total.toString();
console.log("Document title: ", document.title);
return true;
}
render() {
document.title = this.props.total;
return (
<div className="text-center">
<h1>{this.props.total}</h1>
</div>
);
}
}
I thought this would just update the document.title every time this component was rendered, but it doesn't appear to do anything.
Not sure what I'm missing here. Probably something to do with how React runs this function - maybe somewhere that the document variable isn't available?
EDIT:
I'm starting a bounty for this question, as I still haven't found any solution. I've updated my code to a more recent version.
A weird development is that the console.log does print out the title I'm looking for. But for some reason, the actual title in the tab isn't updating. This issue is the same across Chrome, Safari, and Firefox.
I now use react-helmet for this purpose, as it allows to customize different meta tags and links, and it also supports SSR.
import { Helmet } from 'react-helmet'
const Total = () => (
<div className="text-center">
<Helmet>
<meta charSet="utf-8" />
<title>{this.props.total}</title>
</Helmet>
<h1>{this.props.total}</h1>
</div>
)
Original answer: there's actually a package by gaeron for this purpose, but in a declarative way:
import React, { Component } from 'react'
import DocumentTitle from 'react-document-title'
export default class Total extends Component {
render () {
return (
<DocumentTitle title={this.props.total}>
<div className='text-center'>
<h1>{this.props.total}</h1>
</div>
</DocumentTitle>
)
}
}
Inside your componentDidMount() function in App.js (or wherever), simply have:
componentDidMount() {
document.title = "Amazing Page";
}
The reason this works is anywhere in your react project you have access to the Js global scope. Go ahead and type window in your sites console. Basically everything there you will be able to access in your React project.
I think webpack-dev-server runs in an iframe mode by default:
https://webpack.github.io/docs/webpack-dev-server.html#iframe-mode
So that might be why your attempts to set the title are failing. Try setting the inline option to true on webpack-dev-server, if you haven't already.
If the react-document-title package isn't working for you, the quick'n'dirty way to do that would be in a lifecycle method, probably both componentDidMount and componentWillReceiveProps (you can read more about those here):
So you would do something like:
const React = require('react');
export default class Total extends React.Component {
// gets called whenever new props are assigned to the component
// but NOT during the initial mount/render
componentWillReceiveProps(nextProps) {
document.title = this.props.total;
}
// gets called during the initial mount/render
componentDidMount() {
document.title = this.props.total;
}
render() {
return (
<div className="text-center">
<h1>{this.props.total}</h1>
</div>
);
}
}
There is a better way of dynamically changing document title with react-helmet package.
As a matter of fact you can dynamically change anything inside <head> tag using react-helmet from inside your component.
const componentA = (props) => {
return (
<div>
<Helmet>
<title>Your dynamic document/page Title</title>
<meta name="description" content="Helmet application" />
</Helmet>
.....other component content
);
}
To change title, meta tags and favicon dynamically at run time react-helmet provides a simple solution. You can also do this in componentDidMount using the standard document interface. In the example below I am using the same code for multiple sites, so helmet is looking for favicon and title from an environment variable
import { Helmet } from "react-helmet";
import { getAppStyles } from '../relative-path';
import { env } from '../relative-path';
<Helmet>
<meta charSet="utf-8" />
<title>{pageTitle[env.app.NAME].title}</title>
<link rel="shortcut icon" href={appStyles.favicon} />
</Helmet>

Wicket - Panel not replaced the second time

I have a simple modal popup which can be closed by a button. On the page where modal is going to be displayed there is a placeholder with an id. When the page first loads i addOrReplace an empty Panel. Then, in response to proper action I swap this panel with the modal panel. Then I close the panel and swap it again with another empty panel. Everything works fine. But here's the strange thing - when I do this the second time, the modal panel opens normally, but when I press close it does not close even if it is replaced by the new empty panel and added to the target - but it worked before! When I press same button again, everything crashes not being able to find component for markup for the modal (but there should be no markup for it anymore!)
I have thought about it all day yet I still haven't found the reason for all this. Any help would be much appreciated.
private void swapToDummyPopupContainer() {
currentPopupContainer = new DummyPanel("popupContainer");
addOrReplace(currentPopupContainer);
}
private void swapToCreationPopupContainer(final FCalendarEvent event) {
EventCreationPopup popup = new EventCreationPopup("popupContainer", event) {
private static final long serialVersionUID = 965466080498078142L;
#Override
public void onDataSubmit(AjaxRequestTarget target) {
AvailabilityDTO model = getModel();
event.setTitle(model.getDescription());
pushNewEventToModel(model);
availabilityMapping.put(event.getId(), model);
FCalendarEventActions.addEvent(target, fcalendar, event);
swapToDummyPopupContainer();
target.add(currentPopupContainer);
}
#Override
public void onCancel(AjaxRequestTarget target) {
swapToDummyPopupContainer();
target.add(currentPopupContainer);
}
};
currentPopupContainer = popup;
addOrReplace(currentPopupContainer);
}
#Override
protected void onRangeSelection(AjaxRequestTarget target, Date startDate, Date endDate,
boolean isAllDay) {
final FCalendarEvent event = new FCalendarEvent();
swapToCreationPopupContainer(event);
target.add(currentPopupContainer);
}
As for the markup there's
<wicket:container wicket:id="popupContainer" />
in the parent panel and both panels to be swapped are defined like this
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd">
<wicket:panel>
</wicket:panel>
</html>
at the end of the modal markup (before closing tag) there is javascript, but I don't believe it has anything to do with it:
<script type="text/javascript">
var $modal = $('#eventCreationPopup');
$modal.modal('setting', {
selector : {
close : '',
approve : '',
deny : ''
}
});
$modal.modal("show");
</script>
You cannot attach an JavaScript event to
<wicket:container wicket:id="popupContainer" />
because wicket:container has no real tag in the final markup you generate by Wicket. setOutputMarkupPlaceholderTag(true) has no effect in this case.
Change it to the DIV tag:
<div wicket:id="popupContainer"></div>

How to cancel redirection of page with click?

I have a redirection code (working fine) in several pages as follow :
<meta http-equiv="refresh" content ="42; url=http://my url.html#">
But i want the automatic redirection to be cancelled or delayed if the user clicks in any point of the page.
What code should i use ?
Thanks
Here is the code for click on page anywhere:
<script>
$('html').click(function () {
alert("ok"); // Do whatever you want here, just replace. Since i dont know your code. So i can help you till this point.
});
</script>
Remove you <meta http-equiv="refresh"> from Documents <head></head>
Insert a simple JS-Timer and use window.location to refer the User after the Page loaded.
var timeVar = setInterval(function () {myTimer()}, 1000);
var t = 9;
function myTimer() {
if (t>0) {
document.getElementById("timer").innerHTML = t;
t--;
}
else { window.location = 'what/ever/you/are/workingon.html'; }
}
function StopFunction() { clearInterval(timeVar); }
<p>Redirecting in <span id="timer" style="font-weight: bold;">10</span>Seconds.</p>
<p onclick="StopFunction();" style="cursor: pointer; ">Cancel!</p>
The User will load your page and when not canceling the Timer be redirected to the Page you defined. If stopping the Timer the user wont be redirected and stay on the Page. br
Try just to empty cache, it should work.

PhoneGap catch 'Go' pressed event for iPhone

I want to know how to catch 'Go' pressed event using PhoneGap.
I have a form with 2 input fields. How do I catch when user has pressed "Go" in keyboard. I tried butting the input fields in a Form and added a onSubmit method. And in my Js I have the method.
function onLoginSubmit(e){
console.log('submit pressed');
e.preventDefault();
}
But looks like the method is never called. What is the best way of doing it?
An example would be great.
try following code:
<head>
<script language="javascript">
function show()
{
if(window.event.keyCode == 13)
{
alert(window.event.keyCode);
}
}
</script></head>
<body>
<input type="text" name="textfields" onKeyPress="show();">
</body>