質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.37%

  • JavaScript

    21566questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • HTML5

    5454questions

    HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

datalistタグの子要素optionタグに仕込んだ自作の属性をgetAttributeしたいができない

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 7,885

true

score 414

html5から使用できるようになったdatalistタグによるサジェストリストですが、optionタグに仕込んだ自作の属性fidをgetAttributeできません。

select タグを使った場合はvalue部分とテキスト部分に分けられたので見せたくない情報はvalue=に入れればよかったんですがdatalistの場合はそれができないので自分で属性を作ってgetAttributeで取得、と思ってやったのですが得られません。

ご存知のかた、よろしくお願い致します。

ブラウザはchrome バージョン 44.0.2403.125 (64-bit)

<html>
<head>
<meta charset="utf-8">
<script type="text/javascript">
function show() {
  // var fid = document.getElementById("fruits").getAttribute("fid");
  // var fid = document.getElementById("keywords").childNodes[0].getAttribute("fid");
  // var fid = document.querySelector("fid");
 // fidの値を取得したいがうまくいかない・・
  alert(fid);
}
</script>
</head>
<body>
<input type="search" autocomplete="on" list="keywords" id="fruits">
<datalist id="keywords">
<option fid="1" value="みかん">
<option fid="2" value="ぶどう">
<option fid="3" value="ばなな">
</datalist>
<input type="button" onclick="show();" value="表示">
</body>
</html>

補足:用途はたくさんある候補からひとつ選ばせ、たとえばそれを削除したり、何かしらの登録をやるといった具合です。その際、DB上でユニークなのはidだけという設定です(no や nameは重複の余地を残している)。

<?php
$db = new PDO("sqlite:test.db");
/* 初期設定 */
$sql = "CREATE TABLE IF NOT EXISTS fruits (id INTEGER PRIMARY KEY AUTOINCREMENT, no INTEGER, name TEXT)";
$stmt = $db->prepare($sql);
$stmt->execute();
$stmt = $db->query("SELECT COUNT(*) FROM fruits");
/* サンプルデータ投入 */
$count = $stmt->fetchColumn();
if ($count == 0) {
  $fruits = ["みかん", "ぶどう", "ばなな"];
  $i = 1;
  $db->beginTransaction();
  foreach ($fruits as $fruit) {
    $db->exec("INSERT INTO fruits (no, name) VALUES ($i, '$fruit')");
    $i++;
  }
  $db->commit();
}
/* 初期表示 */
if (isset($_GET["func"]) && $_GET["func"] == "init") {
  $stmt = $db->query("SELECT * FROM fruits ORDER BY no");
  $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
  $json = json_encode($rows);
  echo $json;
  $db = null;
  exit();
/* たとえば削除 */
} else if (isset($_POST["func"]) && $_POST["func"] == "postDB") {
  $id = $_POST['id'];
  $no = $_POST['no'];
  $sql = "DELETE FROM fruits WHERE no = $no";
  $stmt = $db->prepare($sql);
  $stmt->execute();
}
$db = null;
?>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript">

Ajax = new XMLHttpRequest();

window.onload = init();

function init() {
  var url = "test.php" + "?dummy=" + new Date().getTime() + "&func=" + "init";
  Ajax.open("GET", url, true);
  Ajax.send(null);
  Ajax.onreadystatechange = function() {
    if (Ajax.readyState == 4 && Ajax.status == 200) {
      var rows = JSON.parse(Ajax.responseText);
      var out = "<input type='search' autocomplete='on' list='keywords' id='fruits'>";
      out += "<datalist id='keywords'>";
      /* 本来このなかにrows[i]['id']があって、それを拾えるようにしたいが表示させたくはない */
      for (var i in rows) {
        out += "<option value='"+rows[i]['no']+"/"+rows[i]['name']+"'>";
      }
      out += "</datalist>";
      out += "<input type='button' onclick='postDB();' value='送信'>";
      document.getElementById("result").innerHTML = out;
    }
  }
}
/* idをDBへ投げる */
function postDB() {
  var name = document.getElementById('fruits').value;
  var i = name.indexOf("/");
  var no = name.substr(0,i);
  // var id = "" 本来これを取得して投げたいができないので代わりにほかのものを投げる
  var id = 0; // エラー回避のためのダミー
  var url = "test.php";
  Ajax.open("POST", url, true);
  Ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  Ajax.send("func=" + "postDB" + "&id=" + id + "&no=" + no);
  Ajax.onreadystatechange = function() {
    if (Ajax.readyState == 4 && Ajax.status == 200) {
      init();
    }
  }
}
</script>
</head>
<body>
<div id="result"></div>
</body>
</html>
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+3

まず、input要素 や datalist要素 の DOM プロパティを覚えると良いと思います。

console.log(document.getElementById('fruits').list.options[0].getAttribute('fid')); // 1

true さんの目的はこれで達成できましたが、"fid" のようなカスタム属性を定義する為に HTML5 では data-* 独自属性が用意されています。
HTML5 では不明な属性(fid属性)は無視する仕様ですが、今後 fid 属性が定義されると問題になるので data-* 独自属性を使うことをお勧めします。

// 参考URL: http://jsfiddle.net/5nfufwdh/2/
console.log(document.getElementById('fruits').list.options[0].dataset,fid); // 1

ところで、input[type=search] は list 属性で入力補完した場合もテキスト入力ボックスとして機能します。
例えば、datalist から "バナナ" を選択した後に "バナナカフェ" に変更したり、"チョコバナナ" に改変することも可能です。
このように自由入力可能な情報に対して <option fid="1" value="みかん"> で対応関係を作る事に意味を見出せるのか、という疑問は残りますが、実際の用途が見えないので何ともいえないところです。
厳密にリストから選択させるのであれば、ユーザの改変の余地が発生しない select 要素が望ましいとは思うのですが。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/08/17 23:55

    fidはselectタグを使った場合のvalue=に相当するものだと思っています。
    <select id="hoge">
    <option value="xxx">xxx</option>
    </select>
    これならvalueの部分にfidを入れれば、fidを画面に表示せず、fidをサーバーに送れます。しかし、datalistの場合、
    <option value="xxx">しかなく、fidを仕込みようがないなあと思っています。そして仕方なくカスタム属性を使って
    <option fid="xxx" value="xxx">といった形にしたうえでgetAttrしようと企んだのですがつまづいているということです。

    追加サンプルでは本来fid(DB上ではid)を送りたいのですが送れないのでかわりのものを送っていますがわかりにくかったら申し訳ありません。

    <option fid="xxx" value="xxx" onchange="funcA();"> とかで捕捉しようとしましたがそれも効かないのでとりあえずDB上の設計でnoやnameをユニークにしちゃおうかと思っています。そうすればもうidは捕まえる必要はなくなりますので。

    キャンセル

  • 2015/08/18 01:29 編集

    <datalist> は有体にいえば、単純な入力補完機能です。オートコンプリートとかサジェスト機能と呼ばれるものと同じです。
    そこにユニークな fid の意味を持たせたいのなら <datalist> を使うべきではありません。
    http://fiddle.jshell.net/5nfufwdh/5/show/light/
    このフォームで送信が期待されるのは "fruits=みかん" であり、datalist の値は送信されません。
    送信されるのはあくまで input[list=keywords] である事に注目してください。
    datalist の内容は fid は元より、value 値すらも送信されません。

    id を機械的に検出するなら input#fruits の value 値から ["みかん", "ぶどう", "バナナ"] と一致するかを検出すれば良いですが、そうするぐらいなら初めから select 要素を使えば良いという事になってしまいます。
    仮に機械的に検出するにしてもその処理が必要なのは PHP の方であり、JavaScript は補助的なものです(JavaScript は容易に無効化できる事から、フォーム送信のバリデートを JavaScript に依存すべきではありません)。

    キャンセル

  • 2015/08/18 08:38

    結局、id を仕込むことをやめ、no と name の組み合わせを一意のものとするような設計にしました(たとえばユーザーが新規アイテムを登録するときにすでに同一のno、nameの組み合わせがあったら弾くといったような)。今回はDOM操作でいろいろと学ぶところがあり、貴重なアドバイスをありがとうございました。

    キャンセル

+2

childNodes を使った場合、改行コードもノードの1つに数えられてしまうようです
ですので
<datalist id="keywords">
の末尾にある改行を先頭ノードとして数えると
function show123() {
  var elms = document.getElementById("keywords").childNodes;
  console.log( elms[1].getAttribute("fid") ); // 0
  console.log( elms[2].getAttribute("fid") ); // 1
  console.log( elms[3].getAttribute("fid") ); // 2
}
で取り出せると思います

別の方法として単純にタグ名でoption要素を抜き出すとか
function show123() {
  var elms = document.getElementById("keywords").getElementsByTagName("option");
  console.log( elms[0].getAttribute("fid") ); // 1
}

以上ご参考までに

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/08/17 23:13

    ありがとうございます。childNodesで最初のオプションしか拾えなかったのでなんでかなあと悩んでいたんですが(当たり前といえばそうですが)、その疑問点は解決しました。ただ、この動作を動的にやりたいと思っています。

    キャンセル

+2

全く別のやり方ですが、連想配列を使って似たようなことができます。
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript">
var datalist = {'みかん':1,'ぶどう':2,'ばなな':3};
function load() {
  for (var data in datalist) {
    var option = document.createElement('option');
    option.id = datalist[data];
    option.value = data;
    document.getElementById("keywords").appendChild(option);
  }
}

function show() {
  var text = document.getElementById('fruits').value;
  if(text in datalist) {
    alert(datalist[text]);
  }else{
    alert('候補以外');
  }
}
</script>
</head>
<body onLoad="load()">
<input type="search" autocomplete="on" list="keywords" id="fruits">
<datalist id="keywords">
</datalist>
<input type="button" onclick="show();" value="表示">
</body>
</html>

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/08/17 23:15

    ありがとうございます。期待通りの動作を得ることが出来ました。もう少し自分でも考えてみたいと思います。連想配列の利用(オブジェクト?)は大変参考になりました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.37%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • JavaScript

    21566questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • HTML5

    5454questions

    HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。