Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 9 of 9
  1. #1
    New Coder
    Join Date
    Mar 2009
    Posts
    74
    Thanks
    3
    Thanked 3 Times in 3 Posts

    Can't stop text selection when dragging object

    Ok, I have 2 example/test pages of the issue I'm experiencing. For now, I'm just trying to get it to work in a standards-compliant browser, so please view in something other than ie:

    http://raphael.no-ip.org/temp/slider_panel_test.html
    http://raphael.no-ip.org/temp/slider_panel_test2.html

    If you click and drag the orange bar up or down, it works fine on the first page. However, on the 2nd page, if you click the orange bar and drag down too fast, it starts selecting text and then the bar gets all wacky.

    My suspicion is that the bar gets messed up because the text is being selected. That occurs because if you scroll too fast, the cursor goes off the orange slider bar and starts selecting. If you scroll very slowly so that the cursor doesn't go off the bar it seems to scroll fine.

    I've tried everything I can think of to stop the text from being selected, but I'm not 100% sure if that's the cause of the problem or just a symptom of something else (maybe bubbling or something?). I tried returning false on every mouse event on the lower panel, tried creating an absolutely-positioned mask above the text in the lower panel, tried removing all text from the lower panel, tried stopPropagation(), etc., but nothing seems to work.

    If I set the mousedown/over/move on the lower panel to return false, it prevents selecting text normally by clicking the mouse & dragging, but it will STILL select the text if I click on the slider bar and drag down.

    Can anyone help? I'm stuck. Thank you!!!

  • #2
    Regular Coder
    Join Date
    Feb 2009
    Location
    NJ, USA
    Posts
    476
    Thanks
    2
    Thanked 70 Times in 69 Posts
    Hmm, I'm not sure the text being selected is the actual problem. Does it work if no text is in either of the boxes?

    I think it may either have to do with how you're going about capturing the events and using them, or your calculations themselves. I see the 'y' value from the mouse go a little nuts when moving it around a bit, but also I see the sliding bar at some points go just half way down to where the mouse is.

  • #3
    New Coder
    Join Date
    Mar 2009
    Posts
    74
    Thanks
    3
    Thanked 3 Times in 3 Posts
    It seems to happen no matter if there is text in the boxes or not.

    It only happens when moving the slider down (not up), and the smaller version (slider_panel_test.html) doesn't have the problem. I don't think the selecting of the text is the issue, but a symptom of the problem (the text selects when moving up, but it doesn't cause a problem).

    It seems to have more to do with moving the mouse off the slider and onto the lower panel.

    Basically, the way it works is when the user mousedown's on the slider, it sets a globally-defined flag to indicate the user has clicked it. This flag is cleared when the user mouseup's on the wrapper. So while the mouse is down while inside the wrapper, it tracks the onmousemove on the wrapper, and sets the height of the upper panel and the lower panel to be a number of pixels above and below the slider but it shouldn't exceed the maximum height of the wrapper.

    I don't think the math on the inner parts is wrong, I'm almost certain it's got to do with capturing the events, as you suggested. I just don't know which event I need to capture, or not capture, or cancel. It seems that something bubbles.

    I suspect that when the mouse moves below the slider as it's being slid down, the mousedown event is triggered on the lower panel. However, since I tried returning false on mousedown and pretty much every other event on the lower panel and none of that fixed it, I'm not sure what to do.

    Thank you for looking at it. Please let me know if you have any ideas!
    Last edited by Raphael; 06-17-2009 at 05:00 AM.

  • #4
    Regular Coder
    Join Date
    Feb 2009
    Location
    NJ, USA
    Posts
    476
    Thanks
    2
    Thanked 70 Times in 69 Posts
    Ok, so I played around with your slider a bit, and found a solution. I think the problem was the way you were using the events. So what I did was used yahoo's YUI javascript library for two utilities: their Event utility, and their DOM (document object model) utility. These utilities were specifically made to "flatten out" the browser inconsistencies for both browser events, and (what I used it for), finding the x/y locations of DOM elements and the mouse in the document. The methods (functions) I used in these utilities are quite small (just a few lines of code), but who would want to write them again themselves

    So anyway, I saw that global flag and all the different places event handlers were being assigned from and just had to start over because it was so hard to follow it all. I hope you don't mind, but I wrote the code to be in an object. If you are unfamiliar with objects in javascript, they are just variables and functions put into one place. Hope this doesn't confuse you, but ya get what ya pay for! (i.e. free code lol) I ended up figuring out a more sraightforward way to make it work, and the best part is, it works on IE as well! (IE7 anyway, haven't tried the others.)

    Here's the code. Follow it through, I added a lot of comments and such, and actually documented it how I usually develop my own code. Code execution actually starts from the last line in the script, which waits until the entire document is loaded, and then calls the initialization function. If you need help understanding it all though, post again and I'll try to explain as best I can. Works nicely though, and I even figured out how to disable text selection while dragging.
    Code:
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Slider</title>
        
        <!--- Include YUI utilities --->
        <script type="text/javascript" src="http://yui.yahooapis.com/combo?2.7.0/build/yahoo-dom-event/yahoo-dom-event.js"></script>
        
        <style type="text/css">
            .wrapper {
                margin-bottom: 30px;
                width: 100%;
                border: 1px solid black;
                font-size: 9px;
                font-family: Arial, Helvetica, sans-serif;
                position: relative;
                overflow: hidden;
            }
            
            .top {
                height: 400px;
                overflow: hidden;
                background-color: red;
            }
            
            .slider {
                height: 10px;
                background-color: orange;
                cursor: move;
                text-align: center;
            }
            
            .bot {
                height: 400px;
                overflow: hidden;
                background-color: lightblue;
            }
        </style>
        
        
        <script type="text/javascript">
            // Declare some global shorthand references to the Dom and Event utilities
            var Dom = YAHOO.util.Dom,
                Event = YAHOO.util.Event;
            
            
            /**
             * Singleton object class to handle all dragbar related functionality
             * 
             * @class DragBar
             * @singleton
             */
            var DragBar = new function() {
                
                this.slideBar = null;
                this.topBox = null;
                this.bottomBox = null;
                
                this.output1 = null;
                this.output2 = null;
                
                this.wrapper = null;
                this.wrapperYPos = 0;            // Stores the initial y position of the wrapper element
                this.wrapperHeight = 0;        // Stores the total height of the wrapper element
                this.sliderHeight = 0;        // Stores the height of the slider bar
                
                
                /**
                 * Initializes the DragBar object (is run when the page finishes loading)
                 * 
                 * @method init
                 * @returns {void}
                 */
                this.init = function() {
                    // Set element references
                    this.slideBar = document.getElementById( 'slider' );
                    this.topBox = document.getElementById( 'top' );
                    this.bottomBox = document.getElementById( 'bot' );
                    
                    this.output1 = document.getElementById( 'output1' );
                    this.output2 = document.getElementById( 'output2' );
                    
                    
                    // Set variables that we'll need
                    this.wrapper = document.getElementById( 'wrapper' );
                    this.wrapperYPos = Dom.getY( this.wrapper );
                    this.wrapperHeight = this.wrapper.offsetHeight;
                    this.sliderHeight = this.slideBar.offsetHeight;
                    
                    
                    // Set event handlers
                    Event.addListener( this.slideBar, 'mousedown', this.startDrag, this, true );    // Last two arguments signify "run handler in the 'this' scope"
                    Event.addListener( this.slideBar, 'mouseup', this.endDrag, this, true );            // Last two arguments signify "run handler in the 'this' scope"
                    
                    // IE-only event handler for disabling the selection of text (IE doesn't respond to stopping the mousedown event like Mozilla based browsers do)
                    Event.addListener( this.wrapper, 'selectstart', function() { return false; } );
                }
                
                
                /**
                 * Starts dragging by adding a mousemove event listener on the wrapper element to call this.drag.
                 * Is called when the slider element is mousedown'd.
                 * 
                 * @method startDrag
                 * @param {HTMLEvent} evt
                 * @returns {void}
                 */
                this.startDrag = function( evt ) {
                    Event.addListener( this.wrapper, 'mousemove', this.drag, this, true );        // Last two arguments signify "run handler in the 'this' scope"
                    
                    // Stop the event from both propagating (bubbling), and running its default behavior (selecting text)
                    Event.stopEvent( evt );
                }
                
                
                /**
                 * Method to run once dragging has been started.  
                 * Continually responds to the mousemove event from the wrapper element (after it is set from this.startDrag)
                 * 
                 * @method drag
                 * @param {HTMLEvent} evt The MouseEvent object returned from the browser from the update of the mouse position
                 * @returns {void}
                 */
                this.drag = function( evt ) {
                    var xy = Event.getXY( evt );
                    var x = xy[ 0 ],
                            y = xy[ 1 ];
                    
                    var fixedY = y - this.wrapperYPos - Math.round( this.sliderHeight / 2 );
                    if( fixedY >= 0 && fixedY <= this.wrapperHeight ) {        // Make sure the bar does not go above or below the wrapper
                        this.topBox.style.height = fixedY + 'px';
                        this.bottomBox.style.height = ( this.wrapperHeight - fixedY ) + 'px';
                    }
                    
                    this.reportValues( fixedY );
                }
                
                
                /**
                 * Ends dragging by removing the mousemove event listener from the wrapper element
                 * Is called when the slider element is mouseup'd.
                 * 
                 * @method endDrag
                 * @param {HTMLEvent} evt
                 * @returns {void}
                 */
                this.endDrag = function( evt ) {
                    Event.removeListener( this.wrapper, 'mousemove' );
                }
                
                
                /**
                 * Reports values to the textfield elements
                 * This method is used for debugging and can be removed when no longer needed.  
                 * If removing, do not forget to remove the call to this method in the this.drag function (method)
                 * 
                 * @method reportValues
                 * @param {int} yPos The y position of the mouse relative to the wrapper
                 * @returns {void}
                 */
                this.reportValues = function( yPos ) {
                    this.output1.value = yPos;
                }
            }
            
            // Initialize the DragBar object when the window has finished loading
            // *** This works the same as putting onload="DragBar.init()" in the body tag, but serves to keep all JS code in the head of the document ***
            Event.addListener( window, 'load', DragBar.init, DragBar, true );                // Last two arguments signify to run DragBar.init() in the DragBar scope
        </script>
    
    </head>
    
    
    <body>
        <div id="wrapper" class="wrapper">
            <div id="top" class="top">lorem ipsum dolor sit amet lorem ipsum dolor sit ametlorem ipsum dolor sit ametlorem ipsum dolor sit ametlorem ipsum dolor sit ametlorem ipsum dolor sit ametlorem ipsum dolor sit ametlorem ipsum dolor sit ametlorem ipsum dolor sit ametlorem ipsum dolor sit ametlorem ipsum dolor sit ametlorem ipsum dolor sit ametlorem ipsum dolor sit amet</div>
            
            <div id="slider" class="slider">X</div>
            
            <div id="bot" class="bot">dolor sit amet aidpising dolor sit amet aidpising dolor sit amet aidpising dolor sit amet aidpising dolor sit amet aidpising dolor sit amet aidpising dolor sit amet aidpising dolor sit amet aidpising dolor sit amet aidpising dolor sit amet aidpising dolor sit amet aidpising dolor sit amet aidpising dolor sit amet aidpising dolor sit amet aidpising dolor sit amet aidpising dolor sit amet aidpising dolor sit amet aidpising </div>
        </div>
        
        <div>
            <input type="text" id="output1" name="output1" style="width:400px;">
            <br><br>
        
            <input type="text" id="output2" name="output2" style="width:400px;">
            <br><br>
        </div>
    </body>
    
    </html>
    All the best buddy, and hope this helps you out.

    Greg

  • #5
    New Coder
    Join Date
    Mar 2009
    Posts
    74
    Thanks
    3
    Thanked 3 Times in 3 Posts
    Thank you, Greg! This is great!!

    -Mark

  • #6
    Regular Coder
    Join Date
    Feb 2009
    Location
    NJ, USA
    Posts
    476
    Thanks
    2
    Thanked 70 Times in 69 Posts
    You're very welcome buddy Actually had some fun trying to figure it out myself! lol. But let me know if you have any questions about how anything is working and I'd be happy to explain.

    Btw, if you want to check out the yui library, it's at http://developer.yahoo.com/yui. Definitely check out the Event utility's page for an explanation of all those addListener() calls you see in the code.

    All the best!

    Greg

  • #7
    New Coder
    Join Date
    Mar 2009
    Posts
    74
    Thanks
    3
    Thanked 3 Times in 3 Posts
    Greg,

    After looking through your code and seeing how it worked, I decided to try to figure out why mine didn't work. It seems that the problem was setting the overflow:hidden on the top and bottom divs. If I commented those lines out, the sliderbar worked. I also put in the preventDefault which keeps the text from selecting. My guess is that the Yahoo functions automatically handle the overflow issue. There are still a few other small issues with my program, and your code is so much cleaner than mine, but I was able to get it to work. Just wanted to pass that along.

    Edit for a little more info:

    Setting the overflow:hidden on the top & bottom divs made it so when I mousemoved on the wrapper it would report the layerY as the Y position within the top or bottom div, not the wrapper. So if the wrapper div was 410 high, with the top & bottom being 200px and the slider 10px and I was over the bottom div, it would only report numbers for event.layerY between 0 and 199, not 200 and 399. Removing the overflow:hidden reported the correct numbers.

    The documentation says "Returns the mouse coordinates relative to a positioned (absolute or relative) element at the time of the event" but Firefox at least seems to include overflow as well, even if the element has no position defined.

    -Mark
    Last edited by Raphael; 06-23-2009 at 10:43 PM.

  • #8
    Regular Coder
    Join Date
    Feb 2009
    Location
    NJ, USA
    Posts
    476
    Thanks
    2
    Thanked 70 Times in 69 Posts
    My guess is that the Yahoo functions automatically handle the overflow issue.
    Actually no, the yahoo functions were just for consistency of event handling. I actually maintained all of your original styles by just taking out the inline styles from the div elements, and making css classes for them instead. The css classes are defined in that first style tag near the top of the page, and are called "top" and "bottom", which both have 'overflow: hidden' in them

    But yes you are exactly right with that overflow: hidden stuff. Very weird behavior with reading event.layerY from a nested div with 'overflow: hidden' and without 'overflow: hidden'.

    That event.layerY is unfortunately only a Mozilla-based browser property tho (altho the equivalent is 'offsetY' in IE). It's also not even a DOM standard, and I think is just maintained by mozilla based browsers to be backwards compatible with Netscape 4 (ugh ). I guess the bottom line is though, using event.layerY is a largely inconsistent way of getting the mouse position relative to an element, and I wouldn't recommend using it.


    So that's why the way that I found the position of the mouse relative to the wrapper element was by first finding the Y position of the mouse on the page (returned by yahoo's getXY() function, which normalizes the process between IE and DOM compliant browsers), and then subtracting that value from the Y position of the wrapper element itself. The mousemove event is being responded to over the wrapper element and its child elements, but getting the Y value explicitly from the wrapper element makes the resulting calculation always consistent. (I also added the slider bar's height divided by 2 in that calculation to put the center of the slider bar right under the mouse.)
    Code:
    var xy = Event.getXY( evt );
    var x = xy[ 0 ],
        y = xy[ 1 ];
    
    var fixedY = y - this.wrapperYPos - Math.round( this.sliderHeight / 2 );
    Now that I think about it, I could have just done: var y = Event.getPageY( evt ), as we didnt need the x position. Oh well, interesting stuff nonetheless!

    Greg
    Last edited by Gjslick; 06-24-2009 at 04:18 AM.

  • Users who have thanked Gjslick for this post:

    Raphael (06-24-2009)

  • #9
    New Coder
    Join Date
    Mar 2009
    Posts
    74
    Thanks
    3
    Thanked 3 Times in 3 Posts
    Greg,

    I see what you're saying about the layerY vs. the DOM standard of getting the Y position from the window. The DOM way is more consistent. You have been VERY helpful!


  •  

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •