Вибір тексту в елементі (подібний до виділення мишею)


424

Я хотів би, щоб користувачі натискали посилання, тоді він вибирає HTML-текст в іншому елементі ( не введенні).

Під "select" я маю на увазі те саме, що ви вибрали б текст, перетягуючи мишу по ньому. Це стосується досліджень, оскільки всі говорять про "вибір" або "виділення" іншими словами.

Чи можливо це? Мій код поки що:

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();
}

Чи пропускаю я щось очевидно очевидне?


Відповіді:


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сніферу.
Джейсон

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

Код Джейсона не можна використовувати для елементів всередині iframe (оскільки область застосування відрізняється від вікна та документа). Я виправив цю проблему і змінив її, щоб використовувати її як будь-який інший плагін jQuery (можна отримати):

Приклад 1: Виділення всього тексту всередині тегів <code> одним натисканням кнопки та додавання класу "вибрано":

$(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.welovestackoverflow", $ ("div.sidebar")). Get (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()його без вводу, про що це питання.
Джейсон

4
але ви можете використовувати його для елемента textarea - це була проблема, з якою я приїхав сюди до Google. Я винен, що не читав питання до кінця.
Остон

4

Мені сподобалась відповідь лепе, за винятком кількох речей:

  1. Нюхає браузер, jQuery чи ні, це не оптимально
  2. СУХИЙ
  3. Не працює в IE8, якщо батько obj не підтримує createTextRange
  4. Спроможність Chrome використовувати setBaseAndExtent має бути використана (IMO)
  5. Не буде обрано текст, що охоплює декілька елементів DOM (елементи в межах "вибраного" елемента). Іншими словами, якщо ви зателефонуєте на selText на div, що містить кілька прольотних елементів, він не вибере текст кожного з цих елементів. Це було для мене зломником, 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.

Редагувати Я забув включити, як пов'язати це з кодом операційної системи:

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

Ура


Так, мені це здається важким, хоча це здається правильним. Моя основна переконання полягає в тому, що використання нестандартного setBaseAndExtent()лише тому, що воно існує, мені здається безглуздим, коли ви можете просто видалити цю гілку, і все працює так само добре і стандартно. Виявлення функцій добре, але я втомився б тестувати все, що досить швидко.
Tim Down

Добре @TimDown сенс використання цього setBaseAndExtentполягає в тому, що це значно ефективніше, і навіть із доданим ifдержавним принципом все ще набагато більше, ніж якщо ви "видалите цю гілку". Я не дуже розумію коментар "Я би втомився .."? Напишіть його і забудьте, єдине, що вам потрібно зробити - це викликати функцію, а не писати її. :)
Madbreaks

@Madbreaks Я був би здивований, якби setBaseAndExtentбув значно ефективнішим, ніж addRange. Чому це було б? Мій інший коментар був пов'язаний з вашим етапом тестування функцій, який поширюється на всю взаємодію з DOM: це надзвичайно багато коду для тестування кожного окремого методу та властивості DOM перед його використанням. Я не схвалюю; Я просто радий підкреслити рядок і зробити ще кілька припущень у своєму коді.
Тім Даун

4

Оновлена ​​версія, яка працює в хромі:

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 (двигун Gecko) та на об'єкт TextRange (двигун Trident.) Я не знаю про жодні рамки JavaScript, які підтримують крос-браузер для цього, але я також ніколи не шукав його. можливо, навіть у jQuery є.


0

Метод Тіма відмінно працює в моєму випадку - вибираючи текст у діві для IE та FF після того, як я замінив наступне твердження:

range.moveToElementText(text);

із наступним:

range.moveToElementText(el);

Текст у діві вибирається, натискаючи на нього за допомогою такої функції jQuery:

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

0

ось ще одне просте рішення, щоб отримати вибраний текст у вигляді рядка, ви можете легко використовувати цей рядок, щоб додати дочірній елемент елемента діли у свій код:

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

0

Моїм особливим випадком використання був вибір діапазону тексту всередині редагованого проміжного елемента, який, наскільки я міг бачити, не описаний ні в одній з відповідей тут.

Основна відмінність полягає в тому , що ви повинні пройти вузол типу Textдо Rangeоб'єкту, як описано в документації Range.setStart () :

Якщо startNode - це Вузол типу Text, Comment або 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);

Інша відповідна документація:
Діапазон
вибору
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. Я хочу, щоб текст був обраний.
Джейсон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.