การเลือกข้อความในองค์ประกอบ (คล้ายกับการเน้นด้วยเมาส์ของคุณ)


424

ฉันต้องการให้ผู้ใช้คลิกที่ลิงค์จากนั้นจะเลือกข้อความ HTML ในองค์ประกอบอื่น ( ไม่ใช่อินพุต)

โดย "เลือก" ฉันหมายถึงวิธีเดียวกับที่คุณจะเลือกข้อความโดยการลากเมาส์ของคุณไป นี่เป็นเรื่องที่น่าสนใจสำหรับการวิจัยเพราะทุกคนพูดถึง "เลือก" หรือ "ไฮไลต์" ในแง่อื่น ๆ

เป็นไปได้ไหม รหัสของฉัน:

HTML:

<a href="javascript:" onclick="SelectText('xhtml-code')">Select Code</a>
<code id="xhtml-code">Some Code here </code>

JS:

function SelectText(element) {
    $("#" + element).select();
}

ฉันขาดอะไรบางอย่างที่ชัดเจนโจ๋งครึ่ม?


ที่เกี่ยวข้องกับการเลือกช่วงข้อความที่ครอบคลุมหลายองค์ประกอบ
HeyJude

คำตอบ:


612

Javascript ธรรมดา

function selectText(node) {
    node = document.getElementById(node);

    if (document.body.createTextRange) {
        const range = document.body.createTextRange();
        range.moveToElementText(node);
        range.select();
    } else if (window.getSelection) {
        const selection = window.getSelection();
        const range = document.createRange();
        range.selectNodeContents(node);
        selection.removeAllRanges();
        selection.addRange(range);
    } else {
        console.warn("Could not select text in node: Unsupported browser.");
    }
}

const clickable = document.querySelector('.click-me');
clickable.addEventListener('click', () => selectText('target'));
<div id="target"><p>Some text goes here!</p><p>Moar text!</p></div>
<p class="click-me">Click me!</p>

นี่คือการสาธิตการทำงาน สำหรับบรรดาของคุณที่กำลังมองหาปลั๊กอิน jQuery ผมทำหนึ่งในบรรดาเกินไป


jQuery (คำตอบดั้งเดิม)

ฉันได้พบวิธีแก้ปัญหานี้ในหัวข้อนี้ ฉันสามารถแก้ไขข้อมูลที่ได้รับและผสมกับ jQuery เล็กน้อยเพื่อสร้างฟังก์ชั่นที่ยอดเยี่ยมโดยสิ้นเชิงเพื่อเลือกข้อความในองค์ประกอบใด ๆ โดยไม่คำนึงถึงเบราว์เซอร์:

function SelectText(element) {
    var text = document.getElementById(element);
    if ($.browser.msie) {
        var range = document.body.createTextRange();
        range.moveToElementText(text);
        range.select();
    } else if ($.browser.mozilla || $.browser.opera) {
        var selection = window.getSelection();
        var range = document.createRange();
        range.selectNodeContents(text);
        selection.removeAllRanges();
        selection.addRange(range);
    } else if ($.browser.safari) {
        var selection = window.getSelection();
        selection.setBaseAndExtent(text, 0, text, 1);
    }
}

โซลูชัน jQuery ให้ฉัน Uncaught TypeError: ไม่สามารถอ่านคุณสมบัติ 'msie' ของ undefined
egmfrs

@egmfrs ใช่แล้วตั้งแต่คำตอบนี้ถูกโพสต์ jquery ได้ลบbrowserดมกลิ่นของพวกเขา
Jason

127

นี่คือรุ่นที่ไม่มีการดมกลิ่นของเบราว์เซอร์และไม่พึ่งพา jQuery:

function selectElementText(el, win) {
    win = win || window;
    var doc = win.document, sel, range;
    if (win.getSelection && doc.createRange) {
        sel = win.getSelection();
        range = doc.createRange();
        range.selectNodeContents(el);
        sel.removeAllRanges();
        sel.addRange(range);
    } else if (doc.body.createTextRange) {
        range = doc.body.createTextRange();
        range.moveToElementText(el);
        range.select();
    }
}

selectElementText(document.getElementById("someElement"));
selectElementText(elementInIframe, iframe.contentWindow);

มีวิธีการเลือกข้อความของdiv contentEditable='true'?
oldboy

@PrimitiveNom: รหัสนี้จะทำงานกับองค์ประกอบที่พึงพอใจ แต่คุณจะต้องตรวจสอบให้แน่ใจว่าได้มุ่งเน้นก่อน
ทิมดาวน์

18

ไม่สามารถใช้รหัสของ Jason สำหรับองค์ประกอบภายใน iframe (เนื่องจากขอบเขตแตกต่างจากหน้าต่างและเอกสาร) ฉันแก้ไขปัญหานั้นแล้วและแก้ไขเพื่อใช้เป็นปลั๊กอิน jQuery อื่น ๆ (สามารถเชื่อมต่อได้):

ตัวอย่างที่ 1: การเลือกข้อความทั้งหมดภายในแท็ก <code> ด้วยคลิกเดียวและเพิ่มคลาส "selected":

$(function() {
    $("code").click(function() {
        $(this).selText().addClass("selected");
    });
});

ตัวอย่างที่ 2: เมื่อคลิกปุ่มเลือกองค์ประกอบภายใน Iframe:

$(function() {
    $("button").click(function() {
        $("iframe").contents().find("#selectme").selText();
    });
});

หมายเหตุ: โปรดจำไว้ว่าแหล่งที่มาของ iframe ควรอยู่ในโดเมนเดียวกันเพื่อป้องกันข้อผิดพลาดด้านความปลอดภัย

ปลั๊กอิน jQuery:

jQuery.fn.selText = function() {
    var obj = this[0];
    if ($.browser.msie) {
        var range = obj.offsetParent.createTextRange();
        range.moveToElementText(obj);
        range.select();
    } else if ($.browser.mozilla || $.browser.opera) {
        var selection = obj.ownerDocument.defaultView.getSelection();
        var range = obj.ownerDocument.createRange();
        range.selectNodeContents(obj);
        selection.removeAllRanges();
        selection.addRange(range);
    } else if ($.browser.safari) {
        var selection = obj.ownerDocument.defaultView.getSelection();
        selection.setBaseAndExtent(obj, 0, obj, 1);
    }
    return this;
}

ฉันทดสอบใน IE8, Firefox, Opera, Safari, Chrome (รุ่นปัจจุบัน) ฉันไม่แน่ใจว่ามันใช้งานได้กับ IE เวอร์ชันเก่าหรือไม่


3
$ .browser เลิกใช้ / ลบแล้ว - ต้องมีการเขียนใหม่
James McCormack

2
@JamesMcCormack: ใช่ ฉันไม่แน่ใจว่าการเขียนใหม่จะคุ้มหรือไม่เพราะมีวิธีแก้ไขปัญหาอื่น ๆ ที่โพสต์ไว้ที่นี่ซึ่งไม่เกี่ยวข้องกับ $ .browser
lepe

16

นี้ด้ายมีสิ่งที่ยอดเยี่ยมจริงๆ แต่ฉันไม่สามารถทำได้ในหน้านี้โดยใช้ FF 3.5b99 + FireBug เนื่องจาก "ข้อผิดพลาดด้านความปลอดภัย"

Yipee !! ฉันสามารถเลือกแถบด้านข้างขวาทั้งหมดด้วยรหัสนี้หวังว่าจะช่วยให้คุณ:

    var r = document.createRange();
    var w=document.getElementById("sidebar");  
    r.selectNodeContents(w);  
    var sel=window.getSelection(); 
    sel.removeAllRanges(); 
    sel.addRange(r); 

PS: - ฉันไม่สามารถใช้วัตถุที่ส่งคืนโดยตัวเลือก jquery เช่น

   var w=$("div.welovestackoverflow",$("div.sidebar"));

   //this throws **security exception**

   r.selectNodeContents(w);

2
คุณต้องได้รับองค์ประกอบจาก jQuery ในขณะที่คุณพยายามเลือกวัตถุ jQuery: var w = $ ("div.welovestackoverflowflow", $ ("div.sidebar")) รับ (0);
Blixt

ไม่ทำงาน ... ฉันได้รับข้อผิดพลาด "วัตถุไม่สนับสนุนวิธีการนี้" และจะเน้นบรรทัดแรก ฉันไม่บางขุดและพบว่ามี "document.body.createTextRange ()" แต่แล้ว "selectNodeContents" ไม่ทำงาน .... และนี่คือใน IE
เจสัน

ฉันอ่านกระทู้ที่คุณพบ ... น่าอัศจรรย์ ... ฉันสามารถสร้างฟังก์ชั่นจากข้อมูลที่ทำงานบนเบราว์เซอร์ทั้งหมด ขอบคุณมาก! วิธีการแก้ปัญหาของฉันถูกโพสต์
Jason

6

คุณสามารถใช้ฟังก์ชั่นต่อไปนี้เพื่อเลือกเนื้อหาขององค์ประกอบใด ๆ :

jQuery.fn.selectText = function(){
    this.find('input').each(function() {
        if($(this).prev().length == 0 || !$(this).prev().hasClass('p_copy')) { 
            $('<p class="p_copy" style="position: absolute; z-index: -1;"></p>').insertBefore($(this));
        }
        $(this).prev().html($(this).val());
    });
    var doc = document;
    var element = this[0];
    console.log(this, element);
    if (doc.body.createTextRange) {
        var range = document.body.createTextRange();
        range.moveToElementText(element);
        range.select();
    } else if (window.getSelection) {
        var selection = window.getSelection();        
        var range = document.createRange();
        range.selectNodeContents(element);
        selection.removeAllRanges();
        selection.addRange(range);
    }
};

ฟังก์ชั่นนี้สามารถเรียกได้ดังนี้:

$('#selectme').selectText();

5

ฉันกำลังค้นหาสิ่งเดียวกันทางออกของฉันคือ:

$('#el-id').focus().select();

3
คุณไม่สามารถใช้focus()กับข้อมูลที่ไม่ได้ใส่ซึ่งเป็นสิ่งที่คำถามนี้เกี่ยวกับ
Jason

4
แต่คุณสามารถใช้มันในองค์ประกอบ textarea - ซึ่งเป็นปัญหาที่ฉันไปเที่ยวที่นี่ ความผิดของฉันที่ไม่ได้อ่านคำถามตลอดไป
Auston

4

ฉันชอบคำตอบของ lepe ยกเว้นบางสิ่ง:

  1. เบราว์เซอร์ดมกลิ่น jQuery หรือไม่ไม่ดีที่สุด
  2. แห้ง
  3. ไม่ทำงานใน IE8 ถ้าผู้ปกครองของ obj ไม่รองรับcreateTextRange
  4. ความสามารถของ Chrome ในการใช้setBaseAndExtentควรได้รับการยกระดับ (IMO)
  5. จะไม่เลือกข้อความที่ขยายไปทั่วองค์ประกอบ DOM หลายองค์ประกอบ (องค์ประกอบภายในองค์ประกอบ "เลือก") กล่าวอีกนัยหนึ่งถ้าคุณเรียกselTextบน div ที่มีองค์ประกอบ span หลายองค์ประกอบมันจะไม่เลือกข้อความของแต่ละองค์ประกอบเหล่านั้น นั่นคือดีลเลอร์สำหรับฉัน YMMV

นี่คือสิ่งที่ฉันคิดขึ้นมาพร้อมกับพยักหน้าให้คำตอบสำหรับแรงบันดาลใจ ฉันแน่ใจว่าฉันจะถูกเยาะเย้ยเพราะนี่อาจเป็นเรื่องที่หนักหน่วงเล็กน้อย แต่การทำงานและหลีกเลี่ยงการเบราว์เซอร์และการดมกลิ่นที่จุด

selectText:function(){

    var range,
        selection,
        obj = this[0],
        type = {
            func:'function',
            obj:'object'
        },
        // Convenience
        is = function(type, o){
            return typeof o === type;
        };

    if(is(type.obj, obj.ownerDocument)
        && is(type.obj, obj.ownerDocument.defaultView)
        && is(type.func, obj.ownerDocument.defaultView.getSelection)){

        selection = obj.ownerDocument.defaultView.getSelection();

        if(is(type.func, selection.setBaseAndExtent)){
            // Chrome, Safari - nice and easy
            selection.setBaseAndExtent(obj, 0, obj, $(obj).contents().size());
        }
        else if(is(type.func, obj.ownerDocument.createRange)){

            range = obj.ownerDocument.createRange();

            if(is(type.func, range.selectNodeContents)
                && is(type.func, selection.removeAllRanges)
                && is(type.func, selection.addRange)){
                // Mozilla
                range.selectNodeContents(obj);
                selection.removeAllRanges();
                selection.addRange(range);
            }
        }
    }
    else if(is(type.obj, document.body) && is(type.obj, document.body.createTextRange)) {

        range = document.body.createTextRange();

        if(is(type.obj, range.moveToElementText) && is(type.obj, range.select)){
            // IE most likely
            range.moveToElementText(obj);
            range.select();
        }
    }

    // Chainable
    return this;
}

แค่นั้นแหละ. บางสิ่งที่คุณเห็นคือเพื่อความสะดวกในการอ่านและ / หรือความสะดวกสบาย ทดสอบบน Mac ในเวอร์ชันล่าสุดของ Opera, Safari, Chrome, Firefox และ IE ทดสอบใน IE8 ด้วยเช่นกัน นอกจากนี้ฉันมักจะประกาศตัวแปรถ้า / เมื่อจำเป็นภายในบล็อคโค้ด แต่ jslint แนะนำพวกเขาทั้งหมดจะถูกประกาศขึ้นด้านบน ตกลง jslint

แก้ไข ฉันลืมที่จะรวมวิธีการผูกเข้ากับรหัสของ op:

function SelectText(element) {
    $("#" + element).selectText();
}

ไชโย


ใช่ที่ดูหนักไปฉันแม้ว่ามันจะถูกต้อง จับหลักของฉันคือการใช้ที่ไม่ได้มาตรฐานsetBaseAndExtent()เพียงเพราะมันดูเหมือนไม่มีจุดหมายสำหรับฉันเมื่อคุณสามารถลบสาขานั้นและทุกอย่างทำงานได้เป็นอย่างดีและในแบบมาตรฐาน การตรวจจับคุณสมบัติดี แต่ฉันเบื่อที่จะทดสอบทุกอย่างที่ค่อนข้างเร็ว
Tim Down

@TimDown ประเด็นของการใช้ประโยชน์setBaseAndExtentคือมันมีประสิทธิภาพมากขึ้นอย่างมากและถึงแม้สถานะที่เพิ่มเข้ามาifจะยังคงอยู่ไกลเกินกว่าที่คุณจะเอาสาขานั้นออกไป ฉันไม่เข้าใจความคิดเห็นของ "ฉันจะเหนื่อย .. " เขียนและลืมสิ่งเดียวที่คุณต้องทำคือเรียกใช้ฟังก์ชันไม่ใช่เขียน :)
Madbreaks

@Madbreaks ฉันต้องการจะประหลาดใจถ้าsetBaseAndExtentอย่างมีนัยสำคัญ performant addRangeมากกว่า ทำไมถึงเป็นเช่นนั้น? ความคิดเห็นอื่น ๆ ของฉันเกี่ยวข้องกับคุณลักษณะการทดสอบร๊อคของคุณที่ขยายไปถึงการโต้ตอบ DOM ทั้งหมด: มันเป็นรหัสที่แย่มากที่จะทดสอบวิธีและคุณสมบัติ DOM ทุกอันก่อนใช้งาน ฉันไม่อนุมัติ ฉันแค่มีความสุขที่จะวาดเส้นและสร้างสมมติฐานเพิ่มเติมอีกสองสามข้อในรหัสของฉัน
Tim Down

4

เวอร์ชันอัปเดตที่ใช้งานได้ใน chrome:

function SelectText(element) {
    var doc = document;
    var text = doc.getElementById(element);    
    if (doc.body.createTextRange) { // ms
        var range = doc.body.createTextRange();
        range.moveToElementText(text);
        range.select();
    } else if (window.getSelection) {
        var selection = window.getSelection();
        var range = doc.createRange();
        range.selectNodeContents(text);
        selection.removeAllRanges();
        selection.addRange(range);

    }
}

$(function() {
    $('p').click(function() {
        SelectText("selectme");

    });
});

http://jsfiddle.net/KcX6A/326/


3

สำหรับแท็กใด ๆ หนึ่งสามารถเลือกข้อความทั้งหมดภายในแท็กนั้นด้วยรหัสสั้นและง่าย มันจะเน้นพื้นที่แท็กทั้งหมดด้วยสีเหลืองและเลือกข้อความภายในคลิกเดียว

document.onclick = function(event) {
    var range, selection;
event.target.style.backgroundColor = 'yellow';
        selection = window.getSelection();
        range = document.createRange();
        range.selectNodeContents(event.target);
        selection.removeAllRanges();
        selection.addRange(range);
};

2

lepe - มันใช้งานได้ดีสำหรับฉันขอบคุณ! ฉันใส่รหัสของคุณในไฟล์ปลั๊กอินจากนั้นใช้ร่วมกับแต่ละคำสั่งเพื่อให้คุณสามารถมีแท็กล่วงหน้าหลายแท็กและลิงก์ "เลือกทั้งหมด" หลายรายการในหน้าเดียวและมันเลือกเอาไว้ล่วงหน้าเพื่อเน้น:

<script type="text/javascript" src="../js/jquery.selecttext.js"></script>
<script type="text/javascript">
  $(document).ready(function() { 
        $(".selectText").each(function(indx) {
                $(this).click(function() {                 
                    $('pre').eq(indx).selText().addClass("selected");
                        return false;               
                    });
        });
  });


1

ลองดูที่Selection object (Gecko engine) และTextRange object (Trident engine.) ฉันไม่รู้เกี่ยวกับเฟรมเวิร์ก JavaScript ใด ๆ ที่มีการสนับสนุนข้ามเบราว์เซอร์สำหรับการติดตั้งนี้ แต่ฉันก็ไม่เคยลองเลย เป็นไปได้ว่าแม้ jQuery ก็มี


0

วิธีการของ Tim ทำงานได้อย่างสมบูรณ์แบบสำหรับกรณีของฉัน - การเลือกข้อความใน div สำหรับทั้ง IE และ FF หลังจากฉันแทนที่คำสั่งต่อไปนี้:

range.moveToElementText(text);

ด้วยดังต่อไปนี้:

range.moveToElementText(el);

ข้อความใน div ถูกเลือกโดยคลิกที่ฟังก์ชัน jQuery ต่อไปนี้:

$(function () {
    $("#divFoo").click(function () {
        selectElementText(document.getElementById("divFoo"));
    })
});

0

นี่เป็นอีกวิธีง่ายๆในการรับข้อความที่เลือกในรูปแบบของสตริงคุณสามารถใช้สตริงนี้ได้อย่างง่ายดายเพื่อผนวกลูกองค์ประกอบ div ในรหัสของคุณ:

var text = '';

if (window.getSelection) {
    text = window.getSelection();

} else if (document.getSelection) {
    text = document.getSelection();

} else if (document.selection) {
    text = document.selection.createRange().text;
}

text = text.toString();

นี่คือเพื่อส่งคืนข้อความที่เน้นไว้ไม่ใช่การสร้างไฮไลต์
MKN Web Solutions

0

กรณีการใช้งานเฉพาะของฉันคือการเลือกช่วงข้อความภายในองค์ประกอบช่วงที่แก้ไขได้ซึ่งเท่าที่ฉันจะเห็นได้นั้นจะไม่มีการอธิบายไว้ในคำตอบใด ๆ ที่นี่

ความแตกต่างที่สำคัญคือคุณต้องผ่านโหนดประเภทTextไปยังRangeวัตถุดังที่อธิบายไว้ในเอกสารของ Range.setStart () :

หาก startNode เป็นโหนดชนิดข้อความความคิดเห็นหรือ CDATASectionดังนั้น startOffset คือจำนวนอักขระจากจุดเริ่มต้นของ startNode สำหรับโหนดชนิดอื่น startOffset คือจำนวนโหนดลูกระหว่างการเริ่มต้นของ startNode

Textโหนดเป็นโหนดลูกคนแรกขององค์ประกอบช่วงเพื่อที่จะได้รับมันเข้าถึงchildNodes[0]ขององค์ประกอบช่วง คำตอบที่เหลือก็เหมือนกับคำตอบอื่น ๆ

นี่คือตัวอย่างรหัส:

var startIndex = 1;
var endIndex = 5;
var element = document.getElementById("spanId");
var textNode = element.childNodes[0];

var range = document.createRange();
range.setStart(textNode, startIndex);
range.setEnd(textNode, endIndex);

var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);

เอกสารอื่น ๆ ที่เกี่ยวข้อง:
Range
Selection
Document.createRange ()
Window.getSelection ()


-1

เพิ่มjQuery.browser.webkitใน "else if" สำหรับ Chrome แล้ว ไม่สามารถทำงานได้ใน Chrome 23

ทำสคริปต์นี้ด้านล่างสำหรับการเลือกเนื้อหาในส่วนแท็กที่มี<pre>class="code"

jQuery( document ).ready(function() {
    jQuery('pre.code').attr('title', 'Click to select all');
    jQuery( '#divFoo' ).click( function() {
        var refNode = jQuery( this )[0];
        if ( jQuery.browser.msie ) {
            var range = document.body.createTextRange();
            range.moveToElementText( refNode );
            range.select();
        } else if ( jQuery.browser.mozilla || jQuery.browser.opera  || jQuery.browser.webkit ) {
            var selection = refNode.ownerDocument.defaultView.getSelection();
            console.log(selection);
            var range = refNode.ownerDocument.createRange();
            range.selectNodeContents( refNode );
            selection.removeAllRanges();
            selection.addRange( range );
        } else if ( jQuery.browser.safari ) {
            var selection = refNode.ownerDocument.defaultView.getSelection();
            selection.setBaseAndExtent( refNode, 0, refNode, 1 );
        }
    } );
} );

-1

ตามเอกสาร jQuery ของselect():

ทริกเกอร์เหตุการณ์ที่เลือกขององค์ประกอบที่ตรงกันแต่ละรายการ สิ่งนี้ทำให้ฟังก์ชั่นทั้งหมดที่ถูกผูกไว้กับเหตุการณ์ที่เลือกนั้นจะถูกดำเนินการและเรียกการกระทำที่เลือกเริ่มต้นของเบราว์เซอร์ในองค์ประกอบที่ตรงกัน

มีคำอธิบายของคุณว่าทำไม jQuery select()ไม่ทำงานในกรณีนี้


4
ฉันไม่ได้พยายามเน้นข้อความด้วยสไตล์ css ฉันต้องการข้อความที่จะเลือก
Jason
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.