2011-08-03 17 views
17

आईई के लिए पिक्सेल में कर्सर या टेक्स्ट स्थिति प्राप्त करें आईई मुझे इनपुट तत्व में एक टेक्स्ट रेंज बनाने की अनुमति देता है, जिस पर मैं getBoundingClientRect() पर कॉल कर सकता हूं और स्थिति को किसी निश्चित चरित्र या कर्सर/कैरेट में पिक्सेल में प्राप्त कर सकता हूं। क्या किसी अन्य चरित्र में पिक्सल में किसी विशेष वर्ण की स्थिति प्राप्त करने का कोई तरीका है?इनपुट तत्व

var input = $("#myInput")[0]; 
var pixelPosition = null; 
if (input.createTextRange) 
{ 
    var range = input.createTextRange(); 
    range.moveStart("character", 6); 
    pixelPosition = range.getBoundingClientRect(); 
} 
else 
{ 
    // Is there any way to create a range on an input's value? 
} 

मैं jQuery का उपयोग कर रहा हूं, लेकिन मुझे संदेह है कि यह मेरी स्थिति को हल करने में सक्षम होगा। मैं एक शुद्ध जावास्क्रिप्ट समाधान की उम्मीद करता हूं, अगर कोई है, लेकिन jQuery उत्तरों का स्वागत है।

+0

आपकी तलाश के अनुसार हो सकता है: http: // stackoverflow।कॉम/प्रश्न/4085312/jquery-get-the-कर्सर-स्थिति-का-पाठ-इन-इनपुट-बिना ब्राउज़र-विशिष्ट-कोड – Jesse

+0

मुझे पूरा यकीन है कि संक्षिप्त उत्तर "नहीं" है, लेकिन मेरे पास नहीं है अभी विस्तृत करने या शोध करने का समय मिला है। –

+0

@ टिमडाउन मैंने अनुरोधित व्यवहार को लागू किया है। चूंकि आपको श्रेणियों के साथ बहुत अनुभव मिला है (आपकी रंगी परियोजना के कारण), क्या आप इसे देख सकते हैं? –

उत्तर

2

मैं अंतराल बिल्कुल तैनात से बाहर एक छिपे हुए नकली इनपुट बना दी और इनपुट करने के लिए इसी तरह स्टाइल। मैंने उस अवधि के पाठ को उस चरित्र तक इनपुट के मूल्य पर सेट किया जिसकी स्थिति मैं ढूंढना चाहता हूं। मैं इनपुट से पहले अवधि डालें और हो यह ऑफसेट है:

function getInputTextPosition(input, charOffset) 
{ 
    var pixelPosition = null; 
    if (input.createTextRange) 
    { 
     var range = input.createTextRange(); 
     range.moveStart("character", charOffset); 
     pixelPosition = range.getBoundingClientRect(); 
    } 
    else 
    { 
     var text = input.value.substr(0, charOffset).replace(/ $/, "\xa0"); 
     var sizer = $("#sizer").insertBefore(input).text(text); 
     pixelPosition = sizer.offset(); 
     pixelPosition.left += sizer.width(); 
     if (!text) sizer.text("."); // for computing height. An empty span returns 0 
     pixelPosition.bottom = pixelPosition.top + sizer.height(); 
    } 
    return pixelPosition 
} 

मेरी आकार मापक अवधि के लिए सीएसएस:

#sizer 
{ 
    position: absolute; 
    display: inline-block; 
    visibility: hidden; 
    margin: 3px; /* simulate padding and border without affecting height and width */ 
    font-family: "segoe ui", Verdana, Arial, Sans-Serif; 
    font-size: 12px; 
} 
+1

पर डेमो मैंने इस कार्यान्वयन के बाद एक परीक्षण केस बनाया है: http://jsfiddle.net/WYzfm/। दिए गए स्टार्ट ऑफ़सेट के बावजूद लौटा ऑफसेट हमेशा बराबर होता है। साथ ही, इस फ़ंक्शन को एक तत्व, '# sizer' परिभाषित करने की आवश्यकता है। यदि आप इस फ़ंक्शन को विकसित करना जारी रखना चाहते हैं, तो मैं '$ ('

') का उपयोग करने की अनुशंसा करता हूं, ताकि यह उस दस्तावेज़ पर निर्भर न हो जहां यह एम्बेड किया गया हो। –

+1

@RobW - एक ही इनपुट में एक अलग वर्ण ऑफ़सेट के लिए 'शीर्ष' या 'नीचे' क्यों भिन्न होगा? मैं केवल 'बाएं' को बदलने की अपेक्षा करता हूं, जो यह करता है: http://jsfiddle.net/gilly3/WYzfm/2/। – gilly3

15

डेमो
मैंने एक ऐसा कार्य लिखा है जो अपेक्षित व्यवहार करता है। यहां एक बहुत विस्तृत प्रदर्शन पैनल पाया जा सकता है: फिडल: http://jsfiddle.net/56Rep/5/
डेमो में इंटरफ़ेस आत्म-व्याख्यात्मक है।

प्रश्न में अनुरोध के रूप में मेरे समारोह में लागू किया जाएगा इस प्रकार है कार्यक्षमता:
    var pixelPosition = getTextBoundingRect(input, 6)

समारोह निर्भरता
अपडेट किया गया: समारोह शुद्ध जावास्क्रिप्ट, और किसी भी पर निर्भर नहीं है प्लगइन या ढांचे!
फ़ंक्शन मानता है कि getBoundingClientRect विधि मौजूद है। पाठ श्रेणियों का उपयोग तब किया जाता है जब वे समर्थित होते हैं। अन्यथा, कार्यक्षमता मेरे फ़ंक्शन तर्क का उपयोग करके हासिल की जाती है।

समारोह तर्क
कोड में ही कई टिप्पणियां हैं। यह हिस्सा एक गहन विस्तार से चला जाता है।

  1. एक अस्थायी<div> कंटेनर बनाया जाता है।
  2. 1 - 3 <span> तत्व बनाए गए हैं। प्रत्येक अवधि में इनपुट के मान का एक हिस्सा होता है (ऑफसेट 0 से selectionStart, selectionStartselectionEnd, selectionEnd स्ट्रिंग के अंत तक, केवल दूसरा अवधि औसत होता है)।
  3. इनपुट तत्व से कई महत्वपूर्ण शैली गुणों को इन <div> और <span> टैग पर कॉपी किया गया है। केवल महत्वपूर्ण शैली गुणों की प्रतिलिपि बनाई गई है। उदाहरण के लिए, color कॉपी नहीं किया गया है, क्योंकि यह किसी भी तरह के चरित्र के ऑफसेट को प्रभावित नहीं करता है। # 1
  4. <div>टेक्स्ट नोड (इनपुट का मूल्य) की सटीक स्थिति पर स्थित है। सीमाओं और पैडिंग को ध्यान में रखा जाता है, यह सुनिश्चित करने के लिए कि अस्थायी <div>सही स्थित है।
  5. एक चर बनाया गया है, जिसमें div.getBoundingClientRect() का वापसी मूल्य है।
  6. अस्थायी <div> हटा दिया गया है, जब तक पैरामीटर debug सत्य पर सेट है।
  7. फ़ंक्शन ClientRect ऑब्जेक्ट देता है। इस ऑब्जेक्ट के बारे में अधिक जानकारी के लिए, this page देखें। top, left, right, bottom, height और width: demo भी संपत्तियों की एक सूची दिखाता है।

# 1: getBoundingClientRect() (और कुछ मामूली गुण) इनपुट तत्व की स्थिति को निर्धारित करने के लिए प्रयोग किया जाता है। फिर, पाठ नोड की वास्तविक स्थिति प्राप्त करने के लिए, पैडिंग और सीमा चौड़ाई जोड़ दी जाती है।

ज्ञात मुद्दे
असंगतता होने की केवल मामले का सामना करना पड़ा था, जब getComputedStylefont-family के लिए एक गलत मान दिया: एक पृष्ठ पर एक font-family संपत्ति परिभाषित नहीं गया है, computedStyle कोई गलत मान देता है (यहां तक ​​कि Firebug इस सामना कर रहा है मुद्दा; पर्यावरण: लिनक्स, फ़ायरफ़ॉक्स 3.6.23, फ़ॉन्ट "सैन्स सेरिफ़")।

डेमो में दिखाई देने के रूप में, स्थिति कभी-कभी थोड़ी दूर होती है (लगभग शून्य, हमेशा 1 पिक्सेल से छोटी)।

तकनीकी प्रतिबंधों को स्क्रिप्ट को स्थानांतरित होने पर स्क्रिप्ट को टेक्स्ट खंड के सटीक ऑफसेट होने से रोकता है, उदाहरण के लिए जब इनपुट फ़ील्ड में पहला दृश्यमान वर्ण पहले मान के वर्ण के बराबर नहीं होता है।

कोड

// @author Rob W  http://stackoverflow.com/users/938089/rob-w 
// @name    getTextBoundingRect 
// @param input   Required HTMLElement with `value` attribute 
// @param selectionStart Optional number: Start offset. Default 0 
// @param selectionEnd Optional number: End offset. Default selectionStart 
// @param debug   Optional boolean. If true, the created test layer 
//       will not be removed. 
function getTextBoundingRect(input, selectionStart, selectionEnd, debug) { 
    // Basic parameter validation 
    if(!input || !('value' in input)) return input; 
    if(typeof selectionStart == "string") selectionStart = parseFloat(selectionStart); 
    if(typeof selectionStart != "number" || isNaN(selectionStart)) { 
     selectionStart = 0; 
    } 
    if(selectionStart < 0) selectionStart = 0; 
    else selectionStart = Math.min(input.value.length, selectionStart); 
    if(typeof selectionEnd == "string") selectionEnd = parseFloat(selectionEnd); 
    if(typeof selectionEnd != "number" || isNaN(selectionEnd) || selectionEnd < selectionStart) { 
     selectionEnd = selectionStart; 
    } 
    if (selectionEnd < 0) selectionEnd = 0; 
    else selectionEnd = Math.min(input.value.length, selectionEnd); 

    // If available (thus IE), use the createTextRange method 
    if (typeof input.createTextRange == "function") { 
     var range = input.createTextRange(); 
     range.collapse(true); 
     range.moveStart('character', selectionStart); 
     range.moveEnd('character', selectionEnd - selectionStart); 
     return range.getBoundingClientRect(); 
    } 
    // createTextRange is not supported, create a fake text range 
    var offset = getInputOffset(), 
     topPos = offset.top, 
     leftPos = offset.left, 
     width = getInputCSS('width', true), 
     height = getInputCSS('height', true); 

     // Styles to simulate a node in an input field 
    var cssDefaultStyles = "white-space:pre;padding:0;margin:0;", 
     listOfModifiers = ['direction', 'font-family', 'font-size', 'font-size-adjust', 'font-variant', 'font-weight', 'font-style', 'letter-spacing', 'line-height', 'text-align', 'text-indent', 'text-transform', 'word-wrap', 'word-spacing']; 

    topPos += getInputCSS('padding-top', true); 
    topPos += getInputCSS('border-top-width', true); 
    leftPos += getInputCSS('padding-left', true); 
    leftPos += getInputCSS('border-left-width', true); 
    leftPos += 1; //Seems to be necessary 

    for (var i=0; i<listOfModifiers.length; i++) { 
     var property = listOfModifiers[i]; 
     cssDefaultStyles += property + ':' + getInputCSS(property) +';'; 
    } 
    // End of CSS variable checks 

    var text = input.value, 
     textLen = text.length, 
     fakeClone = document.createElement("div"); 
    if(selectionStart > 0) appendPart(0, selectionStart); 
    var fakeRange = appendPart(selectionStart, selectionEnd); 
    if(textLen > selectionEnd) appendPart(selectionEnd, textLen); 

    // Styles to inherit the font styles of the element 
    fakeClone.style.cssText = cssDefaultStyles; 

    // Styles to position the text node at the desired position 
    fakeClone.style.position = "absolute"; 
    fakeClone.style.top = topPos + "px"; 
    fakeClone.style.left = leftPos + "px"; 
    fakeClone.style.width = width + "px"; 
    fakeClone.style.height = height + "px"; 
    document.body.appendChild(fakeClone); 
    var returnValue = fakeRange.getBoundingClientRect(); //Get rect 

    if (!debug) fakeClone.parentNode.removeChild(fakeClone); //Remove temp 
    return returnValue; 

    // Local functions for readability of the previous code 
    function appendPart(start, end){ 
     var span = document.createElement("span"); 
     span.style.cssText = cssDefaultStyles; //Force styles to prevent unexpected results 
     span.textContent = text.substring(start, end); 
     fakeClone.appendChild(span); 
     return span; 
    } 
    // Computing offset position 
    function getInputOffset(){ 
     var body = document.body, 
      win = document.defaultView, 
      docElem = document.documentElement, 
      box = document.createElement('div'); 
     box.style.paddingLeft = box.style.width = "1px"; 
     body.appendChild(box); 
     var isBoxModel = box.offsetWidth == 2; 
     body.removeChild(box); 
     box = input.getBoundingClientRect(); 
     var clientTop = docElem.clientTop || body.clientTop || 0, 
      clientLeft = docElem.clientLeft || body.clientLeft || 0, 
      scrollTop = win.pageYOffset || isBoxModel && docElem.scrollTop || body.scrollTop, 
      scrollLeft = win.pageXOffset || isBoxModel && docElem.scrollLeft || body.scrollLeft; 
     return { 
      top : box.top + scrollTop - clientTop, 
      left: box.left + scrollLeft - clientLeft}; 
    } 
    function getInputCSS(prop, isnumber){ 
     var val = document.defaultView.getComputedStyle(input, null).getPropertyValue(prop); 
     return isnumber ? parseFloat(val) : val; 
    } 
} 
+0

+1। यह प्लगइन के रूप में पैकेजिंग के लिए उपयुक्त है या यहां तक ​​कि एक वितरित पुस्तकालय में भी एक बहुत ही मजबूत समाधान है। लेकिन, मुझे जरूरत से ज्यादा रास्ता। निजी तौर पर, मैं सीएसएस में जो कुछ भी कर रहा हूं वह करना पसंद करता हूं। यह जटिलता को बहुत कम करता है। साथ ही, मुझे यह मानने में प्रसन्नता हो रही है कि कॉलर सभी मान्यताओं को करने के बजाय अच्छे तर्क पारित करेगा। कॉलर को कूड़ेदान में गुजरने के परिणामों का सामना करना पड़ता है। एक और विचार: समारोह में बेकिंग की बजाय शुरुआत और अंत स्थिति पाने की क्षमता, आप फ़ंक्शन को सरल बना सकते हैं और फ़ंक्शन को दो बार कॉल करके दोनों पदों को प्राप्त कर सकते हैं। – gilly3

+0

@ gilly3 मैंने पूर्णता के लिए एंड ऑफ़सेट शामिल किया है। बाएं किनारे ऑफसेट को 'बाएं' के माध्यम से प्राप्त किया जा सकता है, और दाएं किनारे ऑफसेट की गणना '.left + .width' का उपयोग करके की जा सकती है। प्रदर्शन कारणों से, फ़ंक्शन को कॉल करना बेहतर है, और फ़ंक्शन को दो बार कॉल करने के बजाय '.left + .width' का उपयोग करें। 'चयन स्टार्ट' और 'चयन अंत' का समर्थन करना एक बड़ा सौदा नहीं है। ** सीएसएस के बारे में **: सीएसएस मुख्य रूप से ऑफसेट-प्रभावित गुणों की प्रतिलिपि बनाने के लिए उपयोग किया जाता है। अगर मेरे 'फंक्शन लॉजिक' सेक्शन के बारे में कुछ अस्पष्ट नहीं है, तो कृपया इसे इंगित करें, ताकि मैं इसे बेहतर बना सकूं। –

+0

उन सरलीकरणों के लिए जिनके बारे में मैं बात कर रहा था, मेरे द्वारा पोस्ट किए गए उत्तर को देखें - यही वह था जिसे मैंने उपयोग किया था। वैसे, आपका कोड आईई में काम नहीं करता है। इसे ठीक करने के लिए, आपको अपनी सीमा को पहले पतन करने की आवश्यकता है और फिर अंत - अंतराल के अंतर से अंत को स्थानांतरित करने की आवश्यकता है। यह अद्यतन देखें: http://jsfiddle.net/gilly3/56Rep/4/ – gilly3

3

मई 2014 अद्यतन: अविश्वसनीय रूप से हल्के और मजबूत textarea-caret-positionघटक पुस्तकालय अब <input type="text"> का भी समर्थन करता है, अन्य सभी उत्तरों को अप्रचलित रूप से प्रस्तुत करता है।

एक डेमो पर RTL समर्थन के प्रति प्रेरणा के लिए रोब डब्ल्यू करने के लिए http://jsfiddle.net/dandv/aFPA7/

धन्यवाद उपलब्ध है।

1

2016 अपडेट:contenteditable संपत्ति का उपयोग करने के लिए एक और आधुनिक HTML5 आधारित समाधान होगा।

<div contenteditable="true"> <!-- behaves as input --> 
    Block of regular text, and <span id='interest'>text of interest</span> 
</div> 

अब हम jquery offset() का उपयोग कर अवधि की स्थिति पा सकते हैं। और निश्चित रूप से, <span> टैग को आगे या गतिशील रूप से सम्मिलित किया जा सकता है।

संबंधित मुद्दे