Rendre le texte "plus" sélectionnable

j'ai un texte dans un <p> balise:

<p>Hello world... and goodbye mind A B!</p>

Comment puis-je augmenter la zone dans laquelle le texte est sélectionnable? Je sais que je peux augmenter le font-size et qui permettrait d'augmenter la zone qui est sélectionnable, mais est-il une meilleure façon?

Pour clarifier cette question. Par exemple, sur les écrans mobiles, j'ai du mal à mettre en évidence des mots qui sont une seule lettre comme i, mais si le coup de détection serait sur une zone plus large, il serait beaucoup plus facile pour le sélectionner.

Comment faire? Un esprit de puzzle de taquineries.

Bounty info

recherche d'une solution de navigation croisée. Veuillez lire abondamment de la question et les commentaires avant de poster une réponse pour éviter la confusion. L'utilisateur @mmm a posté une question qui est assez proche, mais dans son approche, alors que le <p> tag is a une détection de frappe plus large (parfait!), il sélectionne automatiquement en cliquant. J'ai besoin de l'utilisateur pour interagir avec le <p> balise tout comme nous le faisons avec basés par texte normal <p> balises... cependant avec un plus grand détecteur de coups.

EDIT

Plus de précisions. Par exemple, la zone de sélection pour un commentaire à cette question est ce gros:

enter image description here

Vous pouvez trouver ce commentaire ci-dessous. Passez le curseur dessus jusqu'à ce que le curseur soit changé en cursor:text. C'est la sélection par défaut de la zone.

Mais mon but est d' l'étendre à un plus grand zone, comme ceci:

enter image description here

24
demandé sur Henrik Petterson 2016-02-21 01:00:15
la source

6 ответов

de mon test, il fonctionne sur l'iphone ainsi que ff et chrome - si quelqu'un peut tester sur android, j'apprécierai vos commentaires!

la bordure peut évidemment être enlevée.

ce code utilise le code de cette réponse (partie de la fonction SelectText ())): sélectionner un texte dans un élément (semblable à mettre en évidence avec votre souris)

Tripoter

Code:

function extendSelection() {
    var extendBy = arguments.length <= 0 || arguments[0] === undefined ? 15 : arguments[0];

    var extended = document.getElementsByClassName('extendedSelection');
    [].slice.call(extended).forEach(function (v) {
        var bounds = v.getBoundingClientRect();
        var x = bounds.left;
        var r = textWidth(v.innerHTML, ''+ css(v, 'font-weight') +' ' + css(v, 'font-size') + ' ' + css(v, 'font-family') );
        var y = bounds.top;
        var w = bounds.width;
        var h = bounds.height;
        var element = document.createElement('div');
        element.style.position = 'absolute';
        element.style.height = h + extendBy + 'px';
        element.style.width = r + extendBy + 'px';
        element.style.left = x - extendBy / 2 + 'px';
        element.style.top = y - extendBy / 2 + 'px';
        element.style.border = '1px dotted black';
        document.body.appendChild(element);
        element.addEventListener('click', function (e) {
            SelectText(v);
        });
        element.addEventListener('touchend', function (e) {
            SelectText(v);
        });
    });
}

function css(element, property) {
    return window.getComputedStyle(element, null).getPropertyValue(property);
}

function textWidth(text, font) {
    var el = textWidth.canvas || (textWidth.canvas = document.createElement("canvas"));
    var draw = el.getContext("2d");
    draw.font = font;
    var m = draw.measureText(text);
    return m.width;
};

function SelectText(element) {
    var doc = document,
        text = element,
        range,
        selection;
    if (doc.body.createTextRange) {
        range = document.body.createTextRange();
        range.moveToElementText(text);
        range.select();
    } else if (window.getSelection) {
        selection = window.getSelection();
        range = document.createRange();
        range.selectNodeContents(text);
        selection.removeAllRanges();
        selection.addRange(range);
        selection.setSelectionRange(0, element.value.length)
    }
}

extendSelection();
11
répondu bambam 2017-05-23 13:30:27
la source

vous pouvez ajouter du rembourrage autour des paragraphes comme quelqu'un l'a déjà suggéré mais aussi utiliser la marge négative de la même valeur pour l'empêcher d'affecter la disposition.

Ici DEMO (double-clic ou à long tapant n'importe où à l'intérieur de la zone grise doit sélectionner du texte)

code pertinent:

HTML:

<p>normal paragraph</p>
<hr />
<p class="fat-fingers">fat fingers paragraph</p>

CSS:

p {
    //resetting default browser styles for brevity
    //otherwise adjust negative margin value so it's == default margin - padding
    margin: 0;
}

.fat-fingers {
    padding: 10px;
    margin: -10px;
}

note: je n'ai pas testé cas de deux zones de chevauchement, mais je suppose que celui avec l'ordre d'empilage supérieur gagne.

2
répondu Teo Dragovic 2016-02-27 15:55:13
la source

je suppose que le scénario où vous avez un corps de texte, et à l'intérieur de ce corps de texte est un élément d'information assez important ou pertinent pour un utilisateur final et vous aimeriez qu'ils puissent facilement mettre en évidence et copier l'information.

ceci serait considéré comme une dernière option si aucune autre solution n'était trouvée,

<p class="surroundingText"> BLAH BLAH BLAH  <span class="importantText"> This is the information you would like users to be able to highlight </span> BLAH BLAH BLAH BLAH ETC ETC ETC </p>

si vous enveloppez le texte dans des balises de paragraphe séparées et leur donnez une classe alors mettez ce qui suit dans CSS:

.surroundingText {

  -webkit-user-select: none;  /* Chrome all / Safari all */
  -moz-user-select: none;     /* Firefox all */
  -ms-user-select: none;      /* IE 10+ */
  user-select: none;         
}

.importantText {

        -webkit-user-select: all;  /* Chrome all / Safari all */
      -moz-user-select: all;     /* Firefox all */
      -ms-user-select: all;      /* IE 10+ */
      user-select: all;
    }

ainsi le résultat final est seulement le texte entre la balise de portée peut être sélectionné.

1
répondu Steve Hartley 2016-02-21 01:44:39
la source

exagéré pour effet mais qu'en est-il:

<head>
    <style>   
    p {
        letter-spacing: 2px;
        line-height: 3em;
        word-spacing: 1.5em;
        vertical-align: middle;
    }
    </style>
</head>
<body >

    <p>this is a line.</p>
    <p>this is a line.</p>
    <p>this is a line.</p>
    <p>this is a line.</p>
    <p>this is a line.</p>
    <p>this is a line.</p>
    <p>this is a line.</p>
</body>
0
répondu J. Nilles 2016-02-21 01:26:03
la source

une approche consiste à augmenter le line-height<p> élément. Sur un appareil mobile, vous pouvez mieux sélectionner un fragment de texte, en raison de l'espacement plus grand entre les lignes. Si vous définissez des valeurs dans em, l'espacement est toujours relatif au font-size.

Une deuxième approche consiste à augmenter le word-spacing de l'élément de texte à un niveau qui est encore acceptable. Je recommande une valeur maximale de 0.2em.

HTML:

<p class="extendedSelection">Extended Selection</p>

CSS:

p.extendedSelection {
   line-height: 2em;
   word-spacing: 0.2em;
}

JSFiddle ici

si ces deux approches ne sont pas suffisantes, vous pouvez bien sûr créer un élément positionné absolu pour chaque mot, qui superpose l'élément texte, mais a une opacité de 0. Le texte à l'intérieur de cet élément doit être égal au texte derrière, mais avec une plus grande taille de police. Cette approche présente plusieurs inconvénients: Vous devez calculer les positions de chaque mot et dupliquer le contenu du texte. C'est un calcul assez grand pour un petit effet.

0
répondu ssc-hrep3 2016-02-29 23:51:52
la source

Ici est une approche qui peut être considérée si vous êtes ok pour avoir quelques clics supplémentaires

  1. l'utilisateur doit d'abord cliquer quelque part dans le texte qu'il veut sélectionner.
  2. en fonction de la position du clic, nous trouvons le mot qui a été cliqué
  3. maintenant nous prenons le mot précédent, le mot courant (cliqué) et le mot suivant et nous le montrons dans un popup à L'intérieur de H1 (ou tout autre moyen de montrer une plus grande zone pour la rendre plus sélectionnable)
  4. lorsque l'utilisateur sélectionne le texte de ce popup, nous fermons le popup et sélectionnons le texte approprié dans l'élément original que l'utilisateur avait cliqué

Voici ma tentative: https://jsfiddle.net/vnathalye/rtw5bvLx/6/

$('.expandable').click(function(e){
    var clicked = findClickedWord(e.target.childNodes[0], e.clientX, e.clientY);
  if(clicked){
    var $expanded = $('<span>')
                    .appendTo('body')
                    .addClass('expanded')
                    .css({
                      position: "absolute",
                      left: clicked[3].left,
                      top: clicked[3].top,
                      //width: "100px",
                      //height: "100px"
                    })
                    .append($("<h1>").text(clicked[0]));

        var data = {originalElem: e.target.childNodes[0], index: clicked[1], starts: clicked[2]};

    $expanded.data("parentData", data);
    $expanded.on('mouseup', selectionChanged);
    $expanded.on('touchend touchcancel', selectionChanged);

    //alert(JSON.stringify(clicked));
  }
});

function selectionChanged(e){
try {
    var $expanded = $(e.target);
  var data = $expanded.parents(".expanded").data("parentData");
  var selection = window.getSelection();
  if(selection.rangeCount){
    var range1 = selection.getRangeAt(0);
    //alert(range1.startOffset + ":" + range1.endOffset);

    var range2 = document.createRange();
    var originalOffset = data.index>0? data.starts[data.index-1] : data.starts[0];
    range2.setStart(data.originalElem, originalOffset + range1.startOffset);
    range2.setEnd(data.originalElem, originalOffset + range1.endOffset);
    selection.removeAllRanges();
      selection.addRange(range2);
  }
 } catch(err){
 alert(err);
 }
  $expanded.parents(".expanded").remove();
}

function findClickedWord(parentElt, x, y) {
    if (parentElt.nodeName !== '#text') {
        console.log('didn\'t click on text node');
        return null;
    }
    var range = document.createRange();
    var words = parentElt.textContent.split(' ');
    var start = 0;
    var end = 0;
    var starts=[];
    var ends=[];
    for (var i = 0; i < words.length; i++) {
        var word = words[i];
        end = start+word.length;
        starts.push(start);
        ends.push(end);

        range.setStart(parentElt, start);
        range.setEnd(parentElt, end);
        // not getBoundingClientRect as word could wrap
        var rects = range.getClientRects();
        var clickedRect = isClickInRects(rects);

        if (clickedRect) {
                var str = (i==0)? word : words[i-1] + " " + word;
            if(i!=words.length-1) str += " " + words[i+1];
            return [str, i, starts, clickedRect];
        }
        start = end + 1;
    }

    function isClickInRects(rects) {
        for (var i = 0; i < rects.length; ++i) {
            var r = rects[i]
            if (r.left<x && r.right>x && r.top<y && r.bottom>y)
            {            
                return r;
            }
        }
        return false;
    }
    return null;
}

Remarque:

  1. le positionnement du popup peut être amélioré en fonction de vos besoins. Je me suis concentré sur l'obtention du code pour la sélection de texte de travail et n'ont pas peaufiné la logique de position popup.
  2. avec peu de temps je pourrais tester il seulement en FF sur PC et Chrome sur Android.
  3. C'est la première fois que j'utilise des événements tactiles, donc je ne suis pas sûr de les avoir utilisés de la meilleure façon possible. Toute critique, suggestion est la bienvenue.

Crédits

  1. ce code est basé sur l'idée que @mmm a commencé avec, d'utiliser un élément factice pour montrer la zone supplémentaire pour la sélection
  2. j'ai utilisé la version modifiée du [email protected] https://jsfiddle.net/abrady0/ggr5mu7o/