It seems something is wrong in my code, but I cannot figure what. I am trying to call pointerlockchange api to disable pointer and use mouse as fps controller. The problem is that always pointerlockerror is triggered and I get a Error setting pointer lock! message.
The function is called inside $(document).ready. Here's the code.:
function initLock() {
var havePointerLock = 'pointerLockElement' in document || 'mozPointerLockElement' in document || 'webkitPointerLockElement' in document;
if ( havePointerLock ) {
var element = document.body;
console.log('Cathing element', element);
var pointerlockchange = function ( event ) {
if ( document.pointerLockElement === element || document.mozPointerLockElement === element || document.webkitPointerLockElement === element ) {
controls.enabled = true;
console.log('Pointer lock enabled!')
}
else {
controls.enabled = false;
}
}
var pointerlockerror = function ( event ) {
console.log('Error setting pointer lock!');
}
// Hook pointer lock state change events
document.addEventListener( 'pointerlockchange', pointerlockchange, false );
document.addEventListener( 'mozpointerlockchange', pointerlockchange, false );
document.addEventListener( 'webkitpointerlockchange', pointerlockchange, false );
document.addEventListener( 'pointerlockerror', pointerlockerror, false );
document.addEventListener( 'mozpointerlockerror', pointerlockerror, false );
document.addEventListener( 'webkitpointerlockerror', pointerlockerror, false );
// Ask the browser to lock the pointer
element.requestPointerLock = element.requestPointerLock || element.mozRequestPointerLock || element.webkitRequestPointerLock;
if ( /Firefox/i.test( navigator.userAgent ) ) {
var fullscreenchange = function ( event ) {
if ( document.fullscreenElement === element || document.mozFullscreenElement === element || document.mozFullScreenElement === element ) {
document.removeEventListener( 'fullscreenchange', fullscreenchange );
document.removeEventListener( 'mozfullscreenchange', fullscreenchange );
element.requestPointerLock();
}
}
document.addEventListener( 'fullscreenchange', fullscreenchange, false );
document.addEventListener( 'mozfullscreenchange', fullscreenchange, false );
element.requestFullscreen = element.requestFullscreen || element.mozRequestFullscreen || element.mozRequestFullScreen || element.webkitRequestFullscreen;
element.requestFullscreen();
}
else {
element.requestPointerLock();
}
}
else {
console.log('Sorry, pointer lock cannot be set.');
}
}
Found solution by myself. The problem is that requestPointerLock cannot be called automatically, it should be called from the user's callback, for example, when clicking on something.
Related
When I click a menu item on Underscores that takes me to another page, then use the browser back button, the navigation is shown. I want to remove the active state from the navigation when an item is clicked. I've pasted what I've tried, but I get:
Uncaught TypeError: links.addEventListener is not a function
JS
( function() {
const siteNavigation = document.getElementById( 'navigation-outer' ); //changed from 'site-navigation' in default Underscores
// Return early if the navigation doesn't exist.
if ( ! siteNavigation ) {
return;
}
const button = siteNavigation.getElementsByTagName( 'button' )[ 0 ];
// Return early if the button doesn't exist.
if ( 'undefined' === typeof button ) {
return;
}
const menu = siteNavigation.getElementsByTagName( 'ul' )[ 0 ];
// Hide menu toggle button if menu is empty and return early.
if ( 'undefined' === typeof menu ) {
button.style.display = 'none';
return;
}
if ( ! menu.classList.contains( 'nav-menu' ) ) {
menu.classList.add( 'nav-menu' );
}
// Toggle the .toggled class and the aria-expanded value each time the button is clicked.
button.addEventListener( 'click', function() {
siteNavigation.classList.toggle( 'toggled' );
if ( button.getAttribute( 'aria-expanded' ) === 'true' ) {
button.setAttribute( 'aria-expanded', 'false' );
body.className = body.className.replace( ' noscroll', '' ); // My modification
} else {
button.setAttribute( 'aria-expanded', 'true' );
body.className += ' noscroll'; // My modification
}
} );
// Remove the .toggled class and set aria-expanded to false when the user clicks outside the navigation.
document.addEventListener( 'click', function( event ) {
const isClickInside = siteNavigation.contains( event.target );
if ( ! isClickInside ) {
siteNavigation.classList.remove( 'toggled' );
button.setAttribute( 'aria-expanded', 'false' );
}
} );
// Get all the link elements within the menu.
const links = menu.getElementsByTagName( 'a' );
// Close menu on item click.
links.addEventListener( 'click', function( event ) {
siteNavigation.classList.remove( 'toggled' );
button.setAttribute( 'aria-expanded', 'false' );
body.className = body.className.replace( ' noscroll', '' );
} );
// Get all the link elements with children within the menu.
const linksWithChildren = menu.querySelectorAll( '.menu-item-has-children > a, .page_item_has_children > a' );
// Toggle focus each time a menu link is focused or blurred.
for ( const link of links ) {
link.addEventListener( 'focus', toggleFocus, true );
link.addEventListener( 'blur', toggleFocus, true );
}
// Toggle focus each time a menu link with children receive a touch event.
for ( const link of linksWithChildren ) {
link.addEventListener( 'touchstart', toggleFocus, false );
}
/**
* Sets or removes .focus class on an element.
*/
function toggleFocus() {
if ( event.type === 'focus' || event.type === 'blur' ) {
let self = this;
// Move up through the ancestors of the current link until we hit .nav-menu.
while ( ! self.classList.contains( 'nav-menu' ) ) {
// On li elements toggle the class .focus.
if ( 'li' === self.tagName.toLowerCase() ) {
self.classList.toggle( 'focus' );
}
self = self.parentNode;
}
}
if ( event.type === 'touchstart' ) {
const menuItem = this.parentNode;
event.preventDefault();
for ( const link of menuItem.parentNode.children ) {
if ( menuItem !== link ) {
link.classList.remove( 'focus' );
}
}
menuItem.classList.toggle( 'focus' );
}
}
}() );
Try catching elements by id with getElementById instead of getElementsByTagName.
Ok, I'm just confused (and probably too tired for my own good to be working on this now...). I'm trying to get my three.js application to execute different functions for different mouse events when the mouse is over a particular type of entity in my scene. Events of type "mousemove" are working perfectly, and the
console.log( 'INTERSECTED.isGraphElement: ', INTERSECTED.isGraphElement, 'MouseEvent: ', mouse.type );
statement registers all the mouseEvents that I'm listening for, which include "mousemove", "click", "dblclick", "wheel" and "oncontextmenu". It's also detecting INTERSECTED.isGraphElement objects as intented.
Nevertheless, when transformGraphElement() runs, only the "mousemove" events are registered inside the transformGraphElement function. Even the commented-out test line of code that should console out "Got the dblclick!" doesn't run. It appears that no other mouse events that I'm listening for are being detected here.
Things I've tried inside the transformGraphElement function:
Swap out the dblclick event for one of the other types I'm listening for. No dice.
Comment out the line for handling mousemove events. No dice.
Test to see if there's a bug in my obj.referent.transformOnDblClick() function. I only learned that it wasn't being called at all (and runs perfectly when I associate it to the "mousemove" event).
Trying different valid syntaxes for my 'if' statements. No dice.
Trying a bit of refactoring on the mouseEventHandler() function. No dice.
Here's my relevant code:
function render() {
mouseEventHandler( transformGraphElement, unTransformGraphElement );
requestAnimationFrame( render );
renderer.render(scene, entities.cameras.perspCamera );
}
function mouseEventHandler( fn, revFn ){
// update the picking ray with the camera and mouse position
ray.setFromCamera( mouse, entities.cameras.perspCamera );
// calculate objects intersecting the picking ray
var intersects = ray.intersectObjects( scene.children );
if ( intersects && intersects[0] && intersects[0].object ){
if ( intersects[ 0 ].object != INTERSECTED ){ // if there's an intersected object
if ( INTERSECTED ) { // and if a previous INTERSECTED object exists:
revFn( INTERSECTED, mouse ); // restore the previous intersected object to its non-intersected state.
}
INTERSECTED = intersects[ 0 ].object; // set the currently intersected object to INTERSECTED
fn( INTERSECTED, mouse ); // transform the currentlY INTERSECTED object.
}
console.log( 'INTERSECTED.isGraphElement: ', INTERSECTED.isGraphElement, 'MouseEvent: ', mouse.type );
}
}
function transformGraphElement( obj, mouse ){
// Check if INTERSECTED is a Graph Element, and if so, invoke it's transform function.
if ( mouse.type === "mousemove" && obj.isGraphElement ) { obj.referent.transformOnMouseOver(); }
//if ( mouse.type === 'dblclick' ) { console.log('Got the dblclick Inside!') }
if ( mouse.type === 'dblclick' && obj.isGraphElement ) { obj.referent.transformOnDblClick(); ) }
}
function unTransformGraphElement( obj, mouse ){
// Check if INTERSECTED is a Graph Element, and if so, revert it to it's pre-mouseEvent state.
if ( mouse.type === "mousemove" && obj.isGraphElement ) { obj.referent.transformOnMouseOut(); }
if ( mouse.type === 'dblclick' ) { console.log('Got the dblclick Out!') }
}
I'm wondering if its some sort of default behavior or override that I'm running into, but shouldn't the event.preventDefault() line be handling that? (This code below runs before the code above):
var ray = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var INTERSECTED; // Object closest to the camera
function onMouse( event ) {
event.preventDefault();
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
mouse.type = ( event.type );
}
function listenFor(){
document.addEventListener( 'click', onMouse, false );
document.addEventListener( 'mousemove', onMouse, false );
document.addEventListener( 'mousedown', onMouse, false );
document.addEventListener( 'dblclick', onMouse, false )
document.addEventListener( 'wheel', onMouse, false );
document.addEventListener( 'contextmenu', onMouse, false );
}
listenFor();
Console.log( mouse.type ) from inside the onMouse() function registers all the mouse events I'm listening for perfectly.
I've been banging my head against this for 3 hours. I'm hoping it's something stupid simple that I'm missing because I'm in a bad mood. All help is welcome, and let me know if there's any missing code important to providing a useful answer... I don't think so but again, in a bad mood...
Thanks for your help!
Well the next morning I got up and figured out the problem. The issue was with the logic in the code:
if ( intersects[ 0 ].object != INTERSECTED ){ // if there's an intersected object
if ( INTERSECTED ) { // and if a previous INTERSECTED object exists:
revFn( INTERSECTED, mouse ); // restore the previous intersected object to its non-intersected state.
}
INTERSECTED = intersects[ 0 ].object; // set the currently intersected object to INTERSECTED
fn( INTERSECTED, mouse ); // transform the currentlY INTERSECTED object.
}
console.log( 'INTERSECTED.isGraphElement: ', INTERSECTED.isGraphElement, 'MouseEvent: ', mouse.type );
}
Non-mousemove events could not be passed. My solution was to wrap this section with another conditional if (mouse.type === 'mousemove') and then have additional conditionals for other event types. Here's the whole, with a bit of refactoring to make the whole easier to reason about:
var ray = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var INTERSECTED; // Object closest to the camera
function onMouse( event ) {
event.preventDefault();
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
mouseEventHandler( event /*, transformGraphElement, unTransformGraphElement */ );
}
function listenFor(){
document.addEventListener( 'click', onMouse, false );
document.addEventListener( 'mousemove', onMouse, false );
document.addEventListener( 'mousedown', onMouse, false );
document.addEventListener( 'dblclick', onMouse, false )
document.addEventListener( 'wheel', onMouse, false );
document.addEventListener( 'contextmenu', onMouse, false );
}
listenFor();
/* ... */
function render() {
requestAnimationFrame( render );
renderer.render(scene, entities.cameras.perspCamera );
}
function mouseEventHandler( event /* , fn, revFn */ ){
// update the picking ray with the camera and mouse position
ray.setFromCamera( mouse, entities.cameras.perspCamera );
// calculate objects intersecting the picking ray
var intersects = ray.intersectObjects( scene.children );
// if there's at least one intersected object...
if ( intersects && intersects[0] && intersects[0].object ){
// Check if the event is a mouse move, INTERSECTED exists and we're sitting on the same INTERSECTED object as the last time this function ran...
if ( event.type === 'mousemove' ){
// Check if the current top-level intersected object is the previous INTERSECTED
if ( intersects[ 0 ].object != INTERSECTED ){
// ... if there is a previous INTERSECTED
if ( INTERSECTED ) {
// restore the previous INTERSECTED to it's previous state.
unTransformGraphElementOnMouseOut( INTERSECTED, event );
}
// set the currently intersected object to INTERSECTED
INTERSECTED = intersects[ 0 ].object;
// and transform it accordingly.
transformGraphElementOnMouseOver( INTERSECTED, event );
}
}
// Check if the mouse event is a doubble click
if ( event.type === 'dblclick' ){
// If the currently intersected object is INTERSECTED
if ( intersects[ 0 ].object === INTERSECTED ){
// select it.
transformGraphElementOnSelect( INTERSECTED, event );
}
// If the currently intersected object is not INTERSECTED
if ( intersects[ 0 ].object !== INTERSECTED ){
// If there is a previous INTERSECTED
if ( INTERSECTED )
// restore it to its unselected state.
unTransformGraphElementOnUnselect( INTERSECTED, event );
}
}
INTERSECTED && console.log( 'INTERSECTED.isGraphElement: ', INTERSECTED.isGraphElement, 'MouseEvent: ', event.type );
}
}
function transformGraphElementOnMouseOver( obj, event ){
if ( obj.isGraphElement ) { obj.referent.transformOnMouseOver(); }
}
function unTransformGraphElementOnMouseOut( obj, event ){
if ( obj.isGraphElement ) { obj.referent.transformOnMouseOut(); }
}
function transformGraphElementOnSelect( obj, event ){
if ( obj.isGraphElement ) { obj.referent.transformOnDblClick(); }
}
function unTransformGraphElementOnUnselect( obj, event ){
if ( obj.isGraphElement ) { obj.referent.unTransformOnDblClick(); }
}
The logic of mouseEventHandler() still has some issues, but the core frustration is handled. Some additional refactoring worth mentioning:
It wasn't necessary to add the event.type to mouse.
I moved the mouseEventHandler() call from render() to onMouse(). That ensures that mouse events are registered only once.
I got rid of the 'fn'/'revFn' callbacks in mouseEventHandler() as they just made things more confusing than they need to be.
Event doesn't need to be a parameter for the transform functions, that'll be coming out shortly.
Hope this helps someone.
I am using the snippet found at to http://tympanus.net/Development/MorphingSearch/ enlarge a search box when it is clicked..
<script>
(function() {
var morphSearch = document.getElementById( 'morphsearch' ),
searchlink = document.getElementById( 'mybtn' ),
input = morphSearch.querySelector( 'input.morphsearch-input' ),
ctrlClose = morphSearch.querySelector( 'span.morphsearch-close' ),
isOpen = isAnimating = false,
// show/hide search area
toggleSearch = function(evt) {
// return if open and the input gets focused
if( evt.type.toLowerCase() === 'focus' && isOpen ) return false;
if( isOpen ) {
classie.remove( morphSearch, 'open' );
// trick to hide input text once the search overlay closes
// todo: hardcoded times, should be done after transition ends
if( input.value !== '' ) {
setTimeout(function() {
classie.add( morphSearch, 'hideInput' );
setTimeout(function() {
classie.remove( morphSearch, 'hideInput' );
input.value = '';
}, 300 );
}, 500);
}
input.blur();
}
else {
classie.add( morphSearch, 'open' );
}
isOpen = !isOpen;
};
// events
searchlink.addEventListener( 'click', toggleSearch );
ctrlClose.addEventListener( 'click', toggleSearch );
// esc key closes search overlay
// keyboard navigation events
document.addEventListener( 'keydown', function( ev ) {
var keyCode = ev.keyCode || ev.which;
if( keyCode === 27 && isOpen ) {
toggleSearch(ev);
}
} );
/***** for demo purposes only: don't allow to submit the form *****/
morphSearch.querySelector( 'button[type="submit"]' ).addEventListener( 'click', function(ev) { ev.preventDefault(); } );
})();
</script>
All works great but as the search box is in a fixed header the page content jumps to the top when the link is clicked.
Previously when this has happened i have inserted the following...
event.preventDefault();
I can't work out where to insert this in this code though, can anyone help?
You insert it in the function that is called by the click event handlers
toggleSearch = function(evt) {
if (evt.type === 'click') {
evt.preventDefault();
}
// rest of code
}
searchlink.addEventListener( 'click', toggleSearch );
ctrlClose.addEventListener( 'click', toggleSearch );
Note the condition, it's to make sure the default action isn't prevented when the keydown handler is calling the same function
Normal Navigation of Tab button get stuck over this text box in Mozilla Firefox browser. On Tab key press it's not moving to next element. Working fine in IE and Chrome. Can anyone assist me to fix this?
<div class="editor-field">
<div>
<input id="Rentaljeepshop" class="ui-autocomplete-input" type="text" value="Budget Rent A Car" name="Rentaljeepshop" maxlength="50" isautocomplete="true" autocomplete="off" role="textbox" aria-autocomplete="list" aria-haspopup="true"></input>
/div>
</div>
My Jquery is:
(function( $, undefined ) {
$.widget( "ui.autocomplete", {
options: {
appendTo: "body",
delay: 300,
minLength: 1,
position: {
my: "left top",
at: "left bottom",
collision: "none"
},
source: null
},
_create: function() {
var self = this,
doc = this.element[ 0 ].ownerDocument;
this.element
.addClass( "ui-autocomplete-input" )
.attr( "autocomplete", "off" )
// TODO verify these actually work as intended
.attr({
role: "textbox",
"aria-autocomplete": "list",
"aria-haspopup": "true"
})
.bind( "keydown.autocomplete", function( event ) {
if ( self.options.disabled ) {
return;
}
var keyCode = $.ui.keyCode;
switch( event.keyCode ) {
case keyCode.PAGE_UP:
self._move( "previousPage", event );
break;
case keyCode.PAGE_DOWN:
self._move( "nextPage", event );
break;
case keyCode.UP:
self._move( "previous", event );
// prevent moving cursor to beginning of text field in some browsers
event.preventDefault();
break;
case keyCode.DOWN:
self._move( "next", event );
// prevent moving cursor to end of text field in some browsers
event.preventDefault();
break;
case keyCode.ENTER:
case keyCode.NUMPAD_ENTER:
// when menu is open or has focus
if ( self.menu.element.is( ":visible" ) ) {
event.preventDefault();
}
//passthrough - ENTER and TAB both select the current element
case keyCode.TAB:
if ( !self.menu.active ) {
return;
}
self.menu.select( event );
break;
case keyCode.ESCAPE:
self.element.val( self.term );
self.close( event );
break;
default:
// keypress is triggered before the input value is changed
clearTimeout( self.searching );
self.searching = setTimeout(function() {
// only search if the value has changed
if ( self.term != self.element.val() ) {
self.selectedItem = null;
self.search( null, event );
}
}, self.options.delay );
break;
}
})
.bind( "focus.autocomplete", function() {
if ( self.options.disabled ) {
return;
}
self.selectedItem = null;
self.previous = self.element.val();
})
.bind( "blur.autocomplete", function( event ) {
if ( self.options.disabled ) {
return;
}
clearTimeout( self.searching );
// clicks on the menu (or a button to trigger a search) will cause a blur event
self.closing = setTimeout(function() {
self.close( event );
self._change( event );
}, 150 );
});
this._initSource();
this.response = function() {
return self._response.apply( self, arguments );
};
this.menu.element.hide();this.menu = $( "<ul></ul>" )
.addClass( "ui-autocomplete" )
.appendTo( $( this.options.appendTo || "body", doc )[0] )
// prevent the close-on-blur in case of a "slow" click on the menu (long mousedown)
.mousedown(function( event ) {
// clicking on the scrollbar causes focus to shift to the body
// but we can't detect a mouseup or a click immediately afterward
// so we have to track the next mousedown and close the menu if
// the user clicks somewhere outside of the autocomplete
var menuElement = self.menu.element[ 0 ];
if ( event.target === menuElement ) {
setTimeout(function() {
$( document ).one( 'mousedown', function( event ) {
if ( event.target !== self.element[ 0 ] &&
event.target !== menuElement &&
!$.ui.contains( menuElement, event.target ) ) {
self.close();
}
});
}, 1 );
}
// use another timeout to make sure the blur-event-handler on the input was already triggered
setTimeout(function() {
clearTimeout( self.closing );
}, 13);
})
.menu({
focus: function( event, ui ) {
var item = ui.item.data( "item.autocomplete" );
if ( false !== self._trigger( "focus", null, { item: item } ) ) {
// use value to match what will end up in the input, if it was a key event
if ( /^key/.test(event.originalEvent.type) ) {
self.element.val( item.value );
}
}
},
selected: function( event, ui ) {
var item = ui.item.data( "item.autocomplete" ),
previous = self.previous;
// only trigger when focus was lost (click on menu)
if ( self.element[0] !== doc.activeElement ) {
self.element.focus();
self.previous = previous;
}
if ( false !== self._trigger( "select", event, { item: item } ) ) {
self.element.val( item.value );
}
self.close( event );
self.selectedItem = item;
},
blur: function( event, ui ) {
// don't set the value of the text field if it's already correct
// this prevents moving the cursor unnecessarily
if ( self.menu.element.is(":visible") &&
( self.element.val() !== self.term ) ) {
self.element.val( self.term );
}
}
})
.zIndex( this.element.zIndex() + 1 )
// workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781
.css({ top: 0, left: 0 })
.hide()
.data( "menu" );
if ( $.fn.bgiframe ) {
this.menu.element.bgiframe();
}
},
destroy: function() {
this.element
.removeClass( "ui-autocomplete-input" )
.removeAttr( "autocomplete" )
.removeAttr( "role" )
.removeAttr( "aria-autocomplete" )
.removeAttr( "aria-haspopup" );
this.menu.element.remove();
$.Widget.prototype.destroy.call( this );
},
What is this code means ? I guess the code given below is not complete.
case keyCode.TAB:
if ( !self.menu.active ) {
return;
}
self.menu.select( event );
break;
Suggestion: Check your firebug console, if there will be any kind of JavaScript error, it will be displayed over there.
I do not want my Fire Event function in the following code to return a boolean. I think this can be done by removing the event listener but I am not sure how.
function FireEvent( ElementId, EventName )
{
if( document.getElementById(ElementId) != null )
{
if( document.getElementById( ElementId ).fireEvent )
{ document.getElementById( ElementId ).fireEvent( 'on' + EventName ); }
else {
var evObj = document.createEvent( 'Events' ); evObj.initEvent( EventName, true, false ); document.getElementById( ElementId ).dispatchEvent( evObj ); } } }
Please help. Thanks :)